angular-rails-engine 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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();
|