angular-rails-engine 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig ADDED
Binary file
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in bootstrap-rails-engine.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  Make [Angular.js](http://angularjs.org) into Rails Engine.
3
3
 
4
4
  ## Version
5
- Angular.js 1.0.2
5
+ Angular.js 1.0.6
6
6
 
7
7
  ## Rails 3.1 or later
8
8
  Include Gemfile,
@@ -14,6 +14,9 @@ Gem::Specification.new do |gem|
14
14
  gem.files = `git ls-files`.split("\n")
15
15
  gem.require_paths = ["lib"]
16
16
 
17
+ gem.signing_key = File.join(Dir.home,'/.gem/trust/gem-private_key.pem')
18
+ gem.cert_chain = ['gem-public_cert.pem']
19
+
17
20
  gem.add_dependency "railties", ">= 3.0"
18
21
  gem.add_development_dependency "bundler", ">= 1.0"
19
22
  gem.add_development_dependency "rake"
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.2
2
+ * @license AngularJS v1.0.6
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -25,6 +25,18 @@ angular.module('ngCookies', ['ng']).
25
25
  * this object, new cookies are created/deleted at the end of current $eval.
26
26
  *
27
27
  * @example
28
+ <doc:example>
29
+ <doc:source>
30
+ <script>
31
+ function ExampleController($cookies) {
32
+ // Retrieving a cookie
33
+ var favoriteCookie = $cookies.myFavorite;
34
+ // Setting a cookie
35
+ $cookies.myFavorite = 'oatmeal';
36
+ }
37
+ </script>
38
+ </doc:source>
39
+ </doc:example>
28
40
  */
29
41
  factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
30
42
  var cookies = {},
@@ -168,4 +180,5 @@ angular.module('ngCookies', ['ng']).
168
180
 
169
181
  }]);
170
182
 
183
+
171
184
  })(window, window.angular);
@@ -1,5 +1,5 @@
1
1
  /*
2
- AngularJS v1.0.2
2
+ AngularJS v1.0.6
3
3
  (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.2
2
+ * @license AngularJS v1.0.6
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -12,7 +12,7 @@
12
12
  * @description
13
13
  */
14
14
 
15
- /**
15
+ /**
16
16
  * @ngdoc object
17
17
  * @name ngResource.$resource
18
18
  * @requires $http
@@ -24,8 +24,21 @@
24
24
  * The returned resource object has action methods which provide high-level behaviors without
25
25
  * the need to interact with the low level {@link ng.$http $http} service.
26
26
  *
27
+ * # Installation
28
+ * To use $resource make sure you have included the `angular-resource.js` that comes in Angular
29
+ * package. You can also find this file on Google CDN, bower as well as at
30
+ * {@link http://code.angularjs.org/ code.angularjs.org}.
31
+ *
32
+ * Finally load the module in your application:
33
+ *
34
+ * angular.module('app', ['ngResource']);
35
+ *
36
+ * and you are ready to get started!
37
+ *
27
38
  * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
28
- * `/user/:username`.
39
+ * `/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')`.
29
42
  *
30
43
  * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
31
44
  * `actions` methods.
@@ -67,9 +80,9 @@
67
80
  *
68
81
  * Calling these methods invoke an {@link ng.$http} with the specified http method,
69
82
  * destination and parameters. When the data is returned from the server then the object is an
70
- * instance of the resource class `save`, `remove` and `delete` actions are available on it as
71
- * methods with the `$` prefix. This allows you to easily perform CRUD operations (create, read,
72
- * update, delete) on server-side data like this:
83
+ * instance of the resource class. The actions `save`, `remove` and `delete` are available on it
84
+ * as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
85
+ * read, update, delete) on server-side data like this:
73
86
  * <pre>
74
87
  var User = $resource('/user/:userId', {userId:'@id'});
75
88
  var user = User.get({userId:123}, function() {
@@ -149,9 +162,9 @@
149
162
  });
150
163
  </pre>
151
164
  *
152
- * It's worth noting that the success callback for `get`, `query` and other method gets passed
153
- * in the response that came from the server as well as $http header getter function, so one
154
- * could rewrite the above example and get access to http headers as:
165
+ * It's worth noting that the success callback for `get`, `query` and other method gets passed
166
+ * in the response that came from the server as well as $http header getter function, so one
167
+ * could rewrite the above example and get access to http headers as:
155
168
  *
156
169
  <pre>
157
170
  var User = $resource('/user/:userId', {userId:'@id'});
@@ -230,51 +243,51 @@ angular.module('ngResource', ['ng']).
230
243
  return $parse(path)(obj);
231
244
  };
232
245
 
233
- /**
234
- * We need our custom mehtod because encodeURIComponent is too agressive and doesn't follow
235
- * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
236
- * segments:
237
- * segment = *pchar
238
- * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
239
- * pct-encoded = "%" HEXDIG HEXDIG
240
- * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
241
- * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
242
- * / "*" / "+" / "," / ";" / "="
243
- */
244
- function encodeUriSegment(val) {
245
- return encodeUriQuery(val, true).
246
- replace(/%26/gi, '&').
247
- replace(/%3D/gi, '=').
248
- replace(/%2B/gi, '+');
249
- }
250
-
251
-
252
- /**
253
- * This method is intended for encoding *key* or *value* parts of query component. We need a custom
254
- * method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be
255
- * encoded per http://tools.ietf.org/html/rfc3986:
256
- * query = *( pchar / "/" / "?" )
257
- * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
258
- * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
259
- * pct-encoded = "%" HEXDIG HEXDIG
260
- * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
261
- * / "*" / "+" / "," / ";" / "="
262
- */
263
- function encodeUriQuery(val, pctEncodeSpaces) {
264
- return encodeURIComponent(val).
265
- replace(/%40/gi, '@').
266
- replace(/%3A/gi, ':').
267
- replace(/%24/g, '$').
268
- replace(/%2C/gi, ',').
269
- replace((pctEncodeSpaces ? null : /%20/g), '+');
270
- }
271
-
272
- function Route(template, defaults) {
246
+ /**
247
+ * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
248
+ * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
249
+ * segments:
250
+ * segment = *pchar
251
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
252
+ * pct-encoded = "%" HEXDIG HEXDIG
253
+ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
254
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
255
+ * / "*" / "+" / "," / ";" / "="
256
+ */
257
+ function encodeUriSegment(val) {
258
+ return encodeUriQuery(val, true).
259
+ replace(/%26/gi, '&').
260
+ replace(/%3D/gi, '=').
261
+ replace(/%2B/gi, '+');
262
+ }
263
+
264
+
265
+ /**
266
+ * This method is intended for encoding *key* or *value* parts of query component. We need a custom
267
+ * method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be
268
+ * encoded per http://tools.ietf.org/html/rfc3986:
269
+ * query = *( pchar / "/" / "?" )
270
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
271
+ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
272
+ * pct-encoded = "%" HEXDIG HEXDIG
273
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
274
+ * / "*" / "+" / "," / ";" / "="
275
+ */
276
+ function encodeUriQuery(val, pctEncodeSpaces) {
277
+ return encodeURIComponent(val).
278
+ replace(/%40/gi, '@').
279
+ replace(/%3A/gi, ':').
280
+ replace(/%24/g, '$').
281
+ replace(/%2C/gi, ',').
282
+ replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
283
+ }
284
+
285
+ function Route(template, defaults) {
273
286
  this.template = template = template + '#';
274
287
  this.defaults = defaults || {};
275
288
  var urlParams = this.urlParams = {};
276
289
  forEach(template.split(/\W/), function(param){
277
- if (param && template.match(new RegExp("[^\\\\]:" + param + "\\W"))) {
290
+ if (param && (new RegExp("(^|[^\\\\]):" + param + "\\W").test(template))) {
278
291
  urlParams[param] = true;
279
292
  }
280
293
  });
@@ -285,12 +298,25 @@ angular.module('ngResource', ['ng']).
285
298
  url: function(params) {
286
299
  var self = this,
287
300
  url = this.template,
301
+ val,
288
302
  encodedVal;
289
303
 
290
304
  params = params || {};
291
305
  forEach(this.urlParams, function(_, urlParam){
292
- encodedVal = encodeUriSegment(params[urlParam] || self.defaults[urlParam] || "");
293
- url = url.replace(new RegExp(":" + urlParam + "(\\W)"), encodedVal + "$1");
306
+ val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
307
+ if (angular.isDefined(val) && val !== null) {
308
+ encodedVal = encodeUriSegment(val);
309
+ url = url.replace(new RegExp(":" + urlParam + "(\\W)", "g"), encodedVal + "$1");
310
+ } else {
311
+ url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W)", "g"), function(match,
312
+ leadingSlashes, tail) {
313
+ if (tail.charAt(0) == '/') {
314
+ return tail;
315
+ } else {
316
+ return leadingSlashes + tail;
317
+ }
318
+ });
319
+ }
294
320
  });
295
321
  url = url.replace(/\/?#$/, '');
296
322
  var query = [];
@@ -311,9 +337,10 @@ angular.module('ngResource', ['ng']).
311
337
 
312
338
  actions = extend({}, DEFAULT_ACTIONS, actions);
313
339
 
314
- function extractParams(data){
340
+ function extractParams(data, actionParams){
315
341
  var ids = {};
316
- forEach(paramDefaults || {}, function(value, key){
342
+ actionParams = extend({}, paramDefaults, actionParams);
343
+ forEach(actionParams, function(value, key){
317
344
  ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
318
345
  });
319
346
  return ids;
@@ -324,6 +351,7 @@ angular.module('ngResource', ['ng']).
324
351
  }
325
352
 
326
353
  forEach(actions, function(action, name) {
354
+ action.method = angular.uppercase(action.method);
327
355
  var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH';
328
356
  Resource[name] = function(a1, a2, a3, a4) {
329
357
  var params = {};
@@ -367,7 +395,7 @@ angular.module('ngResource', ['ng']).
367
395
  var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
368
396
  $http({
369
397
  method: action.method,
370
- url: route.url(extend({}, extractParams(data), action.params || {}, params)),
398
+ url: route.url(extend({}, extractParams(data, action.params || {}), params)),
371
399
  data: data
372
400
  }).then(function(response) {
373
401
  var data = response.data;
@@ -389,11 +417,6 @@ angular.module('ngResource', ['ng']).
389
417
  };
390
418
 
391
419
 
392
- Resource.bind = function(additionalParamDefaults){
393
- return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
394
- };
395
-
396
-
397
420
  Resource.prototype['$' + name] = function(a1, a2, a3) {
398
421
  var params = extractParams(this),
399
422
  success = noop,
@@ -419,10 +442,16 @@ angular.module('ngResource', ['ng']).
419
442
  Resource[name].call(this, params, data, success, error);
420
443
  };
421
444
  });
445
+
446
+ Resource.bind = function(additionalParamDefaults){
447
+ return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
448
+ };
449
+
422
450
  return Resource;
423
451
  }
424
452
 
425
453
  return ResourceFactory;
426
454
  }]);
427
455
 
456
+
428
457
  })(window, window.angular);
@@ -0,0 +1,10 @@
1
+ /*
2
+ AngularJS v1.0.6
3
+ (c) 2010-2012 Google, Inc. http://angularjs.org
4
+ License: MIT
5
+ */
6
+ (function(C,d,w){'use strict';d.module("ngResource",["ng"]).factory("$resource",["$http","$parse",function(x,y){function s(b,e){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,e?"%20":"+")}function t(b,e){this.template=b+="#";this.defaults=e||{};var a=this.urlParams={};h(b.split(/\W/),function(f){f&&RegExp("(^|[^\\\\]):"+f+"\\W").test(b)&&(a[f]=!0)});this.template=b.replace(/\\:/g,":")}function u(b,e,a){function f(m,a){var b=
7
+ {},a=o({},e,a);h(a,function(a,z){var c;a.charAt&&a.charAt(0)=="@"?(c=a.substr(1),c=y(c)(m)):c=a;b[z]=c});return b}function g(a){v(a||{},this)}var k=new t(b),a=o({},A,a);h(a,function(a,b){a.method=d.uppercase(a.method);var e=a.method=="POST"||a.method=="PUT"||a.method=="PATCH";g[b]=function(b,c,d,B){var j={},i,l=p,q=null;switch(arguments.length){case 4:q=B,l=d;case 3:case 2:if(r(c)){if(r(b)){l=b;q=c;break}l=c;q=d}else{j=b;i=c;l=d;break}case 1:r(b)?l=b:e?i=b:j=b;break;case 0:break;default:throw"Expected between 0-4 arguments [params, data, success, error], got "+
8
+ arguments.length+" arguments.";}var n=this instanceof g?this:a.isArray?[]:new g(i);x({method:a.method,url:k.url(o({},f(i,a.params||{}),j)),data:i}).then(function(b){var c=b.data;if(c)a.isArray?(n.length=0,h(c,function(a){n.push(new g(a))})):v(c,n);(l||p)(n,b.headers)},q);return n};g.prototype["$"+b]=function(a,d,h){var m=f(this),j=p,i;switch(arguments.length){case 3:m=a;j=d;i=h;break;case 2:case 1:r(a)?(j=a,i=d):(m=a,j=d||p);case 0:break;default:throw"Expected between 1-3 arguments [params, success, error], got "+
9
+ arguments.length+" arguments.";}g[b].call(this,m,e?this:w,j,i)}});g.bind=function(d){return u(b,o({},e,d),a)};return g}var A={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},p=d.noop,h=d.forEach,o=d.extend,v=d.copy,r=d.isFunction;t.prototype={url:function(b){var e=this,a=this.template,f,g,b=b||{};h(this.urlParams,function(h,c){f=b.hasOwnProperty(c)?b[c]:e.defaults[c];d.isDefined(f)&&f!==null?(g=s(f,!0).replace(/%26/gi,"&").replace(/%3D/gi,
10
+ "=").replace(/%2B/gi,"+"),a=a.replace(RegExp(":"+c+"(\\W)","g"),g+"$1")):a=a.replace(RegExp("(/?):"+c+"(\\W)","g"),function(a,b,c){return c.charAt(0)=="/"?c:b+c})});var a=a.replace(/\/?#$/,""),k=[];h(b,function(a,b){e.urlParams[b]||k.push(s(b)+"="+s(a))});k.sort();a=a.replace(/\/*$/,"");return a+(k.length?"?"+k.join("&"):"")}};return u}])})(window,window.angular);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.2
2
+ * @license AngularJS v1.0.6
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -416,12 +416,13 @@ angular.module('ngSanitize', []).value('$sanitize', $sanitize);
416
416
  angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) {
417
417
  return function(scope, element, attr) {
418
418
  element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
419
- scope.$watch(attr.ngBindHtml, function(value) {
419
+ scope.$watch(attr.ngBindHtml, function ngBindHtmlWatchAction(value) {
420
420
  value = $sanitize(value);
421
421
  element.html(value || '');
422
422
  });
423
423
  };
424
424
  }]);
425
+
425
426
  /**
426
427
  * @ngdoc filter
427
428
  * @name ngSanitize.filter:linky
@@ -532,4 +533,5 @@ angular.module('ngSanitize').filter('linky', function() {
532
533
  };
533
534
  });
534
535
 
536
+
535
537
  })(window, window.angular);
@@ -1,5 +1,5 @@
1
1
  /*
2
- AngularJS v1.0.2
2
+ AngularJS v1.0.6
3
3
  (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.2
2
+ * @license AngularJS v1.0.6
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -34,12 +34,12 @@ var uppercase = function(string){return isString(string) ? string.toUpperCase()
34
34
 
35
35
  var manualLowercase = function(s) {
36
36
  return isString(s)
37
- ? s.replace(/[A-Z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) | 32);})
37
+ ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
38
38
  : s;
39
39
  };
40
40
  var manualUppercase = function(s) {
41
41
  return isString(s)
42
- ? s.replace(/[a-z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) & ~32);})
42
+ ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
43
43
  : s;
44
44
  };
45
45
 
@@ -52,11 +52,8 @@ if ('i' !== 'I'.toLowerCase()) {
52
52
  uppercase = manualUppercase;
53
53
  }
54
54
 
55
- function fromCharCode(code) {return String.fromCharCode(code);}
56
55
 
57
-
58
- var Error = window.Error,
59
- /** holds major version number for IE or NaN for real browsers */
56
+ var /** holds major version number for IE or NaN for real browsers */
60
57
  msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
61
58
  jqLite, // delay binding since jQuery could be loaded after us.
62
59
  jQuery, // delay binding
@@ -97,6 +94,30 @@ var Error = window.Error,
97
94
  * @param {Object=} context Object to become context (`this`) for the iterator function.
98
95
  * @returns {Object|Array} Reference to `obj`.
99
96
  */
97
+
98
+
99
+ /**
100
+ * @private
101
+ * @param {*} obj
102
+ * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
103
+ */
104
+ function isArrayLike(obj) {
105
+ if (!obj || (typeof obj.length !== 'number')) return false;
106
+
107
+ // We have on object which has length property. Should we treat it as array?
108
+ if (typeof obj.hasOwnProperty != 'function' &&
109
+ typeof obj.constructor != 'function') {
110
+ // This is here for IE8: it is a bogus object treat it as array;
111
+ return true;
112
+ } else {
113
+ return obj instanceof JQLite || // JQLite
114
+ (jQuery && obj instanceof jQuery) || // jQuery
115
+ toString.call(obj) !== '[object Object]' || // some browser native object
116
+ typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
117
+ }
118
+ }
119
+
120
+
100
121
  function forEach(obj, iterator, context) {
101
122
  var key;
102
123
  if (obj) {
@@ -108,7 +129,7 @@ function forEach(obj, iterator, context) {
108
129
  }
109
130
  } else if (obj.forEach && obj.forEach !== forEach) {
110
131
  obj.forEach(iterator, context);
111
- } else if (isObject(obj) && isNumber(obj.length)) {
132
+ } else if (isArrayLike(obj)) {
112
133
  for (key = 0; key < obj.length; key++)
113
134
  iterator.call(context, obj[key], key);
114
135
  } else {
@@ -153,7 +174,7 @@ function reverseParams(iteratorFn) {
153
174
  /**
154
175
  * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
155
176
  * characters such as '012ABC'. The reason why we are not using simply a number counter is that
156
- * the number string gets longer over time, and it can also overflow, where as the the nextId
177
+ * the number string gets longer over time, and it can also overflow, where as the nextId
157
178
  * will grow much slower, it is a string, and it will never overflow.
158
179
  *
159
180
  * @returns an unique alpha-numeric string
@@ -549,9 +570,7 @@ function copy(source, destination){
549
570
  } else {
550
571
  if (source === destination) throw Error("Can't copy equivalent objects or arrays");
551
572
  if (isArray(source)) {
552
- while(destination.length) {
553
- destination.pop();
554
- }
573
+ destination.length = 0;
555
574
  for ( var i = 0; i < source.length; i++) {
556
575
  destination.push(copy(source[i]));
557
576
  }
@@ -627,13 +646,15 @@ function equals(o1, o2) {
627
646
  if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2)) return false;
628
647
  keySet = {};
629
648
  for(key in o1) {
630
- if (key.charAt(0) !== '$' && !isFunction(o1[key]) && !equals(o1[key], o2[key])) {
631
- return false;
632
- }
649
+ if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
650
+ if (!equals(o1[key], o2[key])) return false;
633
651
  keySet[key] = true;
634
652
  }
635
653
  for(key in o2) {
636
- if (!keySet[key] && key.charAt(0) !== '$' && !isFunction(o2[key])) return false;
654
+ if (!keySet[key] &&
655
+ key.charAt(0) !== '$' &&
656
+ o2[key] !== undefined &&
657
+ !isFunction(o2[key])) return false;
637
658
  }
638
659
  return true;
639
660
  }
@@ -760,9 +781,18 @@ function startingTag(element) {
760
781
  // are not allowed to have children. So we just ignore it.
761
782
  element.html('');
762
783
  } catch(e) {}
763
- return jqLite('<div>').append(element).html().
764
- match(/^(<[^>]+>)/)[1].
765
- replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
784
+ // As Per DOM Standards
785
+ var TEXT_NODE = 3;
786
+ var elemHtml = jqLite('<div>').append(element).html();
787
+ try {
788
+ return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
789
+ elemHtml.
790
+ match(/^(<[^>]+>)/)[1].
791
+ replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
792
+ } catch(e) {
793
+ return lowercase(elemHtml);
794
+ }
795
+
766
796
  }
767
797
 
768
798
 
@@ -794,7 +824,7 @@ function toKeyValue(obj) {
794
824
 
795
825
 
796
826
  /**
797
- * We need our custom mehtod because encodeURIComponent is too agressive and doesn't follow
827
+ * We need our custom method because encodeURIComponent is too agressive and doesn't follow
798
828
  * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
799
829
  * segments:
800
830
  * segment = *pchar
@@ -829,7 +859,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
829
859
  replace(/%3A/gi, ':').
830
860
  replace(/%24/g, '$').
831
861
  replace(/%2C/gi, ',').
832
- replace((pctEncodeSpaces ? null : /%20/g), '+');
862
+ replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
833
863
  }
834
864
 
835
865
 
@@ -838,7 +868,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
838
868
  * @name ng.directive:ngApp
839
869
  *
840
870
  * @element ANY
841
- * @param {angular.Module} ngApp on optional application
871
+ * @param {angular.Module} ngApp an optional application
842
872
  * {@link angular.module module} name to load.
843
873
  *
844
874
  * @description
@@ -846,7 +876,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
846
876
  * Use this directive to auto-bootstrap on application. Only
847
877
  * one directive can be used per HTML document. The directive
848
878
  * designates the root of the application and is typically placed
849
- * ot the root of the page.
879
+ * at the root of the page.
850
880
  *
851
881
  * In the example below if the `ngApp` directive would not be placed
852
882
  * on the `html` element then the document would not be compiled
@@ -918,22 +948,38 @@ function angularInit(element, bootstrap) {
918
948
  * @returns {AUTO.$injector} Returns the newly created injector for this app.
919
949
  */
920
950
  function bootstrap(element, modules) {
921
- element = jqLite(element);
922
- modules = modules || [];
923
- modules.unshift(['$provide', function($provide) {
924
- $provide.value('$rootElement', element);
925
- }]);
926
- modules.unshift('ng');
927
- var injector = createInjector(modules);
928
- injector.invoke(
929
- ['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){
930
- scope.$apply(function() {
931
- element.data('$injector', injector);
932
- compile(element)(scope);
933
- });
934
- }]
935
- );
936
- return injector;
951
+ var resumeBootstrapInternal = function() {
952
+ element = jqLite(element);
953
+ modules = modules || [];
954
+ modules.unshift(['$provide', function($provide) {
955
+ $provide.value('$rootElement', element);
956
+ }]);
957
+ modules.unshift('ng');
958
+ var injector = createInjector(modules);
959
+ injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
960
+ function(scope, element, compile, injector) {
961
+ scope.$apply(function() {
962
+ element.data('$injector', injector);
963
+ compile(element)(scope);
964
+ });
965
+ }]
966
+ );
967
+ return injector;
968
+ };
969
+
970
+ var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
971
+
972
+ if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
973
+ return resumeBootstrapInternal();
974
+ }
975
+
976
+ window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
977
+ angular.resumeBootstrap = function(extraModules) {
978
+ forEach(extraModules, function(module) {
979
+ modules.push(module);
980
+ });
981
+ resumeBootstrapInternal();
982
+ };
937
983
  }
938
984
 
939
985
  var SNAKE_CASE_REGEXP = /[A-Z]/g;
@@ -1015,7 +1061,7 @@ function setupModuleLoader(window) {
1015
1061
  *
1016
1062
  * # Module
1017
1063
  *
1018
- * A module is a collocation of services, directives, filters, and configure information. Module
1064
+ * A module is a collocation of services, directives, filters, and configuration information. Module
1019
1065
  * is used to configure the {@link AUTO.$injector $injector}.
1020
1066
  *
1021
1067
  * <pre>
@@ -1045,7 +1091,7 @@ function setupModuleLoader(window) {
1045
1091
  * @param {!string} name The name of the module to create or retrieve.
1046
1092
  * @param {Array.<string>=} requires If specified then new module is being created. If unspecified then the
1047
1093
  * the module is being retrieved for further configuration.
1048
- * @param {Function} configFn Option configuration function for the module. Same as
1094
+ * @param {Function} configFn Optional configuration function for the module. Same as
1049
1095
  * {@link angular.Module#config Module#config()}.
1050
1096
  * @returns {module} new module with the {@link angular.Module} api.
1051
1097
  */
@@ -1200,8 +1246,8 @@ function setupModuleLoader(window) {
1200
1246
  * @param {Function} initializationFn Execute this function after injector creation.
1201
1247
  * Useful for application initialization.
1202
1248
  * @description
1203
- * Use this method to register work which needs to be performed when the injector with
1204
- * with the current module is finished loading.
1249
+ * Use this method to register work which should be performed when the injector is done
1250
+ * loading all modules.
1205
1251
  */
1206
1252
  run: function(block) {
1207
1253
  runBlocks.push(block);
@@ -1247,11 +1293,11 @@ function setupModuleLoader(window) {
1247
1293
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
1248
1294
  */
1249
1295
  var version = {
1250
- full: '1.0.2', // all of these placeholder strings will be replaced by rake's
1251
- major: 1, // compile task
1296
+ full: '1.0.6', // all of these placeholder strings will be replaced by grunt's
1297
+ major: 1, // package task
1252
1298
  minor: 0,
1253
- dot: 2,
1254
- codeName: 'debilitating-awesomeness'
1299
+ dot: 6,
1300
+ codeName: 'universal-irreversibility'
1255
1301
  };
1256
1302
 
1257
1303
 
@@ -1418,11 +1464,12 @@ function publishExternalAPI(angular){
1418
1464
  * - [replaceWith()](http://api.jquery.com/replaceWith/)
1419
1465
  * - [text()](http://api.jquery.com/text/)
1420
1466
  * - [toggleClass()](http://api.jquery.com/toggleClass/)
1467
+ * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
1421
1468
  * - [unbind()](http://api.jquery.com/unbind/)
1422
1469
  * - [val()](http://api.jquery.com/val/)
1423
1470
  * - [wrap()](http://api.jquery.com/wrap/)
1424
1471
  *
1425
- * ## In addtion to the above, Angular privides an additional method to both jQuery and jQuery lite:
1472
+ * ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
1426
1473
  *
1427
1474
  * - `controller(name)` - retrieves the controller of the current element or its parent. By default
1428
1475
  * retrieves controller associated with the `ngController` directive. If `name` is provided as
@@ -1493,12 +1540,7 @@ function JQLitePatchJQueryRemove(name, dispatchThis) {
1493
1540
  for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
1494
1541
  element = jqLite(set[setIndex]);
1495
1542
  if (fireEvent) {
1496
- events = element.data('events');
1497
- if ( (fns = events && events.$destroy) ) {
1498
- forEach(fns, function(fn){
1499
- fn.handler();
1500
- });
1501
- }
1543
+ element.triggerHandler('$destroy');
1502
1544
  } else {
1503
1545
  fireEvent = !fireEvent;
1504
1546
  }
@@ -1630,9 +1672,9 @@ function JQLiteHasClass(element, selector) {
1630
1672
  indexOf( " " + selector + " " ) > -1);
1631
1673
  }
1632
1674
 
1633
- function JQLiteRemoveClass(element, selector) {
1634
- if (selector) {
1635
- forEach(selector.split(' '), function(cssClass) {
1675
+ function JQLiteRemoveClass(element, cssClasses) {
1676
+ if (cssClasses) {
1677
+ forEach(cssClasses.split(' '), function(cssClass) {
1636
1678
  element.className = trim(
1637
1679
  (" " + element.className + " ")
1638
1680
  .replace(/[\n\t]/g, " ")
@@ -1642,9 +1684,9 @@ function JQLiteRemoveClass(element, selector) {
1642
1684
  }
1643
1685
  }
1644
1686
 
1645
- function JQLiteAddClass(element, selector) {
1646
- if (selector) {
1647
- forEach(selector.split(' '), function(cssClass) {
1687
+ function JQLiteAddClass(element, cssClasses) {
1688
+ if (cssClasses) {
1689
+ forEach(cssClasses.split(' '), function(cssClass) {
1648
1690
  if (!JQLiteHasClass(element, cssClass)) {
1649
1691
  element.className = trim(element.className + ' ' + trim(cssClass));
1650
1692
  }
@@ -2015,14 +2057,14 @@ forEach({
2015
2057
  children: function(element) {
2016
2058
  var children = [];
2017
2059
  forEach(element.childNodes, function(element){
2018
- if (element.nodeName != '#text')
2060
+ if (element.nodeType === 1)
2019
2061
  children.push(element);
2020
2062
  });
2021
2063
  return children;
2022
2064
  },
2023
2065
 
2024
2066
  contents: function(element) {
2025
- return element.childNodes;
2067
+ return element.childNodes || [];
2026
2068
  },
2027
2069
 
2028
2070
  append: function(element, node) {
@@ -2085,14 +2127,31 @@ forEach({
2085
2127
  },
2086
2128
 
2087
2129
  next: function(element) {
2088
- return element.nextSibling;
2130
+ if (element.nextElementSibling) {
2131
+ return element.nextElementSibling;
2132
+ }
2133
+
2134
+ // IE8 doesn't have nextElementSibling
2135
+ var elm = element.nextSibling;
2136
+ while (elm != null && elm.nodeType !== 1) {
2137
+ elm = elm.nextSibling;
2138
+ }
2139
+ return elm;
2089
2140
  },
2090
2141
 
2091
2142
  find: function(element, selector) {
2092
2143
  return element.getElementsByTagName(selector);
2093
2144
  },
2094
2145
 
2095
- clone: JQLiteClone
2146
+ clone: JQLiteClone,
2147
+
2148
+ triggerHandler: function(element, eventName) {
2149
+ var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName];
2150
+
2151
+ forEach(eventFns, function(fn) {
2152
+ fn.call(element, null);
2153
+ });
2154
+ }
2096
2155
  }, function(fn, name){
2097
2156
  /**
2098
2157
  * chaining functions
@@ -2210,6 +2269,16 @@ HashQueueMap.prototype = {
2210
2269
  return array.shift();
2211
2270
  }
2212
2271
  }
2272
+ },
2273
+
2274
+ /**
2275
+ * return the first item without deleting it
2276
+ */
2277
+ peek: function(key) {
2278
+ var array = this[hashKey(key)];
2279
+ if (array) {
2280
+ return array[0];
2281
+ }
2213
2282
  }
2214
2283
  };
2215
2284
 
@@ -2233,7 +2302,7 @@ HashQueueMap.prototype = {
2233
2302
  * // create an injector
2234
2303
  * var $injector = angular.injector(['ng']);
2235
2304
  *
2236
- * // use the injector to kick of your application
2305
+ * // use the injector to kick off your application
2237
2306
  * // use the type inference to auto inject arguments, or use implicit injection
2238
2307
  * $injector.invoke(function($rootScope, $compile, $document){
2239
2308
  * $compile($document)($rootScope);
@@ -2253,7 +2322,7 @@ HashQueueMap.prototype = {
2253
2322
 
2254
2323
  var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
2255
2324
  var FN_ARG_SPLIT = /,/;
2256
- var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
2325
+ var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
2257
2326
  var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
2258
2327
  function annotate(fn) {
2259
2328
  var $inject,
@@ -2313,15 +2382,15 @@ function annotate(fn) {
2313
2382
  *
2314
2383
  * <pre>
2315
2384
  * // inferred (only works if code not minified/obfuscated)
2316
- * $inject.invoke(function(serviceA){});
2385
+ * $injector.invoke(function(serviceA){});
2317
2386
  *
2318
2387
  * // annotated
2319
2388
  * function explicit(serviceA) {};
2320
2389
  * explicit.$inject = ['serviceA'];
2321
- * $inject.invoke(explicit);
2390
+ * $injector.invoke(explicit);
2322
2391
  *
2323
2392
  * // inline
2324
- * $inject.invoke(['serviceA', function(serviceA){}]);
2393
+ * $injector.invoke(['serviceA', function(serviceA){}]);
2325
2394
  * </pre>
2326
2395
  *
2327
2396
  * ## Inference
@@ -2405,7 +2474,7 @@ function annotate(fn) {
2405
2474
  * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
2406
2475
  * are supported.
2407
2476
  *
2408
- * # The `$injector` property
2477
+ * # The `$inject` property
2409
2478
  *
2410
2479
  * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
2411
2480
  * services to be injected into the function.
@@ -2467,7 +2536,7 @@ function annotate(fn) {
2467
2536
  * @description
2468
2537
  *
2469
2538
  * Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
2470
- * The providers share the same name as the instance they create with the `Provider` suffixed to them.
2539
+ * The providers share the same name as the instance they create with `Provider` suffixed to them.
2471
2540
  *
2472
2541
  * A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
2473
2542
  * a service. The Provider can have additional methods which would allow for configuration of the provider.
@@ -2653,7 +2722,7 @@ function createInjector(modulesToLoad) {
2653
2722
  }
2654
2723
 
2655
2724
  function provider(name, provider_) {
2656
- if (isFunction(provider_)) {
2725
+ if (isFunction(provider_) || isArray(provider_)) {
2657
2726
  provider_ = providerInjector.instantiate(provider_);
2658
2727
  }
2659
2728
  if (!provider_.$get) {
@@ -2770,7 +2839,7 @@ function createInjector(modulesToLoad) {
2770
2839
  args.push(
2771
2840
  locals && locals.hasOwnProperty(key)
2772
2841
  ? locals[key]
2773
- : getService(key, path)
2842
+ : getService(key)
2774
2843
  );
2775
2844
  }
2776
2845
  if (!fn.$inject) {
@@ -2815,6 +2884,7 @@ function createInjector(modulesToLoad) {
2815
2884
  };
2816
2885
  }
2817
2886
  }
2887
+
2818
2888
  /**
2819
2889
  * @ngdoc function
2820
2890
  * @name ng.$anchorScroll
@@ -2870,11 +2940,12 @@ function $AnchorScrollProvider() {
2870
2940
  }
2871
2941
 
2872
2942
  // does not scroll when user clicks on anchor link that is currently on
2873
- // (no url change, no $locaiton.hash() change), browser native does scroll
2943
+ // (no url change, no $location.hash() change), browser native does scroll
2874
2944
  if (autoScrollingEnabled) {
2875
- $rootScope.$watch(function() {return $location.hash();}, function() {
2876
- $rootScope.$evalAsync(scroll);
2877
- });
2945
+ $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
2946
+ function autoScrollWatchAction() {
2947
+ $rootScope.$evalAsync(scroll);
2948
+ });
2878
2949
  }
2879
2950
 
2880
2951
  return scroll;
@@ -3118,7 +3189,7 @@ function Browser(window, document, $log, $sniffer) {
3118
3189
  */
3119
3190
  self.baseHref = function() {
3120
3191
  var href = baseElement.attr('href');
3121
- return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
3192
+ return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
3122
3193
  };
3123
3194
 
3124
3195
  //////////////////////////////////////////////////////////////
@@ -3157,14 +3228,15 @@ function Browser(window, document, $log, $sniffer) {
3157
3228
  } else {
3158
3229
  if (isString(value)) {
3159
3230
  cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1;
3231
+
3232
+ // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
3233
+ // - 300 cookies
3234
+ // - 20 cookies per unique domain
3235
+ // - 4096 bytes per cookie
3160
3236
  if (cookieLength > 4096) {
3161
3237
  $log.warn("Cookie '"+ name +"' possibly not set or overflowed because it was too large ("+
3162
3238
  cookieLength + " > 4096 bytes)!");
3163
3239
  }
3164
- if (lastCookies.length > 20) {
3165
- $log.warn("Cookie '"+ name +"' possibly not set or overflowed because too many cookies " +
3166
- "were already set (" + lastCookies.length + " > 20 )");
3167
- }
3168
3240
  }
3169
3241
  }
3170
3242
  } else {
@@ -3241,6 +3313,7 @@ function $BrowserProvider(){
3241
3313
  return new Browser($window, $document, $log, $sniffer);
3242
3314
  }];
3243
3315
  }
3316
+
3244
3317
  /**
3245
3318
  * @ngdoc object
3246
3319
  * @name ng.$cacheFactory
@@ -3258,10 +3331,10 @@ function $BrowserProvider(){
3258
3331
  *
3259
3332
  * - `{object}` `info()` — Returns id, size, and options of cache.
3260
3333
  * - `{void}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache.
3261
- * - `{{*}} `get({string} key) — Returns cached value for `key` or undefined for cache miss.
3262
- * - `{void}` `remove({string} key) — Removes a key-value pair from the cache.
3263
- * - `{void}` `removeAll() — Removes all cached values.
3264
- * - `{void}` `destroy() — Removes references to this cache from $cacheFactory.
3334
+ * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
3335
+ * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
3336
+ * - `{void}` `removeAll()` — Removes all cached values.
3337
+ * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
3265
3338
  *
3266
3339
  */
3267
3340
  function $CacheFactoryProvider() {
@@ -3313,6 +3386,8 @@ function $CacheFactoryProvider() {
3313
3386
  remove: function(key) {
3314
3387
  var lruEntry = lruHash[key];
3315
3388
 
3389
+ if (!lruEntry) return;
3390
+
3316
3391
  if (lruEntry == freshEnd) freshEnd = lruEntry.p;
3317
3392
  if (lruEntry == staleEnd) staleEnd = lruEntry.n;
3318
3393
  link(lruEntry.n,lruEntry.p);
@@ -3565,7 +3640,8 @@ function $CompileProvider($provide) {
3565
3640
  Suffix = 'Directive',
3566
3641
  COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
3567
3642
  CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
3568
- MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ';
3643
+ MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
3644
+ urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;
3569
3645
 
3570
3646
 
3571
3647
  /**
@@ -3619,11 +3695,41 @@ function $CompileProvider($provide) {
3619
3695
  };
3620
3696
 
3621
3697
 
3698
+ /**
3699
+ * @ngdoc function
3700
+ * @name ng.$compileProvider#urlSanitizationWhitelist
3701
+ * @methodOf ng.$compileProvider
3702
+ * @function
3703
+ *
3704
+ * @description
3705
+ * Retrieves or overrides the default regular expression that is used for whitelisting of safe
3706
+ * urls during a[href] sanitization.
3707
+ *
3708
+ * The sanitization is a security measure aimed at prevent XSS attacks via html links.
3709
+ *
3710
+ * Any url about to be assigned to a[href] via data-binding is first normalized and turned into an
3711
+ * absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular
3712
+ * expression. If a match is found the original url is written into the dom. Otherwise the
3713
+ * absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM.
3714
+ *
3715
+ * @param {RegExp=} regexp New regexp to whitelist urls with.
3716
+ * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
3717
+ * chaining otherwise.
3718
+ */
3719
+ this.urlSanitizationWhitelist = function(regexp) {
3720
+ if (isDefined(regexp)) {
3721
+ urlSanitizationWhitelist = regexp;
3722
+ return this;
3723
+ }
3724
+ return urlSanitizationWhitelist;
3725
+ };
3726
+
3727
+
3622
3728
  this.$get = [
3623
3729
  '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
3624
- '$controller', '$rootScope',
3730
+ '$controller', '$rootScope', '$document',
3625
3731
  function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
3626
- $controller, $rootScope) {
3732
+ $controller, $rootScope, $document) {
3627
3733
 
3628
3734
  var Attributes = function(element, attr) {
3629
3735
  this.$$element = element;
@@ -3645,7 +3751,8 @@ function $CompileProvider($provide) {
3645
3751
  */
3646
3752
  $set: function(key, value, writeAttr, attrName) {
3647
3753
  var booleanKey = getBooleanAttrName(this.$$element[0], key),
3648
- $$observers = this.$$observers;
3754
+ $$observers = this.$$observers,
3755
+ normalizedVal;
3649
3756
 
3650
3757
  if (booleanKey) {
3651
3758
  this.$$element.prop(key, value);
@@ -3664,6 +3771,19 @@ function $CompileProvider($provide) {
3664
3771
  }
3665
3772
  }
3666
3773
 
3774
+
3775
+ // sanitize a[href] values
3776
+ if (nodeName_(this.$$element[0]) === 'A' && key === 'href') {
3777
+ urlSanitizationNode.setAttribute('href', value);
3778
+
3779
+ // href property always returns normalized absolute url, so we can match against that
3780
+ normalizedVal = urlSanitizationNode.href;
3781
+ if (!normalizedVal.match(urlSanitizationWhitelist)) {
3782
+ this[key] = value = 'unsafe:' + normalizedVal;
3783
+ }
3784
+ }
3785
+
3786
+
3667
3787
  if (writeAttr !== false) {
3668
3788
  if (value === null || value === undefined) {
3669
3789
  this.$$element.removeAttr(attrName);
@@ -3707,7 +3827,8 @@ function $CompileProvider($provide) {
3707
3827
  }
3708
3828
  };
3709
3829
 
3710
- var startSymbol = $interpolate.startSymbol(),
3830
+ var urlSanitizationNode = $document[0].createElement('a'),
3831
+ startSymbol = $interpolate.startSymbol(),
3711
3832
  endSymbol = $interpolate.endSymbol(),
3712
3833
  denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
3713
3834
  ? identity
@@ -3720,27 +3841,34 @@ function $CompileProvider($provide) {
3720
3841
 
3721
3842
  //================================
3722
3843
 
3723
- function compile($compileNode, transcludeFn, maxPriority) {
3724
- if (!($compileNode instanceof jqLite)) {
3725
- // jquery always rewraps, where as we need to preserve the original selector so that we can modify it.
3726
- $compileNode = jqLite($compileNode);
3844
+ function compile($compileNodes, transcludeFn, maxPriority) {
3845
+ if (!($compileNodes instanceof jqLite)) {
3846
+ // jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
3847
+ $compileNodes = jqLite($compileNodes);
3727
3848
  }
3728
3849
  // We can not compile top level text elements since text nodes can be merged and we will
3729
3850
  // not be able to attach scope data to them, so we will wrap them in <span>
3730
- forEach($compileNode, function(node, index){
3731
- if (node.nodeType == 3 /* text node */) {
3732
- $compileNode[index] = jqLite(node).wrap('<span></span>').parent()[0];
3851
+ forEach($compileNodes, function(node, index){
3852
+ if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) {
3853
+ $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
3733
3854
  }
3734
3855
  });
3735
- var compositeLinkFn = compileNodes($compileNode, transcludeFn, $compileNode, maxPriority);
3736
- return function(scope, cloneConnectFn){
3856
+ var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority);
3857
+ return function publicLinkFn(scope, cloneConnectFn){
3737
3858
  assertArg(scope, 'scope');
3738
3859
  // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
3739
3860
  // and sometimes changes the structure of the DOM.
3740
3861
  var $linkNode = cloneConnectFn
3741
- ? JQLitePrototype.clone.call($compileNode) // IMPORTANT!!!
3742
- : $compileNode;
3743
- $linkNode.data('$scope', scope);
3862
+ ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
3863
+ : $compileNodes;
3864
+
3865
+ // Attach scope only to non-text nodes.
3866
+ for(var i = 0, ii = $linkNode.length; i<ii; i++) {
3867
+ var node = $linkNode[i];
3868
+ if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
3869
+ $linkNode.eq(i).data('$scope', scope);
3870
+ }
3871
+ }
3744
3872
  safeAddClass($linkNode, 'ng-scope');
3745
3873
  if (cloneConnectFn) cloneConnectFn($linkNode, scope);
3746
3874
  if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
@@ -3767,7 +3895,7 @@ function $CompileProvider($provide) {
3767
3895
  * functions return values - the linking functions - are combined into a composite linking
3768
3896
  * function, which is the a linking function for the node.
3769
3897
  *
3770
- * @param {NodeList} nodeList an array of nodes to compile
3898
+ * @param {NodeList} nodeList an array of nodes or NodeList to compile
3771
3899
  * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
3772
3900
  * scope argument is auto-generated to the new child of the transcluded parent scope.
3773
3901
  * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
@@ -3777,78 +3905,86 @@ function $CompileProvider($provide) {
3777
3905
  * @returns {?function} A composite linking function of all of the matched directives or null.
3778
3906
  */
3779
3907
  function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) {
3780
- var linkFns = [],
3781
- nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
3908
+ var linkFns = [],
3909
+ nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
3782
3910
 
3783
- for(var i = 0; i < nodeList.length; i++) {
3784
- attrs = new Attributes();
3911
+ for(var i = 0; i < nodeList.length; i++) {
3912
+ attrs = new Attributes();
3785
3913
 
3786
- // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
3787
- directives = collectDirectives(nodeList[i], [], attrs, maxPriority);
3914
+ // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
3915
+ directives = collectDirectives(nodeList[i], [], attrs, maxPriority);
3788
3916
 
3789
- nodeLinkFn = (directives.length)
3790
- ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
3791
- : null;
3917
+ nodeLinkFn = (directives.length)
3918
+ ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
3919
+ : null;
3792
3920
 
3793
- childLinkFn = (nodeLinkFn && nodeLinkFn.terminal)
3794
- ? null
3795
- : compileNodes(nodeList[i].childNodes,
3796
- nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
3921
+ childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)
3922
+ ? null
3923
+ : compileNodes(nodeList[i].childNodes,
3924
+ nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
3797
3925
 
3798
- linkFns.push(nodeLinkFn);
3799
- linkFns.push(childLinkFn);
3800
- linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
3801
- }
3926
+ linkFns.push(nodeLinkFn);
3927
+ linkFns.push(childLinkFn);
3928
+ linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
3929
+ }
3802
3930
 
3803
- // return a linking function if we have found anything, null otherwise
3804
- return linkFnFound ? compositeLinkFn : null;
3931
+ // return a linking function if we have found anything, null otherwise
3932
+ return linkFnFound ? compositeLinkFn : null;
3805
3933
 
3806
- function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
3807
- var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn;
3934
+ function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
3935
+ var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn, i, ii, n;
3808
3936
 
3809
- for(var i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
3810
- node = nodeList[n];
3811
- nodeLinkFn = linkFns[i++];
3812
- childLinkFn = linkFns[i++];
3937
+ // copy nodeList so that linking doesn't break due to live list updates.
3938
+ var stableNodeList = [];
3939
+ for (i = 0, ii = nodeList.length; i < ii; i++) {
3940
+ stableNodeList.push(nodeList[i]);
3941
+ }
3813
3942
 
3814
- if (nodeLinkFn) {
3815
- if (nodeLinkFn.scope) {
3816
- childScope = scope.$new(isObject(nodeLinkFn.scope));
3817
- jqLite(node).data('$scope', childScope);
3818
- } else {
3819
- childScope = scope;
3820
- }
3821
- childTranscludeFn = nodeLinkFn.transclude;
3822
- if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
3823
- nodeLinkFn(childLinkFn, childScope, node, $rootElement,
3824
- (function(transcludeFn) {
3825
- return function(cloneFn) {
3826
- var transcludeScope = scope.$new();
3827
-
3828
- return transcludeFn(transcludeScope, cloneFn).
3829
- bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
3943
+ for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
3944
+ node = stableNodeList[n];
3945
+ nodeLinkFn = linkFns[i++];
3946
+ childLinkFn = linkFns[i++];
3947
+
3948
+ if (nodeLinkFn) {
3949
+ if (nodeLinkFn.scope) {
3950
+ childScope = scope.$new(isObject(nodeLinkFn.scope));
3951
+ jqLite(node).data('$scope', childScope);
3952
+ } else {
3953
+ childScope = scope;
3954
+ }
3955
+ childTranscludeFn = nodeLinkFn.transclude;
3956
+ if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
3957
+ nodeLinkFn(childLinkFn, childScope, node, $rootElement,
3958
+ (function(transcludeFn) {
3959
+ return function(cloneFn) {
3960
+ var transcludeScope = scope.$new();
3961
+ transcludeScope.$$transcluded = true;
3962
+
3963
+ return transcludeFn(transcludeScope, cloneFn).
3964
+ bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
3830
3965
  };
3831
3966
  })(childTranscludeFn || transcludeFn)
3832
- );
3833
- } else {
3834
- nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
3835
- }
3836
- } else if (childLinkFn) {
3837
- childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
3838
- }
3839
- }
3840
- }
3841
- }
3967
+ );
3968
+ } else {
3969
+ nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
3970
+ }
3971
+ } else if (childLinkFn) {
3972
+ childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
3973
+ }
3974
+ }
3975
+ }
3976
+ }
3842
3977
 
3843
3978
 
3844
3979
  /**
3845
- * Looks for directives on the given node ands them to the directive collection which is sorted.
3980
+ * Looks for directives on the given node and adds them to the directive collection which is
3981
+ * sorted.
3846
3982
  *
3847
- * @param node node to search
3848
- * @param directives an array to which the directives are added to. This array is sorted before
3983
+ * @param node Node to search.
3984
+ * @param directives An array to which the directives are added to. This array is sorted before
3849
3985
  * the function returns.
3850
- * @param attrs the shared attrs object which is used to populate the normalized attributes.
3851
- * @param {number=} max directive priority
3986
+ * @param attrs The shared attrs object which is used to populate the normalized attributes.
3987
+ * @param {number=} maxPriority Max directive priority.
3852
3988
  */
3853
3989
  function collectDirectives(node, directives, attrs, maxPriority) {
3854
3990
  var nodeType = node.nodeType,
@@ -3883,7 +4019,7 @@ function $CompileProvider($provide) {
3883
4019
 
3884
4020
  // use class as directive
3885
4021
  className = node.className;
3886
- if (isString(className)) {
4022
+ if (isString(className) && className !== '') {
3887
4023
  while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
3888
4024
  nName = directiveNormalize(match[2]);
3889
4025
  if (addDirective(directives, nName, 'C', maxPriority)) {
@@ -3937,7 +4073,7 @@ function $CompileProvider($provide) {
3937
4073
  preLinkFns = [],
3938
4074
  postLinkFns = [],
3939
4075
  newScopeDirective = null,
3940
- newIsolatedScopeDirective = null,
4076
+ newIsolateScopeDirective = null,
3941
4077
  templateDirective = null,
3942
4078
  $compileNode = templateAttrs.$$element = jqLite(compileNode),
3943
4079
  directive,
@@ -3959,10 +4095,10 @@ function $CompileProvider($provide) {
3959
4095
  }
3960
4096
 
3961
4097
  if (directiveValue = directive.scope) {
3962
- assertNoDuplicate('isolated scope', newIsolatedScopeDirective, directive, $compileNode);
4098
+ assertNoDuplicate('isolated scope', newIsolateScopeDirective, directive, $compileNode);
3963
4099
  if (isObject(directiveValue)) {
3964
4100
  safeAddClass($compileNode, 'ng-isolate-scope');
3965
- newIsolatedScopeDirective = directive;
4101
+ newIsolateScopeDirective = directive;
3966
4102
  }
3967
4103
  safeAddClass($compileNode, 'ng-scope');
3968
4104
  newScopeDirective = newScopeDirective || directive;
@@ -3984,7 +4120,7 @@ function $CompileProvider($provide) {
3984
4120
  if (directiveValue == 'element') {
3985
4121
  $template = jqLite(compileNode);
3986
4122
  $compileNode = templateAttrs.$$element =
3987
- jqLite('<!-- ' + directiveName + ': ' + templateAttrs[directiveName] + ' -->');
4123
+ jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
3988
4124
  compileNode = $compileNode[0];
3989
4125
  replaceWith($rootElement, jqLite($template[0]), compileNode);
3990
4126
  childTranscludeFn = compile($template, transcludeFn, terminalPriority);
@@ -4116,18 +4252,20 @@ function $CompileProvider($provide) {
4116
4252
  }
4117
4253
  $element = attrs.$$element;
4118
4254
 
4119
- if (newScopeDirective && isObject(newScopeDirective.scope)) {
4255
+ if (newIsolateScopeDirective) {
4120
4256
  var LOCAL_REGEXP = /^\s*([@=&])\s*(\w*)\s*$/;
4121
4257
 
4122
4258
  var parentScope = scope.$parent || scope;
4123
4259
 
4124
- forEach(newScopeDirective.scope, function(definiton, scopeName) {
4260
+ forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) {
4125
4261
  var match = definiton.match(LOCAL_REGEXP) || [],
4126
4262
  attrName = match[2]|| scopeName,
4127
4263
  mode = match[1], // @, =, or &
4128
4264
  lastValue,
4129
4265
  parentGet, parentSet;
4130
4266
 
4267
+ scope.$$isolateBindings[scopeName] = mode + attrName;
4268
+
4131
4269
  switch (mode) {
4132
4270
 
4133
4271
  case '@': {
@@ -4144,10 +4282,10 @@ function $CompileProvider($provide) {
4144
4282
  // reset the change, or we will throw this exception on every $digest
4145
4283
  lastValue = scope[scopeName] = parentGet(parentScope);
4146
4284
  throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + attrs[attrName] +
4147
- ' (directive: ' + newScopeDirective.name + ')');
4285
+ ' (directive: ' + newIsolateScopeDirective.name + ')');
4148
4286
  };
4149
4287
  lastValue = scope[scopeName] = parentGet(parentScope);
4150
- scope.$watch(function() {
4288
+ scope.$watch(function parentValueWatch() {
4151
4289
  var parentValue = parentGet(parentScope);
4152
4290
 
4153
4291
  if (parentValue !== scope[scopeName]) {
@@ -4157,7 +4295,7 @@ function $CompileProvider($provide) {
4157
4295
  lastValue = scope[scopeName] = parentValue;
4158
4296
  } else {
4159
4297
  // if the parent can be assigned then do so
4160
- parentSet(parentScope, lastValue = scope[scopeName]);
4298
+ parentSet(parentScope, parentValue = lastValue = scope[scopeName]);
4161
4299
  }
4162
4300
  }
4163
4301
  return parentValue;
@@ -4175,7 +4313,7 @@ function $CompileProvider($provide) {
4175
4313
 
4176
4314
  default: {
4177
4315
  throw Error('Invalid isolate scope definition for directive ' +
4178
- newScopeDirective.name + ': ' + definiton);
4316
+ newIsolateScopeDirective.name + ': ' + definiton);
4179
4317
  }
4180
4318
  }
4181
4319
  });
@@ -4309,7 +4447,7 @@ function $CompileProvider($provide) {
4309
4447
  origAsyncDirective = directives.shift(),
4310
4448
  // The fact that we have to copy and patch the directive seems wrong!
4311
4449
  derivedSyncDirective = extend({}, origAsyncDirective, {
4312
- controller: null, templateUrl: null, transclude: null
4450
+ controller: null, templateUrl: null, transclude: null, scope: null
4313
4451
  });
4314
4452
 
4315
4453
  $compileNode.html('');
@@ -4338,8 +4476,8 @@ function $CompileProvider($provide) {
4338
4476
  }
4339
4477
 
4340
4478
  directives.unshift(derivedSyncDirective);
4341
- afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, tAttrs, childTranscludeFn);
4342
- afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn);
4479
+ afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
4480
+ afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
4343
4481
 
4344
4482
 
4345
4483
  while(linkQueue.length) {
@@ -4401,12 +4539,12 @@ function $CompileProvider($provide) {
4401
4539
  if (interpolateFn) {
4402
4540
  directives.push({
4403
4541
  priority: 0,
4404
- compile: valueFn(function(scope, node) {
4542
+ compile: valueFn(function textInterpolateLinkFn(scope, node) {
4405
4543
  var parent = node.parent(),
4406
4544
  bindings = parent.data('$binding') || [];
4407
4545
  bindings.push(interpolateFn);
4408
4546
  safeAddClass(parent.data('$binding', bindings), 'ng-binding');
4409
- scope.$watch(interpolateFn, function(value) {
4547
+ scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
4410
4548
  node[0].nodeValue = value;
4411
4549
  });
4412
4550
  })
@@ -4418,13 +4556,13 @@ function $CompileProvider($provide) {
4418
4556
  function addAttrInterpolateDirective(node, directives, value, name) {
4419
4557
  var interpolateFn = $interpolate(value, true);
4420
4558
 
4421
-
4422
4559
  // no interpolation found -> ignore
4423
4560
  if (!interpolateFn) return;
4424
4561
 
4562
+
4425
4563
  directives.push({
4426
4564
  priority: 100,
4427
- compile: valueFn(function(scope, element, attr) {
4565
+ compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
4428
4566
  var $$observers = (attr.$$observers || (attr.$$observers = {}));
4429
4567
 
4430
4568
  if (name === 'class') {
@@ -4436,7 +4574,7 @@ function $CompileProvider($provide) {
4436
4574
  attr[name] = undefined;
4437
4575
  ($$observers[name] || ($$observers[name] = [])).$$inter = true;
4438
4576
  (attr.$$observers && attr.$$observers[name].$$scope || scope).
4439
- $watch(interpolateFn, function(value) {
4577
+ $watch(interpolateFn, function interpolateFnWatchAction(value) {
4440
4578
  attr.$set(name, value);
4441
4579
  });
4442
4580
  })
@@ -4604,7 +4742,7 @@ function $ControllerProvider() {
4604
4742
  * @description
4605
4743
  * `$controller` service is responsible for instantiating controllers.
4606
4744
  *
4607
- * It's just simple call to {@link AUTO.$injector $injector}, but extracted into
4745
+ * It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
4608
4746
  * a service, so that one can override this service with {@link https://gist.github.com/1649788
4609
4747
  * BC version}.
4610
4748
  */
@@ -4649,11 +4787,12 @@ function $DocumentProvider(){
4649
4787
  * the browser console.
4650
4788
  *
4651
4789
  * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
4652
- * {@link ngMock.$exceptionHandler mock $exceptionHandler}
4790
+ * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
4653
4791
  *
4654
4792
  * @param {Error} exception Exception associated with the error.
4655
4793
  * @param {string=} cause optional information about the context in which
4656
4794
  * the error was thrown.
4795
+ *
4657
4796
  */
4658
4797
  function $ExceptionHandlerProvider() {
4659
4798
  this.$get = ['$log', function($log){
@@ -4844,7 +4983,7 @@ function $InterpolateProvider() {
4844
4983
  }];
4845
4984
  }
4846
4985
 
4847
- var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
4986
+ var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
4848
4987
  PATH_MATCH = /^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,
4849
4988
  HASH_MATCH = PATH_MATCH,
4850
4989
  DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
@@ -4923,7 +5062,8 @@ function convertToHashbangUrl(url, basePath, hashPrefix) {
4923
5062
  var match = matchUrl(url);
4924
5063
 
4925
5064
  // already hashbang url
4926
- if (decodeURIComponent(match.path) == basePath) {
5065
+ if (decodeURIComponent(match.path) == basePath && !isUndefined(match.hash) &&
5066
+ match.hash.indexOf(hashPrefix) === 0) {
4927
5067
  return url;
4928
5068
  // convert html5 url -> hashbang url
4929
5069
  } else {
@@ -5434,6 +5574,7 @@ function $LocationProvider(){
5434
5574
  var changeCounter = 0;
5435
5575
  $rootScope.$watch(function $locationWatch() {
5436
5576
  var oldUrl = $browser.url();
5577
+ var currentReplace = $location.$$replace;
5437
5578
 
5438
5579
  if (!changeCounter || oldUrl != $location.absUrl()) {
5439
5580
  changeCounter++;
@@ -5442,12 +5583,12 @@ function $LocationProvider(){
5442
5583
  defaultPrevented) {
5443
5584
  $location.$$parse(oldUrl);
5444
5585
  } else {
5445
- $browser.url($location.absUrl(), $location.$$replace);
5446
- $location.$$replace = false;
5586
+ $browser.url($location.absUrl(), currentReplace);
5447
5587
  afterLocationChange(oldUrl);
5448
5588
  }
5449
5589
  });
5450
5590
  }
5591
+ $location.$$replace = false;
5451
5592
 
5452
5593
  return changeCounter;
5453
5594
  });
@@ -5578,7 +5719,15 @@ var OPERATORS = {
5578
5719
  'true':function(){return true;},
5579
5720
  'false':function(){return false;},
5580
5721
  undefined:noop,
5581
- '+':function(self, locals, a,b){a=a(self, locals); b=b(self, locals); return (isDefined(a)?a:0)+(isDefined(b)?b:0);},
5722
+ '+':function(self, locals, a,b){
5723
+ a=a(self, locals); b=b(self, locals);
5724
+ if (isDefined(a)) {
5725
+ if (isDefined(b)) {
5726
+ return a + b;
5727
+ }
5728
+ return a;
5729
+ }
5730
+ return isDefined(b)?b:undefined;},
5582
5731
  '-':function(self, locals, a,b){a=a(self, locals); b=b(self, locals); return (isDefined(a)?a:0)-(isDefined(b)?b:0);},
5583
5732
  '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);},
5584
5733
  '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);},
@@ -6405,9 +6554,10 @@ function getterFn(path, csp) {
6405
6554
  * @param {string} expression String expression to compile.
6406
6555
  * @returns {function(context, locals)} a function which represents the compiled expression:
6407
6556
  *
6408
- * * `context`: an object against which any expressions embedded in the strings are evaluated
6409
- * against (Topically a scope object).
6410
- * * `locals`: local variables context object, useful for overriding values in `context`.
6557
+ * * `context` – `{object}` – an object against which any expressions embedded in the strings
6558
+ * are evaluated against (tipically a scope object).
6559
+ * * `locals` – `{object=}` – local variables context object, useful for overriding values in
6560
+ * `context`.
6411
6561
  *
6412
6562
  * The return function also has an `assign` property, if the expression is assignable, which
6413
6563
  * allows one to set values to expressions.
@@ -6443,8 +6593,8 @@ function $ParseProvider() {
6443
6593
  * interface for interacting with an object that represents the result of an action that is
6444
6594
  * performed asynchronously, and may or may not be finished at any given point in time.
6445
6595
  *
6446
- * From the perspective of dealing with error handling, deferred and promise apis are to
6447
- * asynchronous programing what `try`, `catch` and `throw` keywords are to synchronous programing.
6596
+ * From the perspective of dealing with error handling, deferred and promise APIs are to
6597
+ * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
6448
6598
  *
6449
6599
  * <pre>
6450
6600
  * // for the purpose of this example let's assume that variables `$q` and `scope` are
@@ -6473,12 +6623,12 @@ function $ParseProvider() {
6473
6623
  * alert('Success: ' + greeting);
6474
6624
  * }, function(reason) {
6475
6625
  * alert('Failed: ' + reason);
6476
- * );
6626
+ * });
6477
6627
  * </pre>
6478
6628
  *
6479
6629
  * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
6480
6630
  * comes in the way of
6481
- * [guarantees that promise and deferred apis make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
6631
+ * [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
6482
6632
  *
6483
6633
  * Additionally the promise api allows for composition that is very hard to do with the
6484
6634
  * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
@@ -6490,7 +6640,7 @@ function $ParseProvider() {
6490
6640
  *
6491
6641
  * A new instance of deferred is constructed by calling `$q.defer()`.
6492
6642
  *
6493
- * The purpose of the deferred object is to expose the associated Promise instance as well as apis
6643
+ * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
6494
6644
  * that can be used for signaling the successful or unsuccessful completion of the task.
6495
6645
  *
6496
6646
  * **Methods**
@@ -6533,7 +6683,7 @@ function $ParseProvider() {
6533
6683
  * return result + 1;
6534
6684
  * });
6535
6685
  *
6536
- * // promiseB will be resolved immediately after promiseA is resolved and it's value will be
6686
+ * // promiseB will be resolved immediately after promiseA is resolved and its value will be
6537
6687
  * // the result of promiseA incremented by 1
6538
6688
  * </pre>
6539
6689
  *
@@ -6554,6 +6704,30 @@ function $ParseProvider() {
6554
6704
  * you can treat promises attached to a scope as if they were the resulting values.
6555
6705
  * - Q has many more features that $q, but that comes at a cost of bytes. $q is tiny, but contains
6556
6706
  * all the important functionality needed for common async tasks.
6707
+ *
6708
+ * # Testing
6709
+ *
6710
+ * <pre>
6711
+ * it('should simulate promise', inject(function($q, $rootScope) {
6712
+ * var deferred = $q.defer();
6713
+ * var promise = deferred.promise;
6714
+ * var resolvedValue;
6715
+ *
6716
+ * promise.then(function(value) { resolvedValue = value; });
6717
+ * expect(resolvedValue).toBeUndefined();
6718
+ *
6719
+ * // Simulate resolving of promise
6720
+ * deferred.resolve(123);
6721
+ * // Note that the 'then' function does not get called synchronously.
6722
+ * // This is because we want the promise API to always be async, whether or not
6723
+ * // it got called synchronously or asynchronously.
6724
+ * expect(resolvedValue).toBeUndefined();
6725
+ *
6726
+ * // Propagate promise resolution to 'then' functions using $apply().
6727
+ * $rootScope.$apply();
6728
+ * expect(resolvedValue).toEqual(123);
6729
+ * });
6730
+ * </pre>
6557
6731
  */
6558
6732
  function $QProvider() {
6559
6733
 
@@ -6719,12 +6893,12 @@ function qFactory(nextTick, exceptionHandler) {
6719
6893
  * @methodOf ng.$q
6720
6894
  * @description
6721
6895
  * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
6722
- * This is useful when you are dealing with on object that might or might not be a promise, or if
6896
+ * This is useful when you are dealing with an object that might or might not be a promise, or if
6723
6897
  * the promise comes from a source that can't be trusted.
6724
6898
  *
6725
6899
  * @param {*} value Value or a promise
6726
6900
  * @returns {Promise} Returns a single promise that will be resolved with an array of values,
6727
- * each value coresponding to the promise at the same index in the `promises` array. If any of
6901
+ * each value corresponding to the promise at the same index in the `promises` array. If any of
6728
6902
  * the promises is resolved with a rejection, this resulting promise will be resolved with the
6729
6903
  * same rejection.
6730
6904
  */
@@ -6786,7 +6960,7 @@ function qFactory(nextTick, exceptionHandler) {
6786
6960
  *
6787
6961
  * @param {Array.<Promise>} promises An array of promises.
6788
6962
  * @returns {Promise} Returns a single promise that will be resolved with an array of values,
6789
- * each value coresponding to the promise at the same index in the `promises` array. If any of
6963
+ * each value corresponding to the promise at the same index in the `promises` array. If any of
6790
6964
  * the promises is resolved with a rejection, this resulting promise will be resolved with the
6791
6965
  * same rejection.
6792
6966
  */
@@ -6840,15 +7014,20 @@ function $RouteProvider(){
6840
7014
  *
6841
7015
  * @param {string} path Route path (matched against `$location.path`). If `$location.path`
6842
7016
  * contains redundant trailing slash or is missing one, the route will still match and the
6843
- * `$location.path` will be updated to add or drop the trailing slash to exacly match the
7017
+ * `$location.path` will be updated to add or drop the trailing slash to exactly match the
6844
7018
  * route definition.
7019
+ *
7020
+ * `path` can contain named groups starting with a colon (`:name`). All characters up to the
7021
+ * next slash are matched and stored in `$routeParams` under the given `name` when the route
7022
+ * matches.
7023
+ *
6845
7024
  * @param {Object} route Mapping information to be assigned to `$route.current` on route
6846
7025
  * match.
6847
7026
  *
6848
7027
  * Object properties:
6849
7028
  *
6850
7029
  * - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly
6851
- * created scope or the name of a {@link angular.Module#controller registered controller}
7030
+ * created scope or the name of a {@link angular.Module#controller registered controller}
6852
7031
  * if passed as a string.
6853
7032
  * - `template` – `{string=}` – html template as a string that should be used by
6854
7033
  * {@link ng.directive:ngView ngView} or
@@ -6859,7 +7038,7 @@ function $RouteProvider(){
6859
7038
  * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
6860
7039
  * be injected into the controller. If any of these dependencies are promises, they will be
6861
7040
  * resolved and converted to a value before the controller is instantiated and the
6862
- * `$afterRouteChange` event is fired. The map object is:
7041
+ * `$routeChangeSuccess` event is fired. The map object is:
6863
7042
  *
6864
7043
  * - `key` – `{string}`: a name of a dependency to be injected into the controller.
6865
7044
  * - `factory` - `{string|function}`: If `string` then it is an alias for a service.
@@ -7078,8 +7257,9 @@ function $RouteProvider(){
7078
7257
  * {@link ng.directive:ngView ngView} listens for the directive
7079
7258
  * to instantiate the controller and render the view.
7080
7259
  *
7260
+ * @param {Object} angularEvent Synthetic event object.
7081
7261
  * @param {Route} current Current route information.
7082
- * @param {Route} previous Previous route information.
7262
+ * @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered.
7083
7263
  */
7084
7264
 
7085
7265
  /**
@@ -7106,8 +7286,7 @@ function $RouteProvider(){
7106
7286
  * instance of the Controller.
7107
7287
  */
7108
7288
 
7109
- var matcher = switchRouteMatcher,
7110
- forceReload = false,
7289
+ var forceReload = false,
7111
7290
  $route = {
7112
7291
  routes: routes,
7113
7292
 
@@ -7135,21 +7314,36 @@ function $RouteProvider(){
7135
7314
 
7136
7315
  /////////////////////////////////////////////////////
7137
7316
 
7317
+ /**
7318
+ * @param on {string} current url
7319
+ * @param when {string} route when template to match the url against
7320
+ * @return {?Object}
7321
+ */
7138
7322
  function switchRouteMatcher(on, when) {
7139
7323
  // TODO(i): this code is convoluted and inefficient, we should construct the route matching
7140
7324
  // regex only once and then reuse it
7141
- var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$',
7325
+
7326
+ // Escape regexp special characters.
7327
+ when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$';
7328
+ var regex = '',
7142
7329
  params = [],
7143
7330
  dst = {};
7144
- forEach(when.split(/\W/), function(param) {
7145
- if (param) {
7146
- var paramRegExp = new RegExp(":" + param + "([\\W])");
7147
- if (regex.match(paramRegExp)) {
7148
- regex = regex.replace(paramRegExp, "([^\\/]*)$1");
7149
- params.push(param);
7150
- }
7151
- }
7152
- });
7331
+
7332
+ var re = /:(\w+)/g,
7333
+ paramMatch,
7334
+ lastMatchedIndex = 0;
7335
+
7336
+ while ((paramMatch = re.exec(when)) !== null) {
7337
+ // Find each :param in `when` and replace it with a capturing group.
7338
+ // Append all other sections of when unchanged.
7339
+ regex += when.slice(lastMatchedIndex, paramMatch.index);
7340
+ regex += '([^\\/]*)';
7341
+ params.push(paramMatch[1]);
7342
+ lastMatchedIndex = re.lastIndex;
7343
+ }
7344
+ // Append trailing path part.
7345
+ regex += when.substr(lastMatchedIndex);
7346
+
7153
7347
  var match = on.match(new RegExp(regex));
7154
7348
  if (match) {
7155
7349
  forEach(params, function(name, index) {
@@ -7163,7 +7357,7 @@ function $RouteProvider(){
7163
7357
  var next = parseRoute(),
7164
7358
  last = $route.current;
7165
7359
 
7166
- if (next && last && next.$route === last.$route
7360
+ if (next && last && next.$$route === last.$$route
7167
7361
  && equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
7168
7362
  last.params = next.params;
7169
7363
  copy(last.params, $routeParams);
@@ -7193,7 +7387,7 @@ function $RouteProvider(){
7193
7387
 
7194
7388
  forEach(next.resolve || {}, function(value, key) {
7195
7389
  keys.push(key);
7196
- values.push(isFunction(value) ? $injector.invoke(value) : $injector.get(value));
7390
+ values.push(isString(value) ? $injector.get(value) : $injector.invoke(value));
7197
7391
  });
7198
7392
  if (isDefined(template = next.template)) {
7199
7393
  } else if (isDefined(template = next.templateUrl)) {
@@ -7238,11 +7432,11 @@ function $RouteProvider(){
7238
7432
  // Match a route
7239
7433
  var params, match;
7240
7434
  forEach(routes, function(route, path) {
7241
- if (!match && (params = matcher($location.path(), path))) {
7435
+ if (!match && (params = switchRouteMatcher($location.path(), path))) {
7242
7436
  match = inherit(route, {
7243
7437
  params: extend({}, $location.search(), params),
7244
7438
  pathParams: params});
7245
- match.$route = route;
7439
+ match.$$route = route;
7246
7440
  }
7247
7441
  });
7248
7442
  // No route matched; fallback to "otherwise" route
@@ -7390,7 +7584,7 @@ function $RootScopeProvider(){
7390
7584
  expect(scope.greeting).toEqual(undefined);
7391
7585
 
7392
7586
  scope.$watch('name', function() {
7393
- this.greeting = this.salutation + ' ' + this.name + '!';
7587
+ scope.greeting = scope.salutation + ' ' + scope.name + '!';
7394
7588
  }); // initialize the watch
7395
7589
 
7396
7590
  expect(scope.greeting).toEqual(undefined);
@@ -7433,8 +7627,10 @@ function $RootScopeProvider(){
7433
7627
  this.$$nextSibling = this.$$prevSibling =
7434
7628
  this.$$childHead = this.$$childTail = null;
7435
7629
  this['this'] = this.$root = this;
7630
+ this.$$destroyed = false;
7436
7631
  this.$$asyncQueue = [];
7437
7632
  this.$$listeners = {};
7633
+ this.$$isolateBindings = {};
7438
7634
  }
7439
7635
 
7440
7636
  /**
@@ -7464,9 +7660,9 @@ function $RootScopeProvider(){
7464
7660
  * the scope and its child scopes to be permanently detached from the parent and thus stop
7465
7661
  * participating in model change detection and listener notification by invoking.
7466
7662
  *
7467
- * @param {boolean} isolate if true then the scoped does not prototypically inherit from the
7468
- * parent scope. The scope is isolated, as it can not se parent scope properties.
7469
- * When creating widgets it is useful for the widget to not accidently read parent
7663
+ * @param {boolean} isolate if true then the scope does not prototypically inherit from the
7664
+ * parent scope. The scope is isolated, as it can not see parent scope properties.
7665
+ * When creating widgets it is useful for the widget to not accidentally read parent
7470
7666
  * state.
7471
7667
  *
7472
7668
  * @returns {Object} The newly created child scope.
@@ -7520,18 +7716,18 @@ function $RootScopeProvider(){
7520
7716
  * reruns when it detects changes the `watchExpression` can execute multiple times per
7521
7717
  * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
7522
7718
  * - The `listener` is called only when the value from the current `watchExpression` and the
7523
- * previous call to `watchExpression' are not equal (with the exception of the initial run
7719
+ * previous call to `watchExpression` are not equal (with the exception of the initial run,
7524
7720
  * see below). The inequality is determined according to
7525
- * {@link angular.equals} function. To save the value of the object for later comparison
7721
+ * {@link angular.equals} function. To save the value of the object for later comparison, the
7526
7722
  * {@link angular.copy} function is used. It also means that watching complex options will
7527
7723
  * have adverse memory and performance implications.
7528
7724
  * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This
7529
7725
  * is achieved by rerunning the watchers until no changes are detected. The rerun iteration
7530
- * limit is 100 to prevent infinity loop deadlock.
7726
+ * limit is 10 to prevent an infinite loop deadlock.
7531
7727
  *
7532
7728
  *
7533
7729
  * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
7534
- * you can register an `watchExpression` function with no `listener`. (Since `watchExpression`,
7730
+ * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
7535
7731
  * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a change is
7536
7732
  * detected, be prepared for multiple calls to your listener.)
7537
7733
  *
@@ -7551,7 +7747,7 @@ function $RootScopeProvider(){
7551
7747
  scope.counter = 0;
7552
7748
 
7553
7749
  expect(scope.counter).toEqual(0);
7554
- scope.$watch('name', function(newValue, oldValue) { counter = counter + 1; });
7750
+ scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; });
7555
7751
  expect(scope.counter).toEqual(0);
7556
7752
 
7557
7753
  scope.$digest();
@@ -7577,7 +7773,7 @@ function $RootScopeProvider(){
7577
7773
  * - `string`: Evaluated as {@link guide/expression expression}
7578
7774
  * - `function(newValue, oldValue, scope)`: called with current and previous values as parameters.
7579
7775
  *
7580
- * @param {boolean=} objectEquality Compare object for equality rather then for refference.
7776
+ * @param {boolean=} objectEquality Compare object for equality rather than for reference.
7581
7777
  * @returns {function()} Returns a deregistration function for this listener.
7582
7778
  */
7583
7779
  $watch: function(watchExp, listener, objectEquality) {
@@ -7644,7 +7840,7 @@ function $RootScopeProvider(){
7644
7840
 
7645
7841
  expect(scope.counter).toEqual(0);
7646
7842
  scope.$watch('name', function(newValue, oldValue) {
7647
- counter = counter + 1;
7843
+ scope.counter = scope.counter + 1;
7648
7844
  });
7649
7845
  expect(scope.counter).toEqual(0);
7650
7846
 
@@ -7752,7 +7948,7 @@ function $RootScopeProvider(){
7752
7948
  * @function
7753
7949
  *
7754
7950
  * @description
7755
- * Remove the current scope (and all of its children) from the parent scope. Removal implies
7951
+ * Removes the current scope (and all of its children) from the parent scope. Removal implies
7756
7952
  * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
7757
7953
  * propagate to the current scope and its children. Removal also implies that the current
7758
7954
  * scope is eligible for garbage collection.
@@ -7766,15 +7962,22 @@ function $RootScopeProvider(){
7766
7962
  * perform any necessary cleanup.
7767
7963
  */
7768
7964
  $destroy: function() {
7769
- if ($rootScope == this) return; // we can't remove the root node;
7965
+ // we can't destroy the root scope or a scope that has been already destroyed
7966
+ if ($rootScope == this || this.$$destroyed) return;
7770
7967
  var parent = this.$parent;
7771
7968
 
7772
7969
  this.$broadcast('$destroy');
7970
+ this.$$destroyed = true;
7773
7971
 
7774
7972
  if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
7775
7973
  if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
7776
7974
  if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
7777
7975
  if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
7976
+
7977
+ // This is bogus code that works around Chrome's GC leak
7978
+ // see: https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
7979
+ this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
7980
+ this.$$childTail = null;
7778
7981
  },
7779
7982
 
7780
7983
  /**
@@ -7785,7 +7988,7 @@ function $RootScopeProvider(){
7785
7988
  *
7786
7989
  * @description
7787
7990
  * Executes the `expression` on the current scope returning the result. Any exceptions in the
7788
- * expression are propagated (uncaught). This is useful when evaluating engular expressions.
7991
+ * expression are propagated (uncaught). This is useful when evaluating Angular expressions.
7789
7992
  *
7790
7993
  * # Example
7791
7994
  * <pre>
@@ -7906,23 +8109,23 @@ function $RootScopeProvider(){
7906
8109
  * @function
7907
8110
  *
7908
8111
  * @description
7909
- * Listen on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of
8112
+ * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of
7910
8113
  * event life cycle.
7911
8114
  *
7912
- * @param {string} name Event name to listen on.
7913
- * @param {function(event)} listener Function to call when the event is emitted.
7914
- * @returns {function()} Returns a deregistration function for this listener.
7915
- *
7916
8115
  * The event listener function format is: `function(event, args...)`. The `event` object
7917
8116
  * passed into the listener has the following attributes:
7918
8117
  *
7919
- * - `targetScope` - {Scope}: the scope on which the event was `$emit`-ed or `$broadcast`-ed.
7920
- * - `currentScope` - {Scope}: the current scope which is handling the event.
7921
- * - `name` - {string}: Name of the event.
7922
- * - `stopPropagation` - {function=}: calling `stopPropagation` function will cancel further event propagation
7923
- * (available only for events that were `$emit`-ed).
7924
- * - `preventDefault` - {function}: calling `preventDefault` sets `defaultPrevented` flag to true.
7925
- * - `defaultPrevented` - {boolean}: true if `preventDefault` was called.
8118
+ * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or `$broadcast`-ed.
8119
+ * - `currentScope` - `{Scope}`: the current scope which is handling the event.
8120
+ * - `name` - `{string}`: Name of the event.
8121
+ * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel further event
8122
+ * propagation (available only for events that were `$emit`-ed).
8123
+ * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true.
8124
+ * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
8125
+ *
8126
+ * @param {string} name Event name to listen on.
8127
+ * @param {function(event, args...)} listener Function to call when the event is emitted.
8128
+ * @returns {function()} Returns a deregistration function for this listener.
7926
8129
  */
7927
8130
  $on: function(name, listener) {
7928
8131
  var namedListeners = this.$$listeners[name];
@@ -7932,7 +8135,7 @@ function $RootScopeProvider(){
7932
8135
  namedListeners.push(listener);
7933
8136
 
7934
8137
  return function() {
7935
- arrayRemove(namedListeners, listener);
8138
+ namedListeners[indexOf(namedListeners, listener)] = null;
7936
8139
  };
7937
8140
  },
7938
8141
 
@@ -7980,6 +8183,14 @@ function $RootScopeProvider(){
7980
8183
  namedListeners = scope.$$listeners[name] || empty;
7981
8184
  event.currentScope = scope;
7982
8185
  for (i=0, length=namedListeners.length; i<length; i++) {
8186
+
8187
+ // if listeners were deregistered, defragment the array
8188
+ if (!namedListeners[i]) {
8189
+ namedListeners.splice(i, 1);
8190
+ i--;
8191
+ length--;
8192
+ continue;
8193
+ }
7983
8194
  try {
7984
8195
  namedListeners[i].apply(null, listenerArgs);
7985
8196
  if (stopPropagation) return event;
@@ -8029,19 +8240,29 @@ function $RootScopeProvider(){
8029
8240
  },
8030
8241
  defaultPrevented: false
8031
8242
  },
8032
- listenerArgs = concat([event], arguments, 1);
8243
+ listenerArgs = concat([event], arguments, 1),
8244
+ listeners, i, length;
8033
8245
 
8034
8246
  //down while you can, then up and next sibling or up and next sibling until back at root
8035
8247
  do {
8036
8248
  current = next;
8037
8249
  event.currentScope = current;
8038
- forEach(current.$$listeners[name], function(listener) {
8250
+ listeners = current.$$listeners[name] || [];
8251
+ for (i=0, length = listeners.length; i<length; i++) {
8252
+ // if listeners were deregistered, defragment the array
8253
+ if (!listeners[i]) {
8254
+ listeners.splice(i, 1);
8255
+ i--;
8256
+ length--;
8257
+ continue;
8258
+ }
8259
+
8039
8260
  try {
8040
- listener.apply(null, listenerArgs);
8261
+ listeners[i].apply(null, listenerArgs);
8041
8262
  } catch(e) {
8042
8263
  $exceptionHandler(e);
8043
8264
  }
8044
- });
8265
+ }
8045
8266
 
8046
8267
  // Insanity Warning: scope depth-first traversal
8047
8268
  // yes, this code is a bit crazy, but it works and we have tests to prove it!
@@ -8082,7 +8303,7 @@ function $RootScopeProvider(){
8082
8303
 
8083
8304
  /**
8084
8305
  * function used as an initial value for watchers.
8085
- * because it's uniqueue we can easily tell it apart from other values
8306
+ * because it's unique we can easily tell it apart from other values
8086
8307
  */
8087
8308
  function initWatchVal() {}
8088
8309
  }];
@@ -8296,7 +8517,7 @@ function $HttpProvider() {
8296
8517
  /**
8297
8518
  * @ngdoc function
8298
8519
  * @name ng.$http
8299
- * @requires $httpBacked
8520
+ * @requires $httpBackend
8300
8521
  * @requires $browser
8301
8522
  * @requires $cacheFactory
8302
8523
  * @requires $rootScope
@@ -8332,8 +8553,7 @@ function $HttpProvider() {
8332
8553
  * }).
8333
8554
  * error(function(data, status, headers, config) {
8334
8555
  * // called asynchronously if an error occurs
8335
- * // or server returns response with status
8336
- * // code outside of the <200, 400) range
8556
+ * // or server returns response with an error status.
8337
8557
  * });
8338
8558
  * </pre>
8339
8559
  *
@@ -8342,6 +8562,10 @@ function $HttpProvider() {
8342
8562
  * an object representing the response. See the api signature and type info below for more
8343
8563
  * details.
8344
8564
  *
8565
+ * A response status code that falls in the [200, 300) range is considered a success status and
8566
+ * will result in the success callback being called. Note that if the response is a redirect,
8567
+ * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
8568
+ * called for such responses.
8345
8569
  *
8346
8570
  * # Shortcut methods
8347
8571
  *
@@ -8402,10 +8626,14 @@ function $HttpProvider() {
8402
8626
  * - if XSRF prefix is detected, strip it (see Security Considerations section below)
8403
8627
  * - if json response is detected, deserialize it using a JSON parser
8404
8628
  *
8405
- * To override these transformation locally, specify transform functions as `transformRequest`
8406
- * and/or `transformResponse` properties of the config object. To globally override the default
8407
- * transforms, override the `$httpProvider.defaults.transformRequest` and
8408
- * `$httpProvider.defaults.transformResponse` properties of the `$httpProvider`.
8629
+ * To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and
8630
+ * `$httpProvider.defaults.transformResponse` properties of the `$httpProvider`. These properties are by default an
8631
+ * array of transform functions, which allows you to `push` or `unshift` a new transformation function into the
8632
+ * transformation chain. You can also decide to completely override any default transformations by assigning your
8633
+ * transformation functions to these properties directly without the array wrapper.
8634
+ *
8635
+ * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or
8636
+ * `transformResponse` properties of the config object passed into `$http`.
8409
8637
  *
8410
8638
  *
8411
8639
  * # Caching
@@ -8923,6 +9151,7 @@ function $HttpProvider() {
8923
9151
 
8924
9152
  }];
8925
9153
  }
9154
+
8926
9155
  var XHR = window.XMLHttpRequest || function() {
8927
9156
  try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
8928
9157
  try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
@@ -8990,8 +9219,30 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
8990
9219
  // always async
8991
9220
  xhr.onreadystatechange = function() {
8992
9221
  if (xhr.readyState == 4) {
8993
- completeRequest(
8994
- callback, status || xhr.status, xhr.responseText, xhr.getAllResponseHeaders());
9222
+ var responseHeaders = xhr.getAllResponseHeaders();
9223
+
9224
+ // TODO(vojta): remove once Firefox 21 gets released.
9225
+ // begin: workaround to overcome Firefox CORS http response headers bug
9226
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=608735
9227
+ // Firefox already patched in nightly. Should land in Firefox 21.
9228
+
9229
+ // CORS "simple response headers" http://www.w3.org/TR/cors/
9230
+ var value,
9231
+ simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type",
9232
+ "Expires", "Last-Modified", "Pragma"];
9233
+ if (!responseHeaders) {
9234
+ responseHeaders = "";
9235
+ forEach(simpleHeaders, function (header) {
9236
+ var value = xhr.getResponseHeader(header);
9237
+ if (value) {
9238
+ responseHeaders += header + ": " + value + "\n";
9239
+ }
9240
+ });
9241
+ }
9242
+ // end of the workaround.
9243
+
9244
+ completeRequest(callback, status || xhr.status, xhr.responseText,
9245
+ responseHeaders);
8995
9246
  }
8996
9247
  };
8997
9248
 
@@ -9149,7 +9400,7 @@ function $TimeoutProvider() {
9149
9400
  * @param {number=} [delay=0] Delay in milliseconds.
9150
9401
  * @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise
9151
9402
  * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
9152
- * @returns {*} Promise that will be resolved when the timeout is reached. The value this
9403
+ * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
9153
9404
  * promise will be resolved with is the return value of the `fn` function.
9154
9405
  */
9155
9406
  function timeout(fn, delay, invokeApply) {
@@ -9213,7 +9464,7 @@ function $TimeoutProvider() {
9213
9464
  *
9214
9465
  * Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To
9215
9466
  * achieve this a filter definition consists of a factory function which is annotated with dependencies and is
9216
- * responsible for creating a the filter function.
9467
+ * responsible for creating a filter function.
9217
9468
  *
9218
9469
  * <pre>
9219
9470
  * // Filter registration
@@ -9351,22 +9602,22 @@ function $FilterProvider($provide) {
9351
9602
 
9352
9603
  Search: <input ng-model="searchText">
9353
9604
  <table id="searchTextResults">
9354
- <tr><th>Name</th><th>Phone</th><tr>
9605
+ <tr><th>Name</th><th>Phone</th></tr>
9355
9606
  <tr ng-repeat="friend in friends | filter:searchText">
9356
9607
  <td>{{friend.name}}</td>
9357
9608
  <td>{{friend.phone}}</td>
9358
- <tr>
9609
+ </tr>
9359
9610
  </table>
9360
9611
  <hr>
9361
9612
  Any: <input ng-model="search.$"> <br>
9362
9613
  Name only <input ng-model="search.name"><br>
9363
9614
  Phone only <input ng-model="search.phone"å><br>
9364
9615
  <table id="searchObjResults">
9365
- <tr><th>Name</th><th>Phone</th><tr>
9616
+ <tr><th>Name</th><th>Phone</th></tr>
9366
9617
  <tr ng-repeat="friend in friends | filter:search">
9367
9618
  <td>{{friend.name}}</td>
9368
9619
  <td>{{friend.phone}}</td>
9369
- <tr>
9620
+ </tr>
9370
9621
  </table>
9371
9622
  </doc:source>
9372
9623
  <doc:scenario>
@@ -9390,7 +9641,7 @@ function $FilterProvider($provide) {
9390
9641
  */
9391
9642
  function filterFilter() {
9392
9643
  return function(array, expression) {
9393
- if (!(array instanceof Array)) return array;
9644
+ if (!isArray(array)) return array;
9394
9645
  var predicates = [];
9395
9646
  predicates.check = function(value) {
9396
9647
  for (var j = 0; j < predicates.length; j++) {
@@ -9588,9 +9839,18 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
9588
9839
  formatedText = '',
9589
9840
  parts = [];
9590
9841
 
9842
+ var hasExponent = false;
9591
9843
  if (numStr.indexOf('e') !== -1) {
9592
- formatedText = numStr;
9593
- } else {
9844
+ var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
9845
+ if (match && match[2] == '-' && match[3] > fractionSize + 1) {
9846
+ numStr = '0';
9847
+ } else {
9848
+ formatedText = numStr;
9849
+ hasExponent = true;
9850
+ }
9851
+ }
9852
+
9853
+ if (!hasExponent) {
9594
9854
  var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
9595
9855
 
9596
9856
  // determine fractionSize if it is not specified
@@ -9630,7 +9890,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
9630
9890
  fraction += '0';
9631
9891
  }
9632
9892
 
9633
- if (fractionSize) formatedText += decimalSep + fraction.substr(0, fractionSize);
9893
+ if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
9634
9894
  }
9635
9895
 
9636
9896
  parts.push(isNegative ? pattern.negPre : pattern.posPre);
@@ -9673,8 +9933,13 @@ function dateStrGetter(name, shortForm) {
9673
9933
  }
9674
9934
 
9675
9935
  function timeZoneGetter(date) {
9676
- var offset = date.getTimezoneOffset();
9677
- return padNumber(offset / 60, 2) + padNumber(Math.abs(offset % 60), 2);
9936
+ var zone = -1 * date.getTimezoneOffset();
9937
+ var paddedZone = (zone >= 0) ? "+" : "";
9938
+
9939
+ paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
9940
+ padNumber(Math.abs(zone % 60), 2);
9941
+
9942
+ return paddedZone;
9678
9943
  }
9679
9944
 
9680
9945
  function ampmGetter(date, formats) {
@@ -9738,7 +10003,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
9738
10003
  * * `'ss'`: Second in minute, padded (00-59)
9739
10004
  * * `'s'`: Second in minute (0-59)
9740
10005
  * * `'a'`: am/pm marker
9741
- * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-1200)
10006
+ * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
9742
10007
  *
9743
10008
  * `format` string can also be one of the following predefined
9744
10009
  * {@link guide/i18n localizable formats}:
@@ -9760,7 +10025,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
9760
10025
  *
9761
10026
  * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
9762
10027
  * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's
9763
- * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ).
10028
+ * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
10029
+ * specified in the string input, the time is considered to be in the local timezone.
9764
10030
  * @param {string=} format Formatting rules (see Description). If not specified,
9765
10031
  * `mediumDate` is used.
9766
10032
  * @returns {string} Formatted string or the input if input is not recognized as date/millis.
@@ -9780,7 +10046,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
9780
10046
  expect(binding("1288323623006 | date:'medium'")).
9781
10047
  toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
9782
10048
  expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
9783
- toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} \-?\d{4}/);
10049
+ toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
9784
10050
  expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
9785
10051
  toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
9786
10052
  });
@@ -9791,7 +10057,7 @@ dateFilter.$inject = ['$locale'];
9791
10057
  function dateFilter($locale) {
9792
10058
 
9793
10059
 
9794
- var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
10060
+ var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
9795
10061
  function jsonStringToDate(string){
9796
10062
  var match;
9797
10063
  if (match = string.match(R_ISO8601_STR)) {
@@ -10049,12 +10315,12 @@ function limitToFilter(){
10049
10315
  (<a href ng-click="predicate = '-name'; reverse=false">^</a>)</th>
10050
10316
  <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
10051
10317
  <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
10052
- <tr>
10318
+ </tr>
10053
10319
  <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
10054
10320
  <td>{{friend.name}}</td>
10055
10321
  <td>{{friend.phone}}</td>
10056
10322
  <td>{{friend.age}}</td>
10057
- <tr>
10323
+ </tr>
10058
10324
  </table>
10059
10325
  </div>
10060
10326
  </doc:source>
@@ -10086,7 +10352,7 @@ function limitToFilter(){
10086
10352
  orderByFilter.$inject = ['$parse'];
10087
10353
  function orderByFilter($parse){
10088
10354
  return function(array, sortPredicate, reverseOrder) {
10089
- if (!(array instanceof Array)) return array;
10355
+ if (!isArray(array)) return array;
10090
10356
  if (!sortPredicate) return array;
10091
10357
  sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
10092
10358
  sortPredicate = map(sortPredicate, function(predicate){
@@ -10154,15 +10420,25 @@ function ngDirective(directive) {
10154
10420
  *
10155
10421
  * The reasoning for this change is to allow easy creation of action links with `ngClick` directive
10156
10422
  * without changing the location or causing page reloads, e.g.:
10157
- * <a href="" ng-click="model.$save()">Save</a>
10423
+ * `<a href="" ng-click="model.$save()">Save</a>`
10158
10424
  */
10159
10425
  var htmlAnchorDirective = valueFn({
10160
10426
  restrict: 'E',
10161
10427
  compile: function(element, attr) {
10162
- // turn <a href ng-click="..">link</a> into a link in IE
10163
- // but only if it doesn't have name attribute, in which case it's an anchor
10164
- if (!attr.href) {
10165
- attr.$set('href', '');
10428
+
10429
+ if (msie <= 8) {
10430
+
10431
+ // turn <a href ng-click="..">link</a> into a stylable link in IE
10432
+ // but only if it doesn't have name attribute, in which case it's an anchor
10433
+ if (!attr.href && !attr.name) {
10434
+ attr.$set('href', '');
10435
+ }
10436
+
10437
+ // add a comment node to anchors to workaround IE bug that causes element content to be reset
10438
+ // to new attribute content if attribute is updated with value containing @ and element also
10439
+ // contains value with @
10440
+ // see issue #1949
10441
+ element.append(document.createComment('IE fix'));
10166
10442
  }
10167
10443
 
10168
10444
  return function(scope, element) {
@@ -10242,7 +10518,7 @@ var htmlAnchorDirective = valueFn({
10242
10518
  it('should execute ng-click but not reload when no href but name specified', function() {
10243
10519
  element('#link-5').click();
10244
10520
  expect(input('value').val()).toEqual('5');
10245
- expect(element('#link-5').attr('href')).toBe('');
10521
+ expect(element('#link-5').attr('href')).toBe(undefined);
10246
10522
  });
10247
10523
 
10248
10524
  it('should only change url when only ng-href', function() {
@@ -10460,7 +10736,7 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) {
10460
10736
  priority: 100,
10461
10737
  compile: function() {
10462
10738
  return function(scope, element, attr) {
10463
- scope.$watch(attr[normalized], function(value) {
10739
+ scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
10464
10740
  attr.$set(attrName, !!value);
10465
10741
  });
10466
10742
  };
@@ -10478,12 +10754,16 @@ forEach(['src', 'href'], function(attrName) {
10478
10754
  priority: 99, // it needs to run after the attributes are interpolated
10479
10755
  link: function(scope, element, attr) {
10480
10756
  attr.$observe(normalized, function(value) {
10757
+ if (!value)
10758
+ return;
10759
+
10481
10760
  attr.$set(attrName, value);
10482
10761
 
10483
10762
  // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
10484
10763
  // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
10485
- // to set the property as well to achieve the desired effect
10486
- if (msie) element.prop(attrName, value);
10764
+ // to set the property as well to achieve the desired effect.
10765
+ // we use attr[attrName] value since $set can sanitize the url.
10766
+ if (msie) element.prop(attrName, attr[attrName]);
10487
10767
  });
10488
10768
  }
10489
10769
  };
@@ -10503,13 +10783,13 @@ var nullFormCtrl = {
10503
10783
  *
10504
10784
  * @property {boolean} $pristine True if user has not interacted with the form yet.
10505
10785
  * @property {boolean} $dirty True if user has already interacted with the form.
10506
- * @property {boolean} $valid True if all of the containg forms and controls are valid.
10786
+ * @property {boolean} $valid True if all of the containing forms and controls are valid.
10507
10787
  * @property {boolean} $invalid True if at least one containing control or form is invalid.
10508
10788
  *
10509
10789
  * @property {Object} $error Is an object hash, containing references to all invalid controls or
10510
10790
  * forms, where:
10511
10791
  *
10512
- * - keys are validation tokens (error names) — such as `REQUIRED`, `URL` or `EMAIL`),
10792
+ * - keys are validation tokens (error names) — such as `required`, `url` or `email`),
10513
10793
  * - values are arrays of controls or forms that are invalid with given error.
10514
10794
  *
10515
10795
  * @description
@@ -10606,6 +10886,7 @@ function FormController(element, attrs) {
10606
10886
  element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS);
10607
10887
  form.$dirty = true;
10608
10888
  form.$pristine = false;
10889
+ parentForm.$setDirty();
10609
10890
  };
10610
10891
 
10611
10892
  }
@@ -10621,7 +10902,7 @@ function FormController(element, attrs) {
10621
10902
  * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
10622
10903
  * sub-group of controls needs to be determined.
10623
10904
  *
10624
- * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
10905
+ * @param {string=} name|ngForm Name of the form. If specified, the form controller will be published into
10625
10906
  * related scope, under this name.
10626
10907
  *
10627
10908
  */
@@ -10694,12 +10975,12 @@ function FormController(element, attrs) {
10694
10975
  </script>
10695
10976
  <form name="myForm" ng-controller="Ctrl">
10696
10977
  userType: <input name="input" ng-model="userType" required>
10697
- <span class="error" ng-show="myForm.input.$error.REQUIRED">Required!</span><br>
10978
+ <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
10698
10979
  <tt>userType = {{userType}}</tt><br>
10699
10980
  <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
10700
10981
  <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
10701
10982
  <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
10702
- <tt>myForm.$error.REQUIRED = {{!!myForm.$error.REQUIRED}}</tt><br>
10983
+ <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
10703
10984
  </form>
10704
10985
  </doc:source>
10705
10986
  <doc:scenario>
@@ -10791,7 +11072,10 @@ var inputType = {
10791
11072
  *
10792
11073
  * @param {string} ngModel Assignable angular expression to data-bind to.
10793
11074
  * @param {string=} name Property name of the form under which the control is published.
10794
- * @param {string=} required Sets `required` validation error key if the value is not entered.
11075
+ * @param {string=} required Adds `required` validation error key if the value is not entered.
11076
+ * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
11077
+ * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
11078
+ * `required` when you want to data-bind to the `required` attribute.
10795
11079
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
10796
11080
  * minlength.
10797
11081
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
@@ -10861,6 +11145,9 @@ var inputType = {
10861
11145
  * @param {string=} min Sets the `min` validation error key if the value entered is less then `min`.
10862
11146
  * @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`.
10863
11147
  * @param {string=} required Sets `required` validation error key if the value is not entered.
11148
+ * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
11149
+ * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
11150
+ * `required` when you want to data-bind to the `required` attribute.
10864
11151
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
10865
11152
  * minlength.
10866
11153
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
@@ -10927,6 +11214,9 @@ var inputType = {
10927
11214
  * @param {string} ngModel Assignable angular expression to data-bind to.
10928
11215
  * @param {string=} name Property name of the form under which the control is published.
10929
11216
  * @param {string=} required Sets `required` validation error key if the value is not entered.
11217
+ * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
11218
+ * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
11219
+ * `required` when you want to data-bind to the `required` attribute.
10930
11220
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
10931
11221
  * minlength.
10932
11222
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
@@ -10992,6 +11282,9 @@ var inputType = {
10992
11282
  * @param {string} ngModel Assignable angular expression to data-bind to.
10993
11283
  * @param {string=} name Property name of the form under which the control is published.
10994
11284
  * @param {string=} required Sets `required` validation error key if the value is not entered.
11285
+ * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
11286
+ * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
11287
+ * `required` when you want to data-bind to the `required` attribute.
10995
11288
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
10996
11289
  * minlength.
10997
11290
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
@@ -11414,6 +11707,9 @@ function checkboxInputType(scope, element, attr, ctrl) {
11414
11707
  * @param {string} ngModel Assignable angular expression to data-bind to.
11415
11708
  * @param {string=} name Property name of the form under which the control is published.
11416
11709
  * @param {string=} required Sets `required` validation error key if the value is not entered.
11710
+ * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
11711
+ * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
11712
+ * `required` when you want to data-bind to the `required` attribute.
11417
11713
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
11418
11714
  * minlength.
11419
11715
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
@@ -11438,6 +11734,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
11438
11734
  * @param {string} ngModel Assignable angular expression to data-bind to.
11439
11735
  * @param {string=} name Property name of the form under which the control is published.
11440
11736
  * @param {string=} required Sets `required` validation error key if the value is not entered.
11737
+ * @param {boolean=} ngRequired Sets `required` attribute if set to true
11441
11738
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
11442
11739
  * minlength.
11443
11740
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
@@ -11771,22 +12068,25 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
11771
12068
 
11772
12069
  // model -> value
11773
12070
  var ctrl = this;
11774
- $scope.$watch(ngModelGet, function(value) {
11775
12071
 
11776
- // ignore change from view
11777
- if (ctrl.$modelValue === value) return;
12072
+ $scope.$watch(function ngModelWatch() {
12073
+ var value = ngModelGet($scope);
11778
12074
 
11779
- var formatters = ctrl.$formatters,
11780
- idx = formatters.length;
12075
+ // if scope model value and ngModel value are out of sync
12076
+ if (ctrl.$modelValue !== value) {
11781
12077
 
11782
- ctrl.$modelValue = value;
11783
- while(idx--) {
11784
- value = formatters[idx](value);
11785
- }
12078
+ var formatters = ctrl.$formatters,
12079
+ idx = formatters.length;
11786
12080
 
11787
- if (ctrl.$viewValue !== value) {
11788
- ctrl.$viewValue = value;
11789
- ctrl.$render();
12081
+ ctrl.$modelValue = value;
12082
+ while(idx--) {
12083
+ value = formatters[idx](value);
12084
+ }
12085
+
12086
+ if (ctrl.$viewValue !== value) {
12087
+ ctrl.$viewValue = value;
12088
+ ctrl.$render();
12089
+ }
11790
12090
  }
11791
12091
  });
11792
12092
  }];
@@ -11935,7 +12235,7 @@ var requiredDirective = function() {
11935
12235
  * @name ng.directive:ngList
11936
12236
  *
11937
12237
  * @description
11938
- * Text input that converts between comma-seperated string into an array of strings.
12238
+ * Text input that converts between comma-separated string into an array of strings.
11939
12239
  *
11940
12240
  * @element input
11941
12241
  * @param {string=} ngList optional delimiter that should be used to split the value. If
@@ -12018,7 +12318,7 @@ var ngValueDirective = function() {
12018
12318
  };
12019
12319
  } else {
12020
12320
  return function(scope, elm, attr) {
12021
- scope.$watch(attr.ngValue, function(value) {
12321
+ scope.$watch(attr.ngValue, function valueWatchAction(value) {
12022
12322
  attr.$set('value', value, false);
12023
12323
  });
12024
12324
  };
@@ -12076,7 +12376,7 @@ var ngValueDirective = function() {
12076
12376
  */
12077
12377
  var ngBindDirective = ngDirective(function(scope, element, attr) {
12078
12378
  element.addClass('ng-binding').data('$binding', attr.ngBind);
12079
- scope.$watch(attr.ngBind, function(value) {
12379
+ scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
12080
12380
  element.text(value == undefined ? '' : value);
12081
12381
  });
12082
12382
  });
@@ -12159,7 +12459,7 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
12159
12459
  var ngBindHtmlUnsafeDirective = [function() {
12160
12460
  return function(scope, element, attr) {
12161
12461
  element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe);
12162
- scope.$watch(attr.ngBindHtmlUnsafe, function(value) {
12462
+ scope.$watch(attr.ngBindHtmlUnsafe, function ngBindHtmlUnsafeWatchAction(value) {
12163
12463
  element.html(value || '');
12164
12464
  });
12165
12465
  };
@@ -12168,17 +12468,57 @@ var ngBindHtmlUnsafeDirective = [function() {
12168
12468
  function classDirective(name, selector) {
12169
12469
  name = 'ngClass' + name;
12170
12470
  return ngDirective(function(scope, element, attr) {
12171
- scope.$watch(attr[name], function(newVal, oldVal) {
12471
+ var oldVal = undefined;
12472
+
12473
+ scope.$watch(attr[name], ngClassWatchAction, true);
12474
+
12475
+ attr.$observe('class', function(value) {
12476
+ var ngClass = scope.$eval(attr[name]);
12477
+ ngClassWatchAction(ngClass, ngClass);
12478
+ });
12479
+
12480
+
12481
+ if (name !== 'ngClass') {
12482
+ scope.$watch('$index', function($index, old$index) {
12483
+ var mod = $index % 2;
12484
+ if (mod !== old$index % 2) {
12485
+ if (mod == selector) {
12486
+ addClass(scope.$eval(attr[name]));
12487
+ } else {
12488
+ removeClass(scope.$eval(attr[name]));
12489
+ }
12490
+ }
12491
+ });
12492
+ }
12493
+
12494
+
12495
+ function ngClassWatchAction(newVal) {
12172
12496
  if (selector === true || scope.$index % 2 === selector) {
12173
12497
  if (oldVal && (newVal !== oldVal)) {
12174
- if (isObject(oldVal) && !isArray(oldVal))
12175
- oldVal = map(oldVal, function(v, k) { if (v) return k });
12176
- element.removeClass(isArray(oldVal) ? oldVal.join(' ') : oldVal);
12177
- }
12178
- if (isObject(newVal) && !isArray(newVal))
12179
- newVal = map(newVal, function(v, k) { if (v) return k });
12180
- if (newVal) element.addClass(isArray(newVal) ? newVal.join(' ') : newVal); }
12181
- }, true);
12498
+ removeClass(oldVal);
12499
+ }
12500
+ addClass(newVal);
12501
+ }
12502
+ oldVal = newVal;
12503
+ }
12504
+
12505
+
12506
+ function removeClass(classVal) {
12507
+ if (isObject(classVal) && !isArray(classVal)) {
12508
+ classVal = map(classVal, function(v, k) { if (v) return k });
12509
+ }
12510
+ element.removeClass(isArray(classVal) ? classVal.join(' ') : classVal);
12511
+ }
12512
+
12513
+
12514
+ function addClass(classVal) {
12515
+ if (isObject(classVal) && !isArray(classVal)) {
12516
+ classVal = map(classVal, function(v, k) { if (v) return k });
12517
+ }
12518
+ if (classVal) {
12519
+ element.addClass(isArray(classVal) ? classVal.join(' ') : classVal);
12520
+ }
12521
+ }
12182
12522
  });
12183
12523
  }
12184
12524
 
@@ -12192,7 +12532,7 @@ function classDirective(name, selector) {
12192
12532
  *
12193
12533
  * The directive won't add duplicate classes if a particular class was already set.
12194
12534
  *
12195
- * When the expression changes, the previously added classes are removed and only then the classes
12535
+ * When the expression changes, the previously added classes are removed and only then the
12196
12536
  * new classes are added.
12197
12537
  *
12198
12538
  * @element ANY
@@ -12343,7 +12683,7 @@ var ngClassEvenDirective = classDirective('Even', 1);
12343
12683
  * `angular.min.js` files. Following is the css rule:
12344
12684
  *
12345
12685
  * <pre>
12346
- * [ng\:cloak], [ng-cloak], .ng-cloak {
12686
+ * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
12347
12687
  * display: none;
12348
12688
  * }
12349
12689
  * </pre>
@@ -12837,7 +13177,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
12837
13177
  element.html('');
12838
13178
  };
12839
13179
 
12840
- scope.$watch(srcExp, function(src) {
13180
+ scope.$watch(srcExp, function ngIncludeWatchAction(src) {
12841
13181
  var thisChangeId = ++changeCounter;
12842
13182
 
12843
13183
  if (src) {
@@ -13122,7 +13462,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
13122
13462
  offset + endSymbol));
13123
13463
  });
13124
13464
 
13125
- scope.$watch(function() {
13465
+ scope.$watch(function ngPluralizeWatch() {
13126
13466
  var value = parseFloat(scope.$eval(numberExp));
13127
13467
 
13128
13468
  if (!isNaN(value)) {
@@ -13133,7 +13473,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
13133
13473
  } else {
13134
13474
  return '';
13135
13475
  }
13136
- }, function(newVal) {
13476
+ }, function ngPluralizeWatchAction(newVal) {
13137
13477
  element.text(newVal);
13138
13478
  });
13139
13479
  }
@@ -13228,17 +13568,21 @@ var ngRepeatDirective = ngDirective({
13228
13568
  // We need an array of these objects since the same object can be returned from the iterator.
13229
13569
  // We expect this to be a rare case.
13230
13570
  var lastOrder = new HashQueueMap();
13231
- scope.$watch(function(scope){
13571
+
13572
+ scope.$watch(function ngRepeatWatch(scope){
13232
13573
  var index, length,
13233
13574
  collection = scope.$eval(rhs),
13234
- collectionLength = size(collection, true),
13235
- childScope,
13575
+ cursor = iterStartElement, // current position of the node
13236
13576
  // Same as lastOrder but it has the current state. It will become the
13237
13577
  // lastOrder on the next iteration.
13238
13578
  nextOrder = new HashQueueMap(),
13579
+ arrayBound,
13580
+ childScope,
13239
13581
  key, value, // key/value of iteration
13240
- array, last, // last object information {scope, element, index}
13241
- cursor = iterStartElement; // current position of the node
13582
+ array,
13583
+ last; // last object information {scope, element, index}
13584
+
13585
+
13242
13586
 
13243
13587
  if (!isArray(collection)) {
13244
13588
  // if object, extract keys, sort them and use to determine order of iteration over obj props
@@ -13253,11 +13597,15 @@ var ngRepeatDirective = ngDirective({
13253
13597
  array = collection || [];
13254
13598
  }
13255
13599
 
13600
+ arrayBound = array.length-1;
13601
+
13256
13602
  // we are not using forEach for perf reasons (trying to avoid #call)
13257
13603
  for (index = 0, length = array.length; index < length; index++) {
13258
13604
  key = (collection === array) ? index : array[index];
13259
13605
  value = collection[key];
13606
+
13260
13607
  last = lastOrder.shift(value);
13608
+
13261
13609
  if (last) {
13262
13610
  // if we have already seen this object, then we need to reuse the
13263
13611
  // associated scope/element
@@ -13286,7 +13634,7 @@ var ngRepeatDirective = ngDirective({
13286
13634
  childScope.$index = index;
13287
13635
 
13288
13636
  childScope.$first = (index === 0);
13289
- childScope.$last = (index === (collectionLength - 1));
13637
+ childScope.$last = (index === arrayBound);
13290
13638
  childScope.$middle = !(childScope.$first || childScope.$last);
13291
13639
 
13292
13640
  if (!last) {
@@ -13354,7 +13702,7 @@ var ngRepeatDirective = ngDirective({
13354
13702
  */
13355
13703
  //TODO(misko): refactor to remove element from the DOM
13356
13704
  var ngShowDirective = ngDirective(function(scope, element, attr){
13357
- scope.$watch(attr.ngShow, function(value){
13705
+ scope.$watch(attr.ngShow, function ngShowWatchAction(value){
13358
13706
  element.css('display', toBoolean(value) ? '' : 'none');
13359
13707
  });
13360
13708
  });
@@ -13365,11 +13713,11 @@ var ngShowDirective = ngDirective(function(scope, element, attr){
13365
13713
  * @name ng.directive:ngHide
13366
13714
  *
13367
13715
  * @description
13368
- * The `ngHide` and `ngShow` directives hide or show a portion
13369
- * of the HTML conditionally.
13716
+ * The `ngHide` and `ngShow` directives hide or show a portion of the DOM tree (HTML)
13717
+ * conditionally.
13370
13718
  *
13371
13719
  * @element ANY
13372
- * @param {expression} ngHide If the {@link guide/expression expression} truthy then
13720
+ * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
13373
13721
  * the element is shown or hidden respectively.
13374
13722
  *
13375
13723
  * @example
@@ -13394,7 +13742,7 @@ var ngShowDirective = ngDirective(function(scope, element, attr){
13394
13742
  */
13395
13743
  //TODO(misko): refactor to remove element from the DOM
13396
13744
  var ngHideDirective = ngDirective(function(scope, element, attr){
13397
- scope.$watch(attr.ngHide, function(value){
13745
+ scope.$watch(attr.ngHide, function ngHideWatchAction(value){
13398
13746
  element.css('display', toBoolean(value) ? 'none' : '');
13399
13747
  });
13400
13748
  });
@@ -13437,7 +13785,7 @@ var ngHideDirective = ngDirective(function(scope, element, attr){
13437
13785
  </example>
13438
13786
  */
13439
13787
  var ngStyleDirective = ngDirective(function(scope, element, attr) {
13440
- scope.$watch(attr.ngStyle, function(newStyles, oldStyles) {
13788
+ scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
13441
13789
  if (oldStyles && (newStyles !== oldStyles)) {
13442
13790
  forEach(oldStyles, function(val, style) { element.css(style, '');});
13443
13791
  }
@@ -13453,11 +13801,13 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
13453
13801
  * @description
13454
13802
  * Conditionally change the DOM structure.
13455
13803
  *
13456
- * @usageContent
13457
- * <ANY ng-switch-when="matchValue1">...</ANY>
13804
+ * @usage
13805
+ * <ANY ng-switch="expression">
13806
+ * <ANY ng-switch-when="matchValue1">...</ANY>
13458
13807
  * <ANY ng-switch-when="matchValue2">...</ANY>
13459
13808
  * ...
13460
13809
  * <ANY ng-switch-default>...</ANY>
13810
+ * </ANY>
13461
13811
  *
13462
13812
  * @scope
13463
13813
  * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
@@ -13507,52 +13857,54 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
13507
13857
  var NG_SWITCH = 'ng-switch';
13508
13858
  var ngSwitchDirective = valueFn({
13509
13859
  restrict: 'EA',
13510
- compile: function(element, attr) {
13860
+ require: 'ngSwitch',
13861
+ // asks for $scope to fool the BC controller module
13862
+ controller: ['$scope', function ngSwitchController() {
13863
+ this.cases = {};
13864
+ }],
13865
+ link: function(scope, element, attr, ctrl) {
13511
13866
  var watchExpr = attr.ngSwitch || attr.on,
13512
- cases = {};
13513
-
13514
- element.data(NG_SWITCH, cases);
13515
- return function(scope, element){
13516
- var selectedTransclude,
13517
- selectedElement,
13518
- selectedScope;
13519
-
13520
- scope.$watch(watchExpr, function(value) {
13521
- if (selectedElement) {
13522
- selectedScope.$destroy();
13523
- selectedElement.remove();
13524
- selectedElement = selectedScope = null;
13525
- }
13526
- if ((selectedTransclude = cases['!' + value] || cases['?'])) {
13527
- scope.$eval(attr.change);
13528
- selectedScope = scope.$new();
13529
- selectedTransclude(selectedScope, function(caseElement) {
13530
- selectedElement = caseElement;
13531
- element.append(caseElement);
13532
- });
13533
- }
13534
- });
13535
- };
13867
+ selectedTransclude,
13868
+ selectedElement,
13869
+ selectedScope;
13870
+
13871
+ scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
13872
+ if (selectedElement) {
13873
+ selectedScope.$destroy();
13874
+ selectedElement.remove();
13875
+ selectedElement = selectedScope = null;
13876
+ }
13877
+ if ((selectedTransclude = ctrl.cases['!' + value] || ctrl.cases['?'])) {
13878
+ scope.$eval(attr.change);
13879
+ selectedScope = scope.$new();
13880
+ selectedTransclude(selectedScope, function(caseElement) {
13881
+ selectedElement = caseElement;
13882
+ element.append(caseElement);
13883
+ });
13884
+ }
13885
+ });
13536
13886
  }
13537
13887
  });
13538
13888
 
13539
13889
  var ngSwitchWhenDirective = ngDirective({
13540
13890
  transclude: 'element',
13541
13891
  priority: 500,
13892
+ require: '^ngSwitch',
13542
13893
  compile: function(element, attrs, transclude) {
13543
- var cases = element.inheritedData(NG_SWITCH);
13544
- assertArg(cases);
13545
- cases['!' + attrs.ngSwitchWhen] = transclude;
13894
+ return function(scope, element, attr, ctrl) {
13895
+ ctrl.cases['!' + attrs.ngSwitchWhen] = transclude;
13896
+ };
13546
13897
  }
13547
13898
  });
13548
13899
 
13549
13900
  var ngSwitchDefaultDirective = ngDirective({
13550
13901
  transclude: 'element',
13551
13902
  priority: 500,
13903
+ require: '^ngSwitch',
13552
13904
  compile: function(element, attrs, transclude) {
13553
- var cases = element.inheritedData(NG_SWITCH);
13554
- assertArg(cases);
13555
- cases['?'] = transclude;
13905
+ return function(scope, element, attr, ctrl) {
13906
+ ctrl.cases['?'] = transclude;
13907
+ };
13556
13908
  }
13557
13909
  });
13558
13910
 
@@ -13641,7 +13993,7 @@ var ngTranscludeDirective = ngDirective({
13641
13993
  <hr />
13642
13994
 
13643
13995
  <pre>$location.path() = {{$location.path()}}</pre>
13644
- <pre>$route.current.template = {{$route.current.template}}</pre>
13996
+ <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
13645
13997
  <pre>$route.current.params = {{$route.current.params}}</pre>
13646
13998
  <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
13647
13999
  <pre>$routeParams = {{$routeParams}}</pre>
@@ -13760,7 +14112,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
13760
14112
  if (current.controller) {
13761
14113
  locals.$scope = lastScope;
13762
14114
  controller = $controller(current.controller, locals);
13763
- element.contents().data('$ngControllerController', controller);
14115
+ element.children().data('$ngControllerController', controller);
13764
14116
  }
13765
14117
 
13766
14118
  link(lastScope);
@@ -13851,6 +14203,9 @@ var scriptDirective = ['$templateCache', function($templateCache) {
13851
14203
  *
13852
14204
  * @param {string} name assignable expression to data-bind to.
13853
14205
  * @param {string=} required The control is considered valid only if value is entered.
14206
+ * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
14207
+ * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
14208
+ * `required` when you want to data-bind to the `required` attribute.
13854
14209
  * @param {comprehension_expression=} ngOptions in one of the following forms:
13855
14210
  *
13856
14211
  * * for array data sources:
@@ -13941,7 +14296,7 @@ var scriptDirective = ['$templateCache', function($templateCache) {
13941
14296
 
13942
14297
  var ngOptionsDirective = valueFn({ terminal: true });
13943
14298
  var selectDirective = ['$compile', '$parse', function($compile, $parse) {
13944
- //00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777
14299
+ //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
13945
14300
  var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,
13946
14301
  nullModelCtrl = {$setViewValue: noop};
13947
14302
 
@@ -14084,14 +14439,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
14084
14439
  var lastView;
14085
14440
  ctrl.$render = function() {
14086
14441
  var items = new HashMap(ctrl.$viewValue);
14087
- forEach(selectElement.children(), function(option) {
14442
+ forEach(selectElement.find('option'), function(option) {
14088
14443
  option.selected = isDefined(items.get(option.value));
14089
14444
  });
14090
14445
  };
14091
14446
 
14092
14447
  // we have to do it on each watch since ngModel watches reference, but
14093
14448
  // we need to work of an array, so we need to see if anything was inserted/removed
14094
- scope.$watch(function() {
14449
+ scope.$watch(function selectMultipleWatch() {
14095
14450
  if (!equals(lastView, ctrl.$viewValue)) {
14096
14451
  lastView = copy(ctrl.$viewValue);
14097
14452
  ctrl.$render();
@@ -14101,7 +14456,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
14101
14456
  selectElement.bind('change', function() {
14102
14457
  scope.$apply(function() {
14103
14458
  var array = [];
14104
- forEach(selectElement.children(), function(option) {
14459
+ forEach(selectElement.find('option'), function(option) {
14105
14460
  if (option.selected) {
14106
14461
  array.push(option.value);
14107
14462
  }
@@ -14208,7 +14563,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
14208
14563
  selected,
14209
14564
  selectedSet = false, // nothing is selected yet
14210
14565
  lastElement,
14211
- element;
14566
+ element,
14567
+ label;
14212
14568
 
14213
14569
  if (multiple) {
14214
14570
  selectedSet = new HashMap(modelValue);
@@ -14232,9 +14588,11 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
14232
14588
  selected = modelValue === valueFn(scope, locals);
14233
14589
  selectedSet = selectedSet || selected; // see if at least one item is selected
14234
14590
  }
14591
+ label = displayFn(scope, locals); // what will be seen by the user
14592
+ label = label === undefined ? '' : label; // doing displayFn(scope, locals) || '' overwrites zero values
14235
14593
  optionGroup.push({
14236
14594
  id: keyName ? keys[index] : index, // either the index into array or key from object
14237
- label: displayFn(scope, locals) || '', // what will be seen by the user
14595
+ label: label,
14238
14596
  selected: selected // determine if we should be selected
14239
14597
  });
14240
14598
  }
@@ -14366,7 +14724,7 @@ var optionDirective = ['$interpolate', function($interpolate) {
14366
14724
  }
14367
14725
 
14368
14726
  if (interpolateFn) {
14369
- scope.$watch(interpolateFn, function(newVal, oldVal) {
14727
+ scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
14370
14728
  attr.$set('value', newVal);
14371
14729
  if (newVal !== oldVal) selectCtrl.removeOption(oldVal);
14372
14730
  selectCtrl.addOption(newVal);
@@ -14387,6 +14745,7 @@ var styleDirective = valueFn({
14387
14745
  restrict: 'E',
14388
14746
  terminal: true
14389
14747
  });
14748
+
14390
14749
  //try to bind to jquery now so that one can write angular.element().read()
14391
14750
  //but we will rebind on bootstrap again.
14392
14751
  bindJQuery();