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 +0 -0
- data/Gemfile +1 -1
- data/README.md +1 -1
- data/angular-rails-engine.gemspec +3 -0
- data/{vendor → app}/assets/javascripts/angular/angular-cookies.js +14 -1
- data/{vendor → app}/assets/javascripts/angular/angular-cookies.min.js +1 -1
- data/{vendor → app}/assets/javascripts/angular/angular-resource.js +89 -60
- data/app/assets/javascripts/angular/angular-resource.min.js +10 -0
- data/{vendor → app}/assets/javascripts/angular/angular-sanitize.js +4 -2
- data/{vendor → app}/assets/javascripts/angular/angular-sanitize.min.js +1 -1
- data/{vendor → app}/assets/javascripts/angular/angular.js +736 -377
- data/app/assets/javascripts/angular/angular.min.js +162 -0
- data/gem-public_cert.pem +20 -0
- data/lib/angular-rails-engine.rb +1 -1
- data/lib/angular-rails-engine/version.rb +1 -1
- metadata +40 -12
- metadata.gz.sig +3 -0
- data/vendor/assets/javascripts/angular/angular-resource.min.js +0 -10
- data/vendor/assets/javascripts/angular/angular.min.js +0 -158
data.tar.gz.sig
ADDED
Binary file
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -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
|
+
* @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
|
-
* @license AngularJS v1.0.
|
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`
|
71
|
-
* methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
|
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
|
-
*
|
153
|
-
*
|
154
|
-
*
|
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
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
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 &&
|
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
|
-
|
293
|
-
|
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
|
-
|
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
|
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
|
+
* @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
|
-
* @license AngularJS v1.0.
|
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 (
|
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
|
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
|
-
|
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)
|
631
|
-
|
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] &&
|
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
|
-
|
764
|
-
|
765
|
-
|
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
|
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 ?
|
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
|
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
|
-
*
|
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
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
['$rootScope', '$rootElement', '$compile', '$injector',
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
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
|
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
|
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
|
1204
|
-
*
|
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.
|
1251
|
-
major: 1, //
|
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:
|
1254
|
-
codeName: '
|
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
|
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
|
-
|
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,
|
1634
|
-
if (
|
1635
|
-
forEach(
|
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,
|
1646
|
-
if (
|
1647
|
-
forEach(
|
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.
|
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
|
-
|
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
|
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*(_?)(
|
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
|
-
* $
|
2385
|
+
* $injector.invoke(function(serviceA){});
|
2317
2386
|
*
|
2318
2387
|
* // annotated
|
2319
2388
|
* function explicit(serviceA) {};
|
2320
2389
|
* explicit.$inject = ['serviceA'];
|
2321
|
-
* $
|
2390
|
+
* $injector.invoke(explicit);
|
2322
2391
|
*
|
2323
2392
|
* // inline
|
2324
|
-
* $
|
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 `$
|
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
|
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
|
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 $
|
2943
|
+
// (no url change, no $location.hash() change), browser native does scroll
|
2874
2944
|
if (autoScrollingEnabled) {
|
2875
|
-
$rootScope.$watch(function() {return $location.hash();},
|
2876
|
-
|
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?\:\/\/[^\/]*/, '') :
|
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
|
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($
|
3724
|
-
if (!($
|
3725
|
-
// jquery always rewraps,
|
3726
|
-
$
|
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($
|
3731
|
-
if (node.nodeType == 3 /* text node */) {
|
3732
|
-
$
|
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($
|
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($
|
3742
|
-
: $
|
3743
|
-
|
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
|
-
|
3781
|
-
|
3908
|
+
var linkFns = [],
|
3909
|
+
nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
|
3782
3910
|
|
3783
|
-
|
3784
|
-
|
3911
|
+
for(var i = 0; i < nodeList.length; i++) {
|
3912
|
+
attrs = new Attributes();
|
3785
3913
|
|
3786
|
-
|
3787
|
-
|
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
|
-
|
3790
|
-
|
3791
|
-
|
3917
|
+
nodeLinkFn = (directives.length)
|
3918
|
+
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
|
3919
|
+
: null;
|
3792
3920
|
|
3793
|
-
|
3794
|
-
|
3795
|
-
|
3796
|
-
|
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
|
-
|
3799
|
-
|
3800
|
-
|
3801
|
-
|
3926
|
+
linkFns.push(nodeLinkFn);
|
3927
|
+
linkFns.push(childLinkFn);
|
3928
|
+
linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
|
3929
|
+
}
|
3802
3930
|
|
3803
|
-
|
3804
|
-
|
3931
|
+
// return a linking function if we have found anything, null otherwise
|
3932
|
+
return linkFnFound ? compositeLinkFn : null;
|
3805
3933
|
|
3806
|
-
|
3807
|
-
|
3934
|
+
function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
|
3935
|
+
var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn, i, ii, n;
|
3808
3936
|
|
3809
|
-
|
3810
|
-
|
3811
|
-
|
3812
|
-
|
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
|
-
|
3815
|
-
|
3816
|
-
|
3817
|
-
|
3818
|
-
|
3819
|
-
|
3820
|
-
|
3821
|
-
|
3822
|
-
|
3823
|
-
|
3824
|
-
|
3825
|
-
|
3826
|
-
|
3827
|
-
|
3828
|
-
|
3829
|
-
|
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
|
-
|
3834
|
-
|
3835
|
-
|
3836
|
-
|
3837
|
-
|
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
|
3980
|
+
* Looks for directives on the given node and adds them to the directive collection which is
|
3981
|
+
* sorted.
|
3846
3982
|
*
|
3847
|
-
* @param node
|
3848
|
-
* @param directives
|
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
|
3851
|
-
* @param {number=}
|
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
|
-
|
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',
|
4098
|
+
assertNoDuplicate('isolated scope', newIsolateScopeDirective, directive, $compileNode);
|
3963
4099
|
if (isObject(directiveValue)) {
|
3964
4100
|
safeAddClass($compileNode, 'ng-isolate-scope');
|
3965
|
-
|
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('
|
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 (
|
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(
|
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: ' +
|
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
|
-
|
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,
|
4342
|
-
afterTemplateChildLinkFn = compileNodes($compileNode.
|
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\.-]
|
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(),
|
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){
|
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
|
6409
|
-
* against (
|
6410
|
-
* * `locals
|
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
|
6447
|
-
* asynchronous
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
* `$
|
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
|
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
|
-
|
7325
|
+
|
7326
|
+
// Escape regexp special characters.
|
7327
|
+
when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$';
|
7328
|
+
var regex = '',
|
7142
7329
|
params = [],
|
7143
7330
|
dst = {};
|
7144
|
-
|
7145
|
-
|
7146
|
-
|
7147
|
-
|
7148
|
-
|
7149
|
-
|
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
|
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(
|
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 =
|
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
|
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
|
-
|
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
|
7468
|
-
* parent scope. The scope is isolated, as it can not
|
7469
|
-
* When creating widgets it is useful for the widget to not
|
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
|
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
|
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
|
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
|
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
|
-
*
|
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
|
-
|
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
|
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
|
-
*
|
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}
|
7920
|
-
* - `currentScope` - {Scope}
|
7921
|
-
* - `name` - {string}
|
7922
|
-
* - `stopPropagation` - {function=}
|
7923
|
-
* (available only for events that were `$emit`-ed).
|
7924
|
-
* - `preventDefault` - {function}
|
7925
|
-
* - `defaultPrevented` - {boolean}
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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 $
|
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
|
8406
|
-
*
|
8407
|
-
*
|
8408
|
-
*
|
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
|
-
|
8994
|
-
|
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 {
|
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
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
9593
|
-
|
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
|
9677
|
-
|
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
|
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}
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
-
*
|
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
|
-
|
10163
|
-
|
10164
|
-
|
10165
|
-
|
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
|
-
|
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
|
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 `
|
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
|
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.
|
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.
|
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
|
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
|
-
|
11777
|
-
|
12072
|
+
$scope.$watch(function ngModelWatch() {
|
12073
|
+
var value = ngModelGet($scope);
|
11778
12074
|
|
11779
|
-
|
11780
|
-
|
12075
|
+
// if scope model value and ngModel value are out of sync
|
12076
|
+
if (ctrl.$modelValue !== value) {
|
11781
12077
|
|
11782
|
-
|
11783
|
-
|
11784
|
-
value = formatters[idx](value);
|
11785
|
-
}
|
12078
|
+
var formatters = ctrl.$formatters,
|
12079
|
+
idx = formatters.length;
|
11786
12080
|
|
11787
|
-
|
11788
|
-
|
11789
|
-
|
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-
|
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
|
-
|
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
|
-
|
12175
|
-
|
12176
|
-
|
12177
|
-
|
12178
|
-
|
12179
|
-
|
12180
|
-
|
12181
|
-
|
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
|
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
|
-
|
13571
|
+
|
13572
|
+
scope.$watch(function ngRepeatWatch(scope){
|
13232
13573
|
var index, length,
|
13233
13574
|
collection = scope.$eval(rhs),
|
13234
|
-
|
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,
|
13241
|
-
|
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 ===
|
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
|
-
*
|
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
|
-
* @
|
13457
|
-
* <ANY ng-switch
|
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
|
-
|
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
|
-
|
13513
|
-
|
13514
|
-
|
13515
|
-
|
13516
|
-
|
13517
|
-
|
13518
|
-
|
13519
|
-
|
13520
|
-
|
13521
|
-
|
13522
|
-
|
13523
|
-
|
13524
|
-
|
13525
|
-
|
13526
|
-
|
13527
|
-
|
13528
|
-
|
13529
|
-
|
13530
|
-
|
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
|
-
|
13544
|
-
|
13545
|
-
|
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
|
-
|
13554
|
-
|
13555
|
-
|
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.
|
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.
|
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
|
-
//
|
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.
|
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.
|
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:
|
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();
|