angular-rails-engine 1.1.5.0 → 1.2.0.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,10 +1,11 @@
1
1
  /**
2
- * @license AngularJS v1.1.5
2
+ * @license AngularJS v1.2.0rc1
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
- (function(window, angular, undefined) {
7
- 'use strict';
6
+ (function(window, angular, undefined) {'use strict';
7
+
8
+ var $resourceMinErr = angular.$$minErr('$resource');
8
9
 
9
10
  /**
10
11
  * @ngdoc overview
@@ -37,12 +38,11 @@
37
38
  *
38
39
  * @param {string} url A parametrized URL template with parameters prefixed by `:` as in
39
40
  * `/user/:username`. If you are using a URL with a port number (e.g.
40
- * `http://example.com:8080/api`), you'll need to escape the colon character before the port
41
- * number, like this: `$resource('http://example.com\\:8080/api')`.
41
+ * `http://example.com:8080/api`), it will be respected.
42
42
  *
43
- * If you are using a url with a suffix, just add the suffix, like this:
43
+ * If you are using a url with a suffix, just add the suffix, like this:
44
44
  * `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')
45
- * or even `$resource('http://example.com/resource/:resource_id.:format')`
45
+ * or even `$resource('http://example.com/resource/:resource_id.:format')`
46
46
  * If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
47
47
  * collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
48
48
  * can escape it with `/\.`.
@@ -98,6 +98,9 @@
98
98
  * requests with credentials} for more information.
99
99
  * - **`responseType`** - `{string}` - see {@link
100
100
  * https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
101
+ * - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
102
+ * `response` and `responseError`. Both `response` and `responseError` interceptors get called
103
+ * with `http response` object. See {@link ng.$http $http interceptors}.
101
104
  *
102
105
  * @returns {Object} A resource "class" object with methods for the default set of resource actions
103
106
  * optionally extended with custom `actions`. The default set contains these actions:
@@ -136,24 +139,27 @@
136
139
  * - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
137
140
  * - non-GET instance actions: `instance.$action([parameters], [success], [error])`
138
141
  *
142
+ * Success callback is called with (value, responseHeaders) arguments. Error callback is called
143
+ * with (httpResponse) argument.
139
144
  *
140
- * The Resource instances and collection have these additional properties:
145
+ * Class actions return empty instance (with additional properties below).
146
+ * Instance actions return promise of the action.
141
147
  *
142
- * - `$then`: the `then` method of a {@link ng.$q promise} derived from the underlying
143
- * {@link ng.$http $http} call.
148
+ * The Resource instances and collection have these additional properties:
144
149
  *
145
- * The success callback for the `$then` method will be resolved if the underlying `$http` requests
146
- * succeeds.
150
+ * - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
151
+ * instance or collection.
147
152
  *
148
- * The success callback is called with a single object which is the {@link ng.$http http response}
149
- * object extended with a new property `resource`. This `resource` property is a reference to the
150
- * result of the resource action resource object or array of resources.
153
+ * On success, the promise is resolved with the same resource instance or collection object,
154
+ * updated with data from server. This makes it easy to use in
155
+ * {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view rendering
156
+ * until the resource(s) are loaded.
151
157
  *
152
- * The error callback is called with the {@link ng.$http http response} object when an http
153
- * error occurs.
158
+ * On failure, the promise is resolved with the {@link ng.$http http response} object,
159
+ * without the `resource` property.
154
160
  *
155
- * - `$resolved`: true if the promise has been resolved (either with success or rejection);
156
- * Knowing if the Resource has been resolved is useful in data-binding.
161
+ * - `$resolved`: `true` after first server interaction is completed (either with success or rejection),
162
+ * `false` before that. Knowing if the Resource has been resolved is useful in data-binding.
157
163
  *
158
164
  * @example
159
165
  *
@@ -274,7 +280,7 @@
274
280
  </doc:example>
275
281
  */
276
282
  angular.module('ngResource', ['ng']).
277
- factory('$resource', ['$http', '$parse', function($http, $parse) {
283
+ factory('$resource', ['$http', '$parse', '$q', function($http, $parse, $q) {
278
284
  var DEFAULT_ACTIONS = {
279
285
  'get': {method:'GET'},
280
286
  'save': {method:'POST'},
@@ -345,7 +351,7 @@ angular.module('ngResource', ['ng']).
345
351
 
346
352
  var urlParams = self.urlParams = {};
347
353
  forEach(url.split(/\W/), function(param){
348
- if (param && (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
354
+ if (!(new RegExp("^\\d+$").test(param)) && param && (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
349
355
  urlParams[param] = true;
350
356
  }
351
357
  });
@@ -376,7 +382,7 @@ angular.module('ngResource', ['ng']).
376
382
  url = url.replace(/\/\.(?=\w+($|\?))/, '.');
377
383
  // replace escaped `/\.` with `/.`
378
384
  config.url = url.replace(/\/\\\./, '/.');
379
-
385
+
380
386
 
381
387
  // set params - delegate param encoding to $http
382
388
  forEach(params, function(value, key){
@@ -404,19 +410,19 @@ angular.module('ngResource', ['ng']).
404
410
  return ids;
405
411
  }
406
412
 
413
+ function defaultResponseInterceptor(response) {
414
+ return response.resource;
415
+ }
416
+
407
417
  function Resource(value){
408
418
  copy(value || {}, this);
409
419
  }
410
420
 
411
421
  forEach(actions, function(action, name) {
412
- action.method = angular.uppercase(action.method);
413
- var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH';
422
+ var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
423
+
414
424
  Resource[name] = function(a1, a2, a3, a4) {
415
- var params = {};
416
- var data;
417
- var success = noop;
418
- var error = null;
419
- var promise;
425
+ var params = {}, data, success, error;
420
426
 
421
427
  switch(arguments.length) {
422
428
  case 4:
@@ -448,33 +454,35 @@ angular.module('ngResource', ['ng']).
448
454
  break;
449
455
  case 0: break;
450
456
  default:
451
- throw "Expected between 0-4 arguments [params, data, success, error], got " +
452
- arguments.length + " arguments.";
457
+ throw $resourceMinErr('badargs',
458
+ "Expected up to 4 arguments [params, data, success, error], got {0} arguments", arguments.length);
453
459
  }
454
460
 
455
- var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
456
- var httpConfig = {},
457
- promise;
461
+ var isInstanceCall = data instanceof Resource;
462
+ var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
463
+ var httpConfig = {};
464
+ var responseInterceptor = action.interceptor && action.interceptor.response || defaultResponseInterceptor;
465
+ var responseErrorInterceptor = action.interceptor && action.interceptor.responseError || undefined;
458
466
 
459
467
  forEach(action, function(value, key) {
460
- if (key != 'params' && key != 'isArray' ) {
468
+ if (key != 'params' && key != 'isArray' && key != 'interceptor') {
461
469
  httpConfig[key] = copy(value);
462
470
  }
463
471
  });
472
+
464
473
  httpConfig.data = data;
465
474
  route.setUrlParams(httpConfig, extend({}, extractParams(data, action.params || {}), params), action.url);
466
475
 
467
- function markResolved() { value.$resolved = true; }
468
-
469
- promise = $http(httpConfig);
470
- value.$resolved = false;
471
-
472
- promise.then(markResolved, markResolved);
473
- value.$then = promise.then(function(response) {
474
- var data = response.data;
475
- var then = value.$then, resolved = value.$resolved;
476
+ var promise = $http(httpConfig).then(function(response) {
477
+ var data = response.data,
478
+ promise = value.$promise;
476
479
 
477
480
  if (data) {
481
+ if ( angular.isArray(data) != !!action.isArray ) {
482
+ throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected response' +
483
+ ' to contain an {0} but got an {1}',
484
+ action.isArray?'array':'object', angular.isArray(data)?'array':'object');
485
+ }
478
486
  if (action.isArray) {
479
487
  value.length = 0;
480
488
  forEach(data, function(item) {
@@ -482,44 +490,47 @@ angular.module('ngResource', ['ng']).
482
490
  });
483
491
  } else {
484
492
  copy(data, value);
485
- value.$then = then;
486
- value.$resolved = resolved;
493
+ value.$promise = promise;
487
494
  }
488
495
  }
489
496
 
497
+ value.$resolved = true;
498
+
490
499
  (success||noop)(value, response.headers);
491
500
 
492
501
  response.resource = value;
502
+
493
503
  return response;
494
- }, error).then;
504
+ }, function(response) {
505
+ value.$resolved = true;
495
506
 
496
- return value;
497
- };
507
+ (error||noop)(response);
498
508
 
509
+ return $q.reject(response);
510
+ }).then(responseInterceptor, responseErrorInterceptor);
499
511
 
500
- Resource.prototype['$' + name] = function(a1, a2, a3) {
501
- var params = extractParams(this),
502
- success = noop,
503
- error;
504
512
 
505
- switch(arguments.length) {
506
- case 3: params = a1; success = a2; error = a3; break;
507
- case 2:
508
- case 1:
509
- if (isFunction(a1)) {
510
- success = a1;
511
- error = a2;
512
- } else {
513
- params = a1;
514
- success = a2 || noop;
515
- }
516
- case 0: break;
517
- default:
518
- throw "Expected between 1-3 arguments [params, success, error], got " +
519
- arguments.length + " arguments.";
513
+ if (!isInstanceCall) {
514
+ // we are creating instance / collection
515
+ // - set the initial promise
516
+ // - return the instance / collection
517
+ value.$promise = promise;
518
+ value.$resolved = false;
519
+
520
+ return value;
521
+ }
522
+
523
+ // instance call
524
+ return promise;
525
+ };
526
+
527
+
528
+ Resource.prototype['$' + name] = function(params, success, error) {
529
+ if (isFunction(params)) {
530
+ error = success; success = params; params = {};
520
531
  }
521
- var data = hasBody ? this : undefined;
522
- Resource[name].call(this, params, data, success, error);
532
+ var result = Resource[name](params, this, success, error);
533
+ return result.$promise || result;
523
534
  };
524
535
  });
525
536
 
@@ -1,11 +1,14 @@
1
1
  /*
2
- AngularJS v1.1.5
2
+ AngularJS v1.2.0rc1
3
3
  (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  License: MIT
5
5
  */
6
- (function(B,f,w){'use strict';f.module("ngResource",["ng"]).factory("$resource",["$http","$parse",function(x,y){function u(b,d){this.template=b;this.defaults=d||{};this.urlParams={}}function v(b,d,e){function j(c,b){var p={},b=m({},d,b);l(b,function(a,b){k(a)&&(a=a());var g;a&&a.charAt&&a.charAt(0)=="@"?(g=a.substr(1),g=y(g)(c)):g=a;p[b]=g});return p}function c(c){t(c||{},this)}var n=new u(b),e=m({},z,e);l(e,function(b,d){b.method=f.uppercase(b.method);var p=b.method=="POST"||b.method=="PUT"||b.method==
7
- "PATCH";c[d]=function(a,d,g,A){function f(){h.$resolved=!0}var i={},e,o=q,r=null;switch(arguments.length){case 4:r=A,o=g;case 3:case 2:if(k(d)){if(k(a)){o=a;r=d;break}o=d;r=g}else{i=a;e=d;o=g;break}case 1:k(a)?o=a:p?e=a:i=a;break;case 0:break;default:throw"Expected between 0-4 arguments [params, data, success, error], got "+arguments.length+" arguments.";}var h=this instanceof c?this:b.isArray?[]:new c(e),s={};l(b,function(a,b){b!="params"&&b!="isArray"&&(s[b]=t(a))});s.data=e;n.setUrlParams(s,m({},
8
- j(e,b.params||{}),i),b.url);i=x(s);h.$resolved=!1;i.then(f,f);h.$then=i.then(function(a){var d=a.data,g=h.$then,e=h.$resolved;if(d)b.isArray?(h.length=0,l(d,function(a){h.push(new c(a))})):(t(d,h),h.$then=g,h.$resolved=e);(o||q)(h,a.headers);a.resource=h;return a},r).then;return h};c.prototype["$"+d]=function(a,b,g){var e=j(this),f=q,i;switch(arguments.length){case 3:e=a;f=b;i=g;break;case 2:case 1:k(a)?(f=a,i=b):(e=a,f=b||q);case 0:break;default:throw"Expected between 1-3 arguments [params, success, error], got "+
9
- arguments.length+" arguments.";}c[d].call(this,e,p?this:w,f,i)}});c.bind=function(c){return v(b,m({},d,c),e)};return c}var z={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},q=f.noop,l=f.forEach,m=f.extend,t=f.copy,k=f.isFunction;u.prototype={setUrlParams:function(b,d,e){var j=this,c=e||j.template,n,k,m=j.urlParams={};l(c.split(/\W/),function(b){b&&RegExp("(^|[^\\\\]):"+b+"(\\W|$)").test(c)&&(m[b]=!0)});c=c.replace(/\\:/g,
10
- ":");d=d||{};l(j.urlParams,function(b,a){n=d.hasOwnProperty(a)?d[a]:j.defaults[a];f.isDefined(n)&&n!==null?(k=encodeURIComponent(n).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),c=c.replace(RegExp(":"+a+"(\\W|$)","g"),k+"$1")):c=c.replace(RegExp("(/?):"+a+"(\\W|$)","g"),function(b,a,c){return c.charAt(0)=="/"?c:a+c})});c=c.replace(/\/+$/,"");c=c.replace(/\/\.(?=\w+($|\?))/,".");
11
- b.url=c.replace(/\/\\\./,"/.");l(d,function(c,a){if(!j.urlParams[a])b.params=b.params||{},b.params[a]=c})}};return v}])})(window,window.angular);
6
+ (function(H,g,C){'use strict';var A=g.$$minErr("$resource");g.module("ngResource",["ng"]).factory("$resource",["$http","$parse","$q",function(D,y,E){function n(g,h){this.template=g;this.defaults=h||{};this.urlParams={}}function u(l,h,d){function p(c,b){var e={};b=v({},h,b);q(b,function(a,b){t(a)&&(a=a());var m;a&&a.charAt&&"@"==a.charAt(0)?(m=a.substr(1),m=y(m)(c)):m=a;e[b]=m});return e}function b(c){return c.resource}function e(c){z(c||{},this)}var F=new n(l);d=v({},G,d);q(d,function(c,f){var h=
7
+ /^(POST|PUT|PATCH)$/i.test(c.method);e[f]=function(a,f,m,l){var d={},r,s,w;switch(arguments.length){case 4:w=l,s=m;case 3:case 2:if(t(f)){if(t(a)){s=a;w=f;break}s=f;w=m}else{d=a;r=f;s=m;break}case 1:t(a)?s=a:h?r=a:d=a;break;case 0:break;default:throw A("badargs",arguments.length);}var n=r instanceof e,k=n?r:c.isArray?[]:new e(r),x={},u=c.interceptor&&c.interceptor.response||b,y=c.interceptor&&c.interceptor.responseError||C;q(c,function(a,c){"params"!=c&&("isArray"!=c&&"interceptor"!=c)&&(x[c]=z(a))});
8
+ x.data=r;F.setUrlParams(x,v({},p(r,c.params||{}),d),c.url);d=D(x).then(function(a){var b=a.data,f=k.$promise;if(b){if(g.isArray(b)!=!!c.isArray)throw A("badcfg",c.isArray?"array":"object",g.isArray(b)?"array":"object");c.isArray?(k.length=0,q(b,function(a){k.push(new e(a))})):(z(b,k),k.$promise=f)}k.$resolved=!0;(s||B)(k,a.headers);a.resource=k;return a},function(a){k.$resolved=!0;(w||B)(a);return E.reject(a)}).then(u,y);return n?d:(k.$promise=d,k.$resolved=!1,k)};e.prototype["$"+f]=function(a,c,
9
+ b){t(a)&&(b=c,c=a,a={});a=e[f](a,this,c,b);return a.$promise||a}});e.bind=function(c){return u(l,v({},h,c),d)};return e}var G={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},B=g.noop,q=g.forEach,v=g.extend,z=g.copy,t=g.isFunction;n.prototype={setUrlParams:function(l,h,d){var p=this,b=d||p.template,e,n,c=p.urlParams={};q(b.split(/\W/),function(f){!/^\d+$/.test(f)&&(f&&RegExp("(^|[^\\\\]):"+f+"(\\W|$)").test(b))&&(c[f]=!0)});
10
+ b=b.replace(/\\:/g,":");h=h||{};q(p.urlParams,function(c,d){e=h.hasOwnProperty(d)?h[d]:p.defaults[d];g.isDefined(e)&&null!==e?(n=encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),b=b.replace(RegExp(":"+d+"(\\W|$)","g"),n+"$1")):b=b.replace(RegExp("(/?):"+d+"(\\W|$)","g"),function(a,c,b){return"/"==b.charAt(0)?b:c+b})});b=b.replace(/\/+$/,"");b=b.replace(/\/\.(?=\w+($|\?))/,
11
+ ".");l.url=b.replace(/\/\\\./,"/.");q(h,function(c,b){p.urlParams[b]||(l.params=l.params||{},l.params[b]=c)})}};return u}])})(window,window.angular);
12
+ /*
13
+ //@ sourceMappingURL=angular-resource.min.js.map
14
+ */
@@ -0,0 +1,860 @@
1
+ /**
2
+ * @license AngularJS v1.2.0rc1
3
+ * (c) 2010-2012 Google, Inc. http://angularjs.org
4
+ * License: MIT
5
+ */
6
+ (function(window, angular, undefined) {'use strict';
7
+
8
+ var copy = angular.copy,
9
+ equals = angular.equals,
10
+ extend = angular.extend,
11
+ forEach = angular.forEach,
12
+ isDefined = angular.isDefined,
13
+ isFunction = angular.isFunction,
14
+ isString = angular.isString,
15
+ jqLite = angular.element,
16
+ noop = angular.noop,
17
+ toJson = angular.toJson;
18
+
19
+
20
+ function inherit(parent, extra) {
21
+ return extend(new (extend(function() {}, {prototype:parent}))(), extra);
22
+ }
23
+
24
+ /**
25
+ * @ngdoc overview
26
+ * @name ngRoute
27
+ * @description
28
+ *
29
+ * Module that provides routing and deeplinking services and directives for angular apps.
30
+ */
31
+
32
+ var ngRouteModule = angular.module('ngRoute', ['ng']).
33
+ provider('$route', $RouteProvider);
34
+
35
+ /**
36
+ * @ngdoc object
37
+ * @name ngRoute.$routeProvider
38
+ * @function
39
+ *
40
+ * @description
41
+ *
42
+ * Used for configuring routes. See {@link ngRoute.$route $route} for an example.
43
+ */
44
+ function $RouteProvider(){
45
+ var routes = {};
46
+
47
+ /**
48
+ * @ngdoc method
49
+ * @name ngRoute.$routeProvider#when
50
+ * @methodOf ngRoute.$routeProvider
51
+ *
52
+ * @param {string} path Route path (matched against `$location.path`). If `$location.path`
53
+ * contains redundant trailing slash or is missing one, the route will still match and the
54
+ * `$location.path` will be updated to add or drop the trailing slash to exactly match the
55
+ * route definition.
56
+ *
57
+ * * `path` can contain named groups starting with a colon (`:name`). All characters up
58
+ * to the next slash are matched and stored in `$routeParams` under the given `name`
59
+ * when the route matches.
60
+ * * `path` can contain named groups starting with a colon and ending with a star (`:name*`).
61
+ * All characters are eagerly stored in `$routeParams` under the given `name`
62
+ * when the route matches.
63
+ * * `path` can contain optional named groups with a question mark (`:name?`).
64
+ *
65
+ * For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
66
+ * `/color/brown/largecode/code/with/slashs/edit` and extract:
67
+ *
68
+ * * `color: brown`
69
+ * * `largecode: code/with/slashs`.
70
+ *
71
+ *
72
+ * @param {Object} route Mapping information to be assigned to `$route.current` on route
73
+ * match.
74
+ *
75
+ * Object properties:
76
+ *
77
+ * - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly
78
+ * created scope or the name of a {@link angular.Module#controller registered controller}
79
+ * if passed as a string.
80
+ * - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
81
+ * published to scope under the `controllerAs` name.
82
+ * - `template` – `{string=|function()=}` – html template as a string or a function that
83
+ * returns an html template as a string which should be used by {@link
84
+ * ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
85
+ * This property takes precedence over `templateUrl`.
86
+ *
87
+ * If `template` is a function, it will be called with the following parameters:
88
+ *
89
+ * - `{Array.<Object>}` - route parameters extracted from the current
90
+ * `$location.path()` by applying the current route
91
+ *
92
+ * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
93
+ * template that should be used by {@link ngRoute.directive:ngView ngView}.
94
+ *
95
+ * If `templateUrl` is a function, it will be called with the following parameters:
96
+ *
97
+ * - `{Array.<Object>}` - route parameters extracted from the current
98
+ * `$location.path()` by applying the current route
99
+ *
100
+ * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
101
+ * be injected into the controller. If any of these dependencies are promises, they will be
102
+ * resolved and converted to a value before the controller is instantiated and the
103
+ * `$routeChangeSuccess` event is fired. The map object is:
104
+ *
105
+ * - `key` – `{string}`: a name of a dependency to be injected into the controller.
106
+ * - `factory` - `{string|function}`: If `string` then it is an alias for a service.
107
+ * Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
108
+ * and the return value is treated as the dependency. If the result is a promise, it is resolved
109
+ * before its value is injected into the controller. Be aware that `ngRoute.$routeParams` will
110
+ * still refer to the previous route within these resolve functions. Use `$route.current.params`
111
+ * to access the new route parameters, instead.
112
+ *
113
+ * - `redirectTo` – {(string|function())=} – value to update
114
+ * {@link ng.$location $location} path with and trigger route redirection.
115
+ *
116
+ * If `redirectTo` is a function, it will be called with the following parameters:
117
+ *
118
+ * - `{Object.<string>}` - route parameters extracted from the current
119
+ * `$location.path()` by applying the current route templateUrl.
120
+ * - `{string}` - current `$location.path()`
121
+ * - `{Object}` - current `$location.search()`
122
+ *
123
+ * The custom `redirectTo` function is expected to return a string which will be used
124
+ * to update `$location.path()` and `$location.search()`.
125
+ *
126
+ * - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search()
127
+ * changes.
128
+ *
129
+ * If the option is set to `false` and url in the browser changes, then
130
+ * `$routeUpdate` event is broadcasted on the root scope.
131
+ *
132
+ * - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
133
+ *
134
+ * If the option is set to `true`, then the particular route can be matched without being
135
+ * case sensitive
136
+ *
137
+ * @returns {Object} self
138
+ *
139
+ * @description
140
+ * Adds a new route definition to the `$route` service.
141
+ */
142
+ this.when = function(path, route) {
143
+ routes[path] = extend(
144
+ {reloadOnSearch: true},
145
+ route,
146
+ path && pathRegExp(path, route)
147
+ );
148
+
149
+ // create redirection for trailing slashes
150
+ if (path) {
151
+ var redirectPath = (path[path.length-1] == '/')
152
+ ? path.substr(0, path.length-1)
153
+ : path +'/';
154
+
155
+ routes[redirectPath] = extend(
156
+ {redirectTo: path},
157
+ pathRegExp(redirectPath, route)
158
+ );
159
+ }
160
+
161
+ return this;
162
+ };
163
+
164
+ /**
165
+ * @param path {string} path
166
+ * @param opts {Object} options
167
+ * @return {?Object}
168
+ *
169
+ * @description
170
+ * Normalizes the given path, returning a regular expression
171
+ * and the original path.
172
+ *
173
+ * Inspired by pathRexp in visionmedia/express/lib/utils.js.
174
+ */
175
+ function pathRegExp(path, opts) {
176
+ var insensitive = opts.caseInsensitiveMatch,
177
+ ret = {
178
+ originalPath: path,
179
+ regexp: path
180
+ },
181
+ keys = ret.keys = [];
182
+
183
+ path = path
184
+ .replace(/([().])/g, '\\$1')
185
+ .replace(/(\/)?:(\w+)([\?|\*])?/g, function(_, slash, key, option){
186
+ var optional = option === '?' ? option : null;
187
+ var star = option === '*' ? option : null;
188
+ keys.push({ name: key, optional: !!optional });
189
+ slash = slash || '';
190
+ return ''
191
+ + (optional ? '' : slash)
192
+ + '(?:'
193
+ + (optional ? slash : '')
194
+ + (star && '(.+)?' || '([^/]+)?') + ')'
195
+ + (optional || '');
196
+ })
197
+ .replace(/([\/$\*])/g, '\\$1');
198
+
199
+ ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
200
+ return ret;
201
+ }
202
+
203
+ /**
204
+ * @ngdoc method
205
+ * @name ngRoute.$routeProvider#otherwise
206
+ * @methodOf ngRoute.$routeProvider
207
+ *
208
+ * @description
209
+ * Sets route definition that will be used on route change when no other route definition
210
+ * is matched.
211
+ *
212
+ * @param {Object} params Mapping information to be assigned to `$route.current`.
213
+ * @returns {Object} self
214
+ */
215
+ this.otherwise = function(params) {
216
+ this.when(null, params);
217
+ return this;
218
+ };
219
+
220
+
221
+ this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$http', '$templateCache', '$sce',
222
+ function( $rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
223
+
224
+ /**
225
+ * @ngdoc object
226
+ * @name ngRoute.$route
227
+ * @requires $location
228
+ * @requires $routeParams
229
+ *
230
+ * @property {Object} current Reference to the current route definition.
231
+ * The route definition contains:
232
+ *
233
+ * - `controller`: The controller constructor as define in route definition.
234
+ * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
235
+ * controller instantiation. The `locals` contain
236
+ * the resolved values of the `resolve` map. Additionally the `locals` also contain:
237
+ *
238
+ * - `$scope` - The current route scope.
239
+ * - `$template` - The current route template HTML.
240
+ *
241
+ * @property {Array.<Object>} routes Array of all configured routes.
242
+ *
243
+ * @description
244
+ * Is used for deep-linking URLs to controllers and views (HTML partials).
245
+ * It watches `$location.url()` and tries to map the path to an existing route definition.
246
+ *
247
+ * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
248
+ *
249
+ * The `$route` service is typically used in conjunction with {@link ngRoute.directive:ngView ngView}
250
+ * directive and the {@link ngRoute.$routeParams $routeParams} service.
251
+ *
252
+ * @example
253
+ This example shows how changing the URL hash causes the `$route` to match a route against the
254
+ URL, and the `ngView` pulls in the partial.
255
+
256
+ Note that this example is using {@link ng.directive:script inlined templates}
257
+ to get it working on jsfiddle as well.
258
+
259
+ <example module="ngView" deps="angular-route.js">
260
+ <file name="index.html">
261
+ <div ng-controller="MainCntl">
262
+ Choose:
263
+ <a href="Book/Moby">Moby</a> |
264
+ <a href="Book/Moby/ch/1">Moby: Ch1</a> |
265
+ <a href="Book/Gatsby">Gatsby</a> |
266
+ <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
267
+ <a href="Book/Scarlet">Scarlet Letter</a><br/>
268
+
269
+ <div ng-view></div>
270
+ <hr />
271
+
272
+ <pre>$location.path() = {{$location.path()}}</pre>
273
+ <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
274
+ <pre>$route.current.params = {{$route.current.params}}</pre>
275
+ <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
276
+ <pre>$routeParams = {{$routeParams}}</pre>
277
+ </div>
278
+ </file>
279
+
280
+ <file name="book.html">
281
+ controller: {{name}}<br />
282
+ Book Id: {{params.bookId}}<br />
283
+ </file>
284
+
285
+ <file name="chapter.html">
286
+ controller: {{name}}<br />
287
+ Book Id: {{params.bookId}}<br />
288
+ Chapter Id: {{params.chapterId}}
289
+ </file>
290
+
291
+ <file name="script.js">
292
+ angular.module('ngView', ['ngRoute']).config(function($routeProvider, $locationProvider) {
293
+ $routeProvider.when('/Book/:bookId', {
294
+ templateUrl: 'book.html',
295
+ controller: BookCntl,
296
+ resolve: {
297
+ // I will cause a 1 second delay
298
+ delay: function($q, $timeout) {
299
+ var delay = $q.defer();
300
+ $timeout(delay.resolve, 1000);
301
+ return delay.promise;
302
+ }
303
+ }
304
+ });
305
+ $routeProvider.when('/Book/:bookId/ch/:chapterId', {
306
+ templateUrl: 'chapter.html',
307
+ controller: ChapterCntl
308
+ });
309
+
310
+ // configure html5 to get links working on jsfiddle
311
+ $locationProvider.html5Mode(true);
312
+ });
313
+
314
+ function MainCntl($scope, $route, $routeParams, $location) {
315
+ $scope.$route = $route;
316
+ $scope.$location = $location;
317
+ $scope.$routeParams = $routeParams;
318
+ }
319
+
320
+ function BookCntl($scope, $routeParams) {
321
+ $scope.name = "BookCntl";
322
+ $scope.params = $routeParams;
323
+ }
324
+
325
+ function ChapterCntl($scope, $routeParams) {
326
+ $scope.name = "ChapterCntl";
327
+ $scope.params = $routeParams;
328
+ }
329
+ </file>
330
+
331
+ <file name="scenario.js">
332
+ it('should load and compile correct template', function() {
333
+ element('a:contains("Moby: Ch1")').click();
334
+ var content = element('.doc-example-live [ng-view]').text();
335
+ expect(content).toMatch(/controller\: ChapterCntl/);
336
+ expect(content).toMatch(/Book Id\: Moby/);
337
+ expect(content).toMatch(/Chapter Id\: 1/);
338
+
339
+ element('a:contains("Scarlet")').click();
340
+ sleep(2); // promises are not part of scenario waiting
341
+ content = element('.doc-example-live [ng-view]').text();
342
+ expect(content).toMatch(/controller\: BookCntl/);
343
+ expect(content).toMatch(/Book Id\: Scarlet/);
344
+ });
345
+ </file>
346
+ </example>
347
+ */
348
+
349
+ /**
350
+ * @ngdoc event
351
+ * @name ngRoute.$route#$routeChangeStart
352
+ * @eventOf ngRoute.$route
353
+ * @eventType broadcast on root scope
354
+ * @description
355
+ * Broadcasted before a route change. At this point the route services starts
356
+ * resolving all of the dependencies needed for the route change to occurs.
357
+ * Typically this involves fetching the view template as well as any dependencies
358
+ * defined in `resolve` route property. Once all of the dependencies are resolved
359
+ * `$routeChangeSuccess` is fired.
360
+ *
361
+ * @param {Route} next Future route information.
362
+ * @param {Route} current Current route information.
363
+ */
364
+
365
+ /**
366
+ * @ngdoc event
367
+ * @name ngRoute.$route#$routeChangeSuccess
368
+ * @eventOf ngRoute.$route
369
+ * @eventType broadcast on root scope
370
+ * @description
371
+ * Broadcasted after a route dependencies are resolved.
372
+ * {@link ngRoute.directive:ngView ngView} listens for the directive
373
+ * to instantiate the controller and render the view.
374
+ *
375
+ * @param {Object} angularEvent Synthetic event object.
376
+ * @param {Route} current Current route information.
377
+ * @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered.
378
+ */
379
+
380
+ /**
381
+ * @ngdoc event
382
+ * @name ngRoute.$route#$routeChangeError
383
+ * @eventOf ngRoute.$route
384
+ * @eventType broadcast on root scope
385
+ * @description
386
+ * Broadcasted if any of the resolve promises are rejected.
387
+ *
388
+ * @param {Route} current Current route information.
389
+ * @param {Route} previous Previous route information.
390
+ * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
391
+ */
392
+
393
+ /**
394
+ * @ngdoc event
395
+ * @name ngRoute.$route#$routeUpdate
396
+ * @eventOf ngRoute.$route
397
+ * @eventType broadcast on root scope
398
+ * @description
399
+ *
400
+ * The `reloadOnSearch` property has been set to false, and we are reusing the same
401
+ * instance of the Controller.
402
+ */
403
+
404
+ var forceReload = false,
405
+ $route = {
406
+ routes: routes,
407
+
408
+ /**
409
+ * @ngdoc method
410
+ * @name ngRoute.$route#reload
411
+ * @methodOf ngRoute.$route
412
+ *
413
+ * @description
414
+ * Causes `$route` service to reload the current route even if
415
+ * {@link ng.$location $location} hasn't changed.
416
+ *
417
+ * As a result of that, {@link ngRoute.directive:ngView ngView}
418
+ * creates new scope, reinstantiates the controller.
419
+ */
420
+ reload: function() {
421
+ forceReload = true;
422
+ $rootScope.$evalAsync(updateRoute);
423
+ }
424
+ };
425
+
426
+ $rootScope.$on('$locationChangeSuccess', updateRoute);
427
+
428
+ return $route;
429
+
430
+ /////////////////////////////////////////////////////
431
+
432
+ /**
433
+ * @param on {string} current url
434
+ * @param route {Object} route regexp to match the url against
435
+ * @return {?Object}
436
+ *
437
+ * @description
438
+ * Check if the route matches the current url.
439
+ *
440
+ * Inspired by match in
441
+ * visionmedia/express/lib/router/router.js.
442
+ */
443
+ function switchRouteMatcher(on, route) {
444
+ var keys = route.keys,
445
+ params = {};
446
+
447
+ if (!route.regexp) return null;
448
+
449
+ var m = route.regexp.exec(on);
450
+ if (!m) return null;
451
+
452
+ var N = 0;
453
+ for (var i = 1, len = m.length; i < len; ++i) {
454
+ var key = keys[i - 1];
455
+
456
+ var val = 'string' == typeof m[i]
457
+ ? decodeURIComponent(m[i])
458
+ : m[i];
459
+
460
+ if (key && val) {
461
+ params[key.name] = val;
462
+ }
463
+ }
464
+ return params;
465
+ }
466
+
467
+ function updateRoute() {
468
+ var next = parseRoute(),
469
+ last = $route.current;
470
+
471
+ if (next && last && next.$$route === last.$$route
472
+ && equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
473
+ last.params = next.params;
474
+ copy(last.params, $routeParams);
475
+ $rootScope.$broadcast('$routeUpdate', last);
476
+ } else if (next || last) {
477
+ forceReload = false;
478
+ $rootScope.$broadcast('$routeChangeStart', next, last);
479
+ $route.current = next;
480
+ if (next) {
481
+ if (next.redirectTo) {
482
+ if (isString(next.redirectTo)) {
483
+ $location.path(interpolate(next.redirectTo, next.params)).search(next.params)
484
+ .replace();
485
+ } else {
486
+ $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
487
+ .replace();
488
+ }
489
+ }
490
+ }
491
+
492
+ $q.when(next).
493
+ then(function() {
494
+ if (next) {
495
+ var locals = extend({}, next.resolve),
496
+ template, templateUrl;
497
+
498
+ forEach(locals, function(value, key) {
499
+ locals[key] = isString(value) ? $injector.get(value) : $injector.invoke(value);
500
+ });
501
+
502
+ if (isDefined(template = next.template)) {
503
+ if (isFunction(template)) {
504
+ template = template(next.params);
505
+ }
506
+ } else if (isDefined(templateUrl = next.templateUrl)) {
507
+ if (isFunction(templateUrl)) {
508
+ templateUrl = templateUrl(next.params);
509
+ }
510
+ templateUrl = $sce.getTrustedResourceUrl(templateUrl);
511
+ if (isDefined(templateUrl)) {
512
+ next.loadedTemplateUrl = templateUrl;
513
+ template = $http.get(templateUrl, {cache: $templateCache}).
514
+ then(function(response) { return response.data; });
515
+ }
516
+ }
517
+ if (isDefined(template)) {
518
+ locals['$template'] = template;
519
+ }
520
+ return $q.all(locals);
521
+ }
522
+ }).
523
+ // after route change
524
+ then(function(locals) {
525
+ if (next == $route.current) {
526
+ if (next) {
527
+ next.locals = locals;
528
+ copy(next.params, $routeParams);
529
+ }
530
+ $rootScope.$broadcast('$routeChangeSuccess', next, last);
531
+ }
532
+ }, function(error) {
533
+ if (next == $route.current) {
534
+ $rootScope.$broadcast('$routeChangeError', next, last, error);
535
+ }
536
+ });
537
+ }
538
+ }
539
+
540
+
541
+ /**
542
+ * @returns the current active route, by matching it against the URL
543
+ */
544
+ function parseRoute() {
545
+ // Match a route
546
+ var params, match;
547
+ forEach(routes, function(route, path) {
548
+ if (!match && (params = switchRouteMatcher($location.path(), route))) {
549
+ match = inherit(route, {
550
+ params: extend({}, $location.search(), params),
551
+ pathParams: params});
552
+ match.$$route = route;
553
+ }
554
+ });
555
+ // No route matched; fallback to "otherwise" route
556
+ return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
557
+ }
558
+
559
+ /**
560
+ * @returns interpolation of the redirect path with the parameters
561
+ */
562
+ function interpolate(string, params) {
563
+ var result = [];
564
+ forEach((string||'').split(':'), function(segment, i) {
565
+ if (i == 0) {
566
+ result.push(segment);
567
+ } else {
568
+ var segmentMatch = segment.match(/(\w+)(.*)/);
569
+ var key = segmentMatch[1];
570
+ result.push(params[key]);
571
+ result.push(segmentMatch[2] || '');
572
+ delete params[key];
573
+ }
574
+ });
575
+ return result.join('');
576
+ }
577
+ }];
578
+ }
579
+
580
+ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
581
+
582
+
583
+ /**
584
+ * @ngdoc object
585
+ * @name ngRoute.$routeParams
586
+ * @requires $route
587
+ *
588
+ * @description
589
+ * Current set of route parameters. The route parameters are a combination of the
590
+ * {@link ng.$location $location} `search()`, and `path()`. The `path` parameters
591
+ * are extracted when the {@link ngRoute.$route $route} path is matched.
592
+ *
593
+ * In case of parameter name collision, `path` params take precedence over `search` params.
594
+ *
595
+ * The service guarantees that the identity of the `$routeParams` object will remain unchanged
596
+ * (but its properties will likely change) even when a route change occurs.
597
+ *
598
+ * Note that the `$routeParams` are only updated *after* a route change completes successfully.
599
+ * This means that you cannot rely on `$routeParams` being correct in route resolve functions.
600
+ * Instead you can use `$route.current.params` to access the new route's parameters.
601
+ *
602
+ * @example
603
+ * <pre>
604
+ * // Given:
605
+ * // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
606
+ * // Route: /Chapter/:chapterId/Section/:sectionId
607
+ * //
608
+ * // Then
609
+ * $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
610
+ * </pre>
611
+ */
612
+ function $RouteParamsProvider() {
613
+ this.$get = function() { return {}; };
614
+ }
615
+
616
+ /**
617
+ * @ngdoc directive
618
+ * @name ngRoute.directive:ngView
619
+ * @restrict ECA
620
+ *
621
+ * @description
622
+ * # Overview
623
+ * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
624
+ * including the rendered template of the current route into the main layout (`index.html`) file.
625
+ * Every time the current route changes, the included view changes with it according to the
626
+ * configuration of the `$route` service.
627
+ *
628
+ * @animations
629
+ * enter - animation is used to bring new content into the browser.
630
+ * leave - animation is used to animate existing content away.
631
+ *
632
+ * The enter and leave animation occur concurrently.
633
+ *
634
+ * @scope
635
+ * @example
636
+ <example module="ngViewExample" deps="angular-route.js" animations="true">
637
+ <file name="index.html">
638
+ <div ng-controller="MainCntl as main">
639
+ Choose:
640
+ <a href="Book/Moby">Moby</a> |
641
+ <a href="Book/Moby/ch/1">Moby: Ch1</a> |
642
+ <a href="Book/Gatsby">Gatsby</a> |
643
+ <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
644
+ <a href="Book/Scarlet">Scarlet Letter</a><br/>
645
+
646
+ <div class="example-animate-container">
647
+ <div ng-view class="view-example"></div>
648
+ </div>
649
+ <hr />
650
+
651
+ <pre>$location.path() = {{main.$location.path()}}</pre>
652
+ <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
653
+ <pre>$route.current.params = {{main.$route.current.params}}</pre>
654
+ <pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
655
+ <pre>$routeParams = {{main.$routeParams}}</pre>
656
+ </div>
657
+ </file>
658
+
659
+ <file name="book.html">
660
+ <div>
661
+ controller: {{book.name}}<br />
662
+ Book Id: {{book.params.bookId}}<br />
663
+ </div>
664
+ </file>
665
+
666
+ <file name="chapter.html">
667
+ <div>
668
+ controller: {{chapter.name}}<br />
669
+ Book Id: {{chapter.params.bookId}}<br />
670
+ Chapter Id: {{chapter.params.chapterId}}
671
+ </div>
672
+ </file>
673
+
674
+ <file name="animations.css">
675
+ .example-animate-container {
676
+ position:relative;
677
+ background:white;
678
+ border:1px solid black;
679
+ height:40px;
680
+ overflow:hidden;
681
+ }
682
+
683
+ .example-animate-container > div {
684
+ padding:10px;
685
+ }
686
+
687
+ .view-example.ng-enter, .view-example.ng-leave {
688
+ -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
689
+ -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
690
+ -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
691
+ transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
692
+
693
+ display:block;
694
+ width:100%;
695
+ border-left:1px solid black;
696
+
697
+ position:absolute;
698
+ top:0;
699
+ left:0;
700
+ right:0;
701
+ bottom:0;
702
+ padding:10px;
703
+ }
704
+
705
+ .example-animate-container {
706
+ position:relative;
707
+ height:100px;
708
+ }
709
+
710
+ .view-example.ng-enter {
711
+ left:100%;
712
+ }
713
+ .view-example.ng-enter.ng-enter-active {
714
+ left:0;
715
+ }
716
+
717
+ .view-example.ng-leave { }
718
+ .view-example.ng-leave.ng-leave-active {
719
+ left:-100%;
720
+ }
721
+ </file>
722
+
723
+ <file name="script.js">
724
+ angular.module('ngViewExample', ['ngRoute', 'ngAnimate'], function($routeProvider, $locationProvider) {
725
+ $routeProvider.when('/Book/:bookId', {
726
+ templateUrl: 'book.html',
727
+ controller: BookCntl,
728
+ controllerAs: 'book'
729
+ });
730
+ $routeProvider.when('/Book/:bookId/ch/:chapterId', {
731
+ templateUrl: 'chapter.html',
732
+ controller: ChapterCntl,
733
+ controllerAs: 'chapter'
734
+ });
735
+
736
+ // configure html5 to get links working on jsfiddle
737
+ $locationProvider.html5Mode(true);
738
+ });
739
+
740
+ function MainCntl($route, $routeParams, $location) {
741
+ this.$route = $route;
742
+ this.$location = $location;
743
+ this.$routeParams = $routeParams;
744
+ }
745
+
746
+ function BookCntl($routeParams) {
747
+ this.name = "BookCntl";
748
+ this.params = $routeParams;
749
+ }
750
+
751
+ function ChapterCntl($routeParams) {
752
+ this.name = "ChapterCntl";
753
+ this.params = $routeParams;
754
+ }
755
+ </file>
756
+
757
+ <file name="scenario.js">
758
+ it('should load and compile correct template', function() {
759
+ element('a:contains("Moby: Ch1")').click();
760
+ var content = element('.doc-example-live [ng-view]').text();
761
+ expect(content).toMatch(/controller\: ChapterCntl/);
762
+ expect(content).toMatch(/Book Id\: Moby/);
763
+ expect(content).toMatch(/Chapter Id\: 1/);
764
+
765
+ element('a:contains("Scarlet")').click();
766
+ content = element('.doc-example-live [ng-view]').text();
767
+ expect(content).toMatch(/controller\: BookCntl/);
768
+ expect(content).toMatch(/Book Id\: Scarlet/);
769
+ });
770
+ </file>
771
+ </example>
772
+ */
773
+
774
+
775
+ /**
776
+ * @ngdoc event
777
+ * @name ngRoute.directive:ngView#$viewContentLoaded
778
+ * @eventOf ngRoute.directive:ngView
779
+ * @eventType emit on the current ngView scope
780
+ * @description
781
+ * Emitted every time the ngView content is reloaded.
782
+ */
783
+ var NG_VIEW_PRIORITY = 500;
784
+ var ngViewDirective = ['$route', '$anchorScroll', '$compile', '$controller', '$animate',
785
+ function($route, $anchorScroll, $compile, $controller, $animate) {
786
+ return {
787
+ restrict: 'ECA',
788
+ terminal: true,
789
+ priority: NG_VIEW_PRIORITY,
790
+ compile: function(element, attr) {
791
+ var onloadExp = attr.onload || '';
792
+
793
+ element.html('');
794
+ var anchor = jqLite(document.createComment(' ngView '));
795
+ element.replaceWith(anchor);
796
+
797
+ return function(scope) {
798
+ var currentScope, currentElement;
799
+
800
+ scope.$on('$routeChangeSuccess', update);
801
+ update();
802
+
803
+ function cleanupLastView() {
804
+ if (currentScope) {
805
+ currentScope.$destroy();
806
+ currentScope = null;
807
+ }
808
+ if(currentElement) {
809
+ $animate.leave(currentElement);
810
+ currentElement = null;
811
+ }
812
+ }
813
+
814
+ function update() {
815
+ var locals = $route.current && $route.current.locals,
816
+ template = locals && locals.$template;
817
+
818
+ if (template) {
819
+ cleanupLastView();
820
+
821
+ currentScope = scope.$new();
822
+ currentElement = element.clone();
823
+ currentElement.html(template);
824
+ $animate.enter(currentElement, null, anchor);
825
+
826
+ var link = $compile(currentElement, false, NG_VIEW_PRIORITY - 1),
827
+ current = $route.current;
828
+
829
+ if (current.controller) {
830
+ locals.$scope = currentScope;
831
+ var controller = $controller(current.controller, locals);
832
+ if (current.controllerAs) {
833
+ currentScope[current.controllerAs] = controller;
834
+ }
835
+ currentElement.data('$ngControllerController', controller);
836
+ currentElement.children().data('$ngControllerController', controller);
837
+ }
838
+
839
+ current.scope = currentScope;
840
+
841
+ link(currentScope);
842
+
843
+ currentScope.$emit('$viewContentLoaded');
844
+ currentScope.$eval(onloadExp);
845
+
846
+ // $anchorScroll might listen on event...
847
+ $anchorScroll();
848
+ } else {
849
+ cleanupLastView();
850
+ }
851
+ }
852
+ }
853
+ }
854
+ };
855
+ }];
856
+
857
+ ngRouteModule.directive('ngView', ngViewDirective);
858
+
859
+
860
+ })(window, window.angular);