angular-rails-engine 1.2.5.0 → 1.2.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +1 -1
- data/app/assets/javascripts/angular/angular-animate.js +461 -195
- data/app/assets/javascripts/angular/angular-animate.min.js +23 -18
- data/app/assets/javascripts/angular/angular-cookies.js +1 -1
- data/app/assets/javascripts/angular/angular-cookies.min.js +2 -1
- data/app/assets/javascripts/angular/angular-loader.js +2 -2
- data/app/assets/javascripts/angular/angular-loader.min.js +3 -2
- data/app/assets/javascripts/angular/angular-mocks.js +54 -34
- data/app/assets/javascripts/angular/angular-resource.js +36 -5
- data/app/assets/javascripts/angular/angular-resource.min.js +9 -8
- data/app/assets/javascripts/angular/angular-route.js +25 -15
- data/app/assets/javascripts/angular/angular-route.min.js +10 -9
- data/app/assets/javascripts/angular/angular-sanitize.js +35 -32
- data/app/assets/javascripts/angular/angular-sanitize.min.js +3 -2
- data/app/assets/javascripts/angular/angular-scenario.js +1472 -966
- data/app/assets/javascripts/angular/angular-touch.js +1 -1
- data/app/assets/javascripts/angular/angular-touch.min.js +2 -1
- data/app/assets/javascripts/angular/angular.js +1470 -965
- data/app/assets/javascripts/angular/angular.min.js +200 -196
- data/app/assets/stylesheets/angular/angular-csp.css +18 -0
- data/gem-public_cert.pem +11 -10
- data/lib/angular-rails-engine.rb +1 -1
- data/lib/angular-rails-engine/version.rb +1 -1
- metadata +14 -13
- metadata.gz.sig +0 -0
- data/app/assets/stylesheets/angular-csp.css +0 -24
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
AngularJS v1.2.
|
2
|
+
AngularJS v1.2.13
|
3
3
|
(c) 2010-2014 Google, Inc. http://angularjs.org
|
4
4
|
License: MIT
|
5
5
|
*/
|
@@ -10,3 +10,4 @@ a.touches&&a.touches.length?a.touches:[a],b=c[0].clientX,c=c[0].clientY;1>b&&1>c
|
|
10
10
|
function(a){q=!0;s=a.target?a.target:a.srcElement;3==s.nodeType&&(s=s.parentNode);c.addClass(p);t=Date.now();a=a.touches&&a.touches.length?a.touches:[a];a=a[0].originalEvent||a[0];w=a.clientX;x=a.clientY});c.on("touchmove",function(a){f()});c.on("touchcancel",function(a){f()});c.on("touchend",function(a){var h=Date.now()-t,e=a.changedTouches&&a.changedTouches.length?a.changedTouches:a.touches&&a.touches.length?a.touches:[a],g=e[0].originalEvent||e[0],e=g.clientX,g=g.clientY,p=Math.sqrt(Math.pow(e-
|
11
11
|
w,2)+Math.pow(g-x,2));q&&(750>h&&12>p)&&(k||(b[0].addEventListener("click",n,!0),b[0].addEventListener("touchstart",r,!0),k=[]),m=Date.now(),l(k,e,g),s&&s.blur(),v.isDefined(d.disabled)&&!1!==d.disabled||c.triggerHandler("click",[a]));f()});c.onclick=function(a){};c.on("click",function(b,c){a.$apply(function(){h(a,{$event:c||b})})});c.on("mousedown",function(a){c.addClass(p)});c.on("mousemove mouseup",function(a){c.removeClass(p)})}}]);t("ngSwipeLeft",-1,"swipeleft");t("ngSwipeRight",1,"swiperight")})(window,
|
12
12
|
window.angular);
|
13
|
+
//# sourceMappingURL=angular-touch.min.js.map
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license AngularJS v1.2.
|
2
|
+
* @license AngularJS v1.2.13
|
3
3
|
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
4
4
|
* License: MIT
|
5
5
|
*/
|
@@ -68,7 +68,7 @@ function minErr(module) {
|
|
68
68
|
return match;
|
69
69
|
});
|
70
70
|
|
71
|
-
message = message + '\nhttp://errors.angularjs.org/1.2.
|
71
|
+
message = message + '\nhttp://errors.angularjs.org/1.2.13/' +
|
72
72
|
(module ? module + '/' : '') + code;
|
73
73
|
for (i = 2; i < arguments.length; i++) {
|
74
74
|
message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
|
@@ -160,6 +160,7 @@ function minErr(module) {
|
|
160
160
|
-assertNotHasOwnProperty,
|
161
161
|
-getter,
|
162
162
|
-getBlockElements,
|
163
|
+
-hasOwnProperty,
|
163
164
|
|
164
165
|
*/
|
165
166
|
|
@@ -175,7 +176,7 @@ function minErr(module) {
|
|
175
176
|
* @returns {string} Lowercased string.
|
176
177
|
*/
|
177
178
|
var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;};
|
178
|
-
|
179
|
+
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
179
180
|
|
180
181
|
/**
|
181
182
|
* @ngdoc function
|
@@ -271,7 +272,8 @@ function isArrayLike(obj) {
|
|
271
272
|
* is the value of an object property or an array element and `key` is the object property key or
|
272
273
|
* array element index. Specifying a `context` for the function is optional.
|
273
274
|
*
|
274
|
-
*
|
275
|
+
* It is worth noting that `.forEach` does not iterate over inherited properties because it filters
|
276
|
+
* using the `hasOwnProperty` method.
|
275
277
|
*
|
276
278
|
<pre>
|
277
279
|
var values = {name: 'misko', gender: 'male'};
|
@@ -279,7 +281,7 @@ function isArrayLike(obj) {
|
|
279
281
|
angular.forEach(values, function(value, key){
|
280
282
|
this.push(key + ': ' + value);
|
281
283
|
}, log);
|
282
|
-
expect(log).toEqual(['name: misko', 'gender:male']);
|
284
|
+
expect(log).toEqual(['name: misko', 'gender: male']);
|
283
285
|
</pre>
|
284
286
|
*
|
285
287
|
* @param {Object|Array} obj Object to iterate over.
|
@@ -292,7 +294,9 @@ function forEach(obj, iterator, context) {
|
|
292
294
|
if (obj) {
|
293
295
|
if (isFunction(obj)){
|
294
296
|
for (key in obj) {
|
295
|
-
|
297
|
+
// Need to check if hasOwnProperty exists,
|
298
|
+
// as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
|
299
|
+
if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
|
296
300
|
iterator.call(context, obj[key], key);
|
297
301
|
}
|
298
302
|
}
|
@@ -848,7 +852,7 @@ function shallowCopy(src, dst) {
|
|
848
852
|
for(var key in src) {
|
849
853
|
// shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
|
850
854
|
// so we don't need to worry about using our custom hasOwnProperty here
|
851
|
-
if (src.hasOwnProperty(key) && key.
|
855
|
+
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
852
856
|
dst[key] = src[key];
|
853
857
|
}
|
854
858
|
}
|
@@ -1036,7 +1040,9 @@ function fromJson(json) {
|
|
1036
1040
|
|
1037
1041
|
|
1038
1042
|
function toBoolean(value) {
|
1039
|
-
if (
|
1043
|
+
if (typeof value === 'function') {
|
1044
|
+
value = true;
|
1045
|
+
} else if (value && value.length !== 0) {
|
1040
1046
|
var v = lowercase("" + value);
|
1041
1047
|
value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
|
1042
1048
|
} else {
|
@@ -1205,6 +1211,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
|
1205
1211
|
<file name="index.html">
|
1206
1212
|
<div ng-controller="ngAppDemoController">
|
1207
1213
|
I can add: {{a}} + {{b}} = {{ a+b }}
|
1214
|
+
</div>
|
1208
1215
|
</file>
|
1209
1216
|
<file name="script.js">
|
1210
1217
|
angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
|
@@ -1829,11 +1836,11 @@ function setupModuleLoader(window) {
|
|
1829
1836
|
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
|
1830
1837
|
*/
|
1831
1838
|
var version = {
|
1832
|
-
full: '1.2.
|
1839
|
+
full: '1.2.13', // all of these placeholder strings will be replaced by grunt's
|
1833
1840
|
major: 1, // package task
|
1834
1841
|
minor: 2,
|
1835
|
-
dot:
|
1836
|
-
codeName: '
|
1842
|
+
dot: 13,
|
1843
|
+
codeName: 'romantic-transclusion'
|
1837
1844
|
};
|
1838
1845
|
|
1839
1846
|
|
@@ -1995,7 +2002,7 @@ function publishExternalAPI(angular){
|
|
1995
2002
|
* - [`after()`](http://api.jquery.com/after/)
|
1996
2003
|
* - [`append()`](http://api.jquery.com/append/)
|
1997
2004
|
* - [`attr()`](http://api.jquery.com/attr/)
|
1998
|
-
* - [`bind()`](http://api.jquery.com/
|
2005
|
+
* - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
|
1999
2006
|
* - [`children()`](http://api.jquery.com/children/) - Does not support selectors
|
2000
2007
|
* - [`clone()`](http://api.jquery.com/clone/)
|
2001
2008
|
* - [`contents()`](http://api.jquery.com/contents/)
|
@@ -2009,6 +2016,7 @@ function publishExternalAPI(angular){
|
|
2009
2016
|
* - [`next()`](http://api.jquery.com/next/) - Does not support selectors
|
2010
2017
|
* - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
|
2011
2018
|
* - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
|
2019
|
+
* - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
|
2012
2020
|
* - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
|
2013
2021
|
* - [`prepend()`](http://api.jquery.com/prepend/)
|
2014
2022
|
* - [`prop()`](http://api.jquery.com/prop/)
|
@@ -2021,7 +2029,7 @@ function publishExternalAPI(angular){
|
|
2021
2029
|
* - [`text()`](http://api.jquery.com/text/)
|
2022
2030
|
* - [`toggleClass()`](http://api.jquery.com/toggleClass/)
|
2023
2031
|
* - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
|
2024
|
-
* - [`unbind()`](http://api.jquery.com/
|
2032
|
+
* - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
|
2025
2033
|
* - [`val()`](http://api.jquery.com/val/)
|
2026
2034
|
* - [`wrap()`](http://api.jquery.com/wrap/)
|
2027
2035
|
*
|
@@ -2061,6 +2069,14 @@ var jqCache = JQLite.cache = {},
|
|
2061
2069
|
? function(element, type, fn) {element.removeEventListener(type, fn, false); }
|
2062
2070
|
: function(element, type, fn) {element.detachEvent('on' + type, fn); });
|
2063
2071
|
|
2072
|
+
/*
|
2073
|
+
* !!! This is an undocumented "private" function !!!
|
2074
|
+
*/
|
2075
|
+
var jqData = JQLite._data = function(node) {
|
2076
|
+
//jQuery always returns an object on cache miss
|
2077
|
+
return this.cache[node[this.expando]] || {};
|
2078
|
+
};
|
2079
|
+
|
2064
2080
|
function jqNextId() { return ++jqId; }
|
2065
2081
|
|
2066
2082
|
|
@@ -2129,6 +2145,9 @@ function JQLite(element) {
|
|
2129
2145
|
if (element instanceof JQLite) {
|
2130
2146
|
return element;
|
2131
2147
|
}
|
2148
|
+
if (isString(element)) {
|
2149
|
+
element = trim(element);
|
2150
|
+
}
|
2132
2151
|
if (!(this instanceof JQLite)) {
|
2133
2152
|
if (isString(element) && element.charAt(0) != '<') {
|
2134
2153
|
throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
|
@@ -2600,7 +2619,10 @@ function createEventHandler(element, events) {
|
|
2600
2619
|
return event.defaultPrevented || event.returnValue === false;
|
2601
2620
|
};
|
2602
2621
|
|
2603
|
-
|
2622
|
+
// Copy event handlers in case event handlers array is modified during execution.
|
2623
|
+
var eventHandlersCopy = shallowCopy(events[type || event.type] || []);
|
2624
|
+
|
2625
|
+
forEach(eventHandlersCopy, function(fn) {
|
2604
2626
|
fn.call(element, event);
|
2605
2627
|
});
|
2606
2628
|
|
@@ -2696,6 +2718,19 @@ forEach({
|
|
2696
2718
|
|
2697
2719
|
off: jqLiteOff,
|
2698
2720
|
|
2721
|
+
one: function(element, type, fn) {
|
2722
|
+
element = jqLite(element);
|
2723
|
+
|
2724
|
+
//add the listener twice so that when it is called
|
2725
|
+
//you can remove the original function and still be
|
2726
|
+
//able to call element.off(ev, fn) normally
|
2727
|
+
element.on(type, function onFn() {
|
2728
|
+
element.off(type, fn);
|
2729
|
+
element.off(type, onFn);
|
2730
|
+
});
|
2731
|
+
element.on(type, fn);
|
2732
|
+
},
|
2733
|
+
|
2699
2734
|
replaceWith: function(element, replaceNode) {
|
2700
2735
|
var index, parent = element.parentNode;
|
2701
2736
|
jqLiteDealoc(element);
|
@@ -3258,11 +3293,9 @@ function annotate(fn) {
|
|
3258
3293
|
* @param {(Object|function())} provider If the provider is:
|
3259
3294
|
*
|
3260
3295
|
* - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
|
3261
|
-
*
|
3262
|
-
*
|
3263
|
-
*
|
3264
|
-
* {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as
|
3265
|
-
* `object`.
|
3296
|
+
* {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created.
|
3297
|
+
* - `Constructor`: a new instance of the provider will be created using
|
3298
|
+
* {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`.
|
3266
3299
|
*
|
3267
3300
|
* @returns {Object} registered provider instance
|
3268
3301
|
|
@@ -3378,7 +3411,7 @@ function annotate(fn) {
|
|
3378
3411
|
* constructor function that will be used to instantiate the service instance.
|
3379
3412
|
*
|
3380
3413
|
* You should use {@link AUTO.$provide#methods_service $provide.service(class)} if you define your service
|
3381
|
-
* as a type/class.
|
3414
|
+
* as a type/class.
|
3382
3415
|
*
|
3383
3416
|
* @param {string} name The name of the instance.
|
3384
3417
|
* @param {Function} constructor A class (constructor function) that will be instantiated.
|
@@ -3386,20 +3419,24 @@ function annotate(fn) {
|
|
3386
3419
|
*
|
3387
3420
|
* @example
|
3388
3421
|
* Here is an example of registering a service using
|
3389
|
-
* {@link AUTO.$provide#methods_service $provide.service(class)}
|
3422
|
+
* {@link AUTO.$provide#methods_service $provide.service(class)}.
|
3390
3423
|
* <pre>
|
3391
|
-
*
|
3392
|
-
*
|
3393
|
-
*
|
3394
|
-
*
|
3395
|
-
*
|
3396
|
-
*
|
3424
|
+
* var Ping = function($http) {
|
3425
|
+
* this.$http = $http;
|
3426
|
+
* };
|
3427
|
+
*
|
3428
|
+
* Ping.$inject = ['$http'];
|
3429
|
+
*
|
3430
|
+
* Ping.prototype.send = function() {
|
3431
|
+
* return this.$http.get('/ping');
|
3432
|
+
* };
|
3433
|
+
* $provide.service('ping', Ping);
|
3397
3434
|
* </pre>
|
3398
3435
|
* You would then inject and use this service like this:
|
3399
3436
|
* <pre>
|
3400
|
-
* someModule.controller
|
3401
|
-
* ping.send()
|
3402
|
-
* ]
|
3437
|
+
* someModule.controller('Ctrl', ['ping', function(ping) {
|
3438
|
+
* ping.send();
|
3439
|
+
* }]);
|
3403
3440
|
* </pre>
|
3404
3441
|
*/
|
3405
3442
|
|
@@ -3491,7 +3528,7 @@ function annotate(fn) {
|
|
3491
3528
|
* Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
|
3492
3529
|
* calls to {@link ng.$log#error $log.warn()}.
|
3493
3530
|
* <pre>
|
3494
|
-
* $
|
3531
|
+
* $provide.decorator('$log', ['$delegate', function($delegate) {
|
3495
3532
|
* $delegate.warn = $delegate.error;
|
3496
3533
|
* return $delegate;
|
3497
3534
|
* }]);
|
@@ -3644,6 +3681,11 @@ function createInjector(modulesToLoad) {
|
|
3644
3681
|
path.unshift(serviceName);
|
3645
3682
|
cache[serviceName] = INSTANTIATING;
|
3646
3683
|
return cache[serviceName] = factory(serviceName);
|
3684
|
+
} catch (err) {
|
3685
|
+
if (cache[serviceName] === INSTANTIATING) {
|
3686
|
+
delete cache[serviceName];
|
3687
|
+
}
|
3688
|
+
throw err;
|
3647
3689
|
} finally {
|
3648
3690
|
path.shift();
|
3649
3691
|
}
|
@@ -3864,6 +3906,28 @@ var $AnimateProvider = ['$provide', function($provide) {
|
|
3864
3906
|
$provide.factory(key, factory);
|
3865
3907
|
};
|
3866
3908
|
|
3909
|
+
/**
|
3910
|
+
* @ngdoc function
|
3911
|
+
* @name ng.$animateProvider#classNameFilter
|
3912
|
+
* @methodOf ng.$animateProvider
|
3913
|
+
*
|
3914
|
+
* @description
|
3915
|
+
* Sets and/or returns the CSS class regular expression that is checked when performing
|
3916
|
+
* an animation. Upon bootstrap the classNameFilter value is not set at all and will
|
3917
|
+
* therefore enable $animate to attempt to perform an animation on any element.
|
3918
|
+
* When setting the classNameFilter value, animations will only be performed on elements
|
3919
|
+
* that successfully match the filter expression. This in turn can boost performance
|
3920
|
+
* for low-powered devices as well as applications containing a lot of structural operations.
|
3921
|
+
* @param {RegExp=} expression The className expression which will be checked against all animations
|
3922
|
+
* @return {RegExp} The current CSS className expression value. If null then there is no expression value
|
3923
|
+
*/
|
3924
|
+
this.classNameFilter = function(expression) {
|
3925
|
+
if(arguments.length === 1) {
|
3926
|
+
this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
|
3927
|
+
}
|
3928
|
+
return this.$$classNameFilter;
|
3929
|
+
};
|
3930
|
+
|
3867
3931
|
this.$get = ['$timeout', function($timeout) {
|
3868
3932
|
|
3869
3933
|
/**
|
@@ -4003,6 +4067,29 @@ var $AnimateProvider = ['$provide', function($provide) {
|
|
4003
4067
|
done && $timeout(done, 0, false);
|
4004
4068
|
},
|
4005
4069
|
|
4070
|
+
/**
|
4071
|
+
*
|
4072
|
+
* @ngdoc function
|
4073
|
+
* @name ng.$animate#setClass
|
4074
|
+
* @methodOf ng.$animate
|
4075
|
+
* @function
|
4076
|
+
* @description Adds and/or removes the given CSS classes to and from the element.
|
4077
|
+
* Once complete, the done() callback will be fired (if provided).
|
4078
|
+
* @param {jQuery/jqLite element} element the element which will it's CSS classes changed
|
4079
|
+
* removed from it
|
4080
|
+
* @param {string} add the CSS classes which will be added to the element
|
4081
|
+
* @param {string} remove the CSS class which will be removed from the element
|
4082
|
+
* @param {function=} done the callback function (if provided) that will be fired after the
|
4083
|
+
* CSS classes have been set on the element
|
4084
|
+
*/
|
4085
|
+
setClass : function(element, add, remove, done) {
|
4086
|
+
forEach(element, function (element) {
|
4087
|
+
jqLiteAddClass(element, add);
|
4088
|
+
jqLiteRemoveClass(element, remove);
|
4089
|
+
});
|
4090
|
+
done && $timeout(done, 0, false);
|
4091
|
+
},
|
4092
|
+
|
4006
4093
|
enabled : noop
|
4007
4094
|
};
|
4008
4095
|
}];
|
@@ -4156,8 +4243,9 @@ function Browser(window, document, $log, $sniffer) {
|
|
4156
4243
|
* @param {boolean=} replace Should new url replace current history record ?
|
4157
4244
|
*/
|
4158
4245
|
self.url = function(url, replace) {
|
4159
|
-
// Android Browser BFCache causes location reference to become stale.
|
4246
|
+
// Android Browser BFCache causes location, history reference to become stale.
|
4160
4247
|
if (location !== window.location) location = window.location;
|
4248
|
+
if (history !== window.history) history = window.history;
|
4161
4249
|
|
4162
4250
|
// setter
|
4163
4251
|
if (url) {
|
@@ -4209,7 +4297,7 @@ function Browser(window, document, $log, $sniffer) {
|
|
4209
4297
|
* @description
|
4210
4298
|
* Register callback function that will be called, when url changes.
|
4211
4299
|
*
|
4212
|
-
* It's only called when the url is changed
|
4300
|
+
* It's only called when the url is changed from outside of angular:
|
4213
4301
|
* - user types different url into address bar
|
4214
4302
|
* - user clicks on history (forward/back) button
|
4215
4303
|
* - user clicks on a link
|
@@ -4251,7 +4339,7 @@ function Browser(window, document, $log, $sniffer) {
|
|
4251
4339
|
/**
|
4252
4340
|
* @name ng.$browser#baseHref
|
4253
4341
|
* @methodOf ng.$browser
|
4254
|
-
*
|
4342
|
+
*
|
4255
4343
|
* @description
|
4256
4344
|
* Returns current <base href>
|
4257
4345
|
* (always relative - without domain)
|
@@ -4260,7 +4348,7 @@ function Browser(window, document, $log, $sniffer) {
|
|
4260
4348
|
*/
|
4261
4349
|
self.baseHref = function() {
|
4262
4350
|
var href = baseElement.attr('href');
|
4263
|
-
return href ? href.replace(/^https
|
4351
|
+
return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
|
4264
4352
|
};
|
4265
4353
|
|
4266
4354
|
//////////////////////////////////////////////////////////////
|
@@ -4282,13 +4370,13 @@ function Browser(window, document, $log, $sniffer) {
|
|
4282
4370
|
* It is not meant to be used directly, use the $cookie service instead.
|
4283
4371
|
*
|
4284
4372
|
* The return values vary depending on the arguments that the method was called with as follows:
|
4285
|
-
*
|
4373
|
+
*
|
4286
4374
|
* - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
|
4287
4375
|
* it
|
4288
4376
|
* - cookies(name, value) -> set name to value, if value is undefined delete the cookie
|
4289
4377
|
* - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
|
4290
4378
|
* way)
|
4291
|
-
*
|
4379
|
+
*
|
4292
4380
|
* @returns {Object} Hash of all cookies (if called without any parameter)
|
4293
4381
|
*/
|
4294
4382
|
self.cookies = function(name, value) {
|
@@ -4666,7 +4754,7 @@ function $TemplateCacheProvider() {
|
|
4666
4754
|
* @function
|
4667
4755
|
*
|
4668
4756
|
* @description
|
4669
|
-
* Compiles
|
4757
|
+
* Compiles an HTML string or DOM into a template and produces a template function, which
|
4670
4758
|
* can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
|
4671
4759
|
*
|
4672
4760
|
* The compilation is a process of walking the DOM tree and matching DOM elements to
|
@@ -5066,13 +5154,17 @@ function $TemplateCacheProvider() {
|
|
5066
5154
|
<div compile="html"></div>
|
5067
5155
|
</div>
|
5068
5156
|
</doc:source>
|
5069
|
-
<doc:
|
5157
|
+
<doc:protractor>
|
5070
5158
|
it('should auto compile', function() {
|
5071
|
-
|
5072
|
-
|
5073
|
-
|
5159
|
+
var textarea = $('textarea');
|
5160
|
+
var output = $('div[compile]');
|
5161
|
+
// The initial state reads 'Hello Angular'.
|
5162
|
+
expect(output.getText()).toBe('Hello Angular');
|
5163
|
+
textarea.clear();
|
5164
|
+
textarea.sendKeys('{{name}}!');
|
5165
|
+
expect(output.getText()).toBe('Angular!');
|
5074
5166
|
});
|
5075
|
-
</doc:
|
5167
|
+
</doc:protractor>
|
5076
5168
|
</doc:example>
|
5077
5169
|
|
5078
5170
|
*
|
@@ -5111,14 +5203,14 @@ function $TemplateCacheProvider() {
|
|
5111
5203
|
* example would not point to the clone, but rather to the original template that was cloned. In
|
5112
5204
|
* this case, you can access the clone via the cloneAttachFn:
|
5113
5205
|
* <pre>
|
5114
|
-
* var
|
5206
|
+
* var templateElement = angular.element('<p>{{total}}</p>'),
|
5115
5207
|
* scope = ....;
|
5116
5208
|
*
|
5117
|
-
* var clonedElement = $compile(
|
5209
|
+
* var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
|
5118
5210
|
* //attach the clone to DOM document at the right place
|
5119
5211
|
* });
|
5120
5212
|
*
|
5121
|
-
* //now we have reference to the cloned DOM via `
|
5213
|
+
* //now we have reference to the cloned DOM via `clonedElement`
|
5122
5214
|
* </pre>
|
5123
5215
|
*
|
5124
5216
|
*
|
@@ -5140,7 +5232,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5140
5232
|
var hasDirectives = {},
|
5141
5233
|
Suffix = 'Directive',
|
5142
5234
|
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
5143
|
-
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)
|
5235
|
+
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
5236
|
+
TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|tbody)(\s+[^>]*)?>/i;
|
5144
5237
|
|
5145
5238
|
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
|
5146
5239
|
// The assumption is that future DOM event attribute names will begin with
|
@@ -5327,8 +5420,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5327
5420
|
* @param {string} oldClasses The former CSS className value
|
5328
5421
|
*/
|
5329
5422
|
$updateClass : function(newClasses, oldClasses) {
|
5330
|
-
|
5331
|
-
|
5423
|
+
var toAdd = tokenDifference(newClasses, oldClasses);
|
5424
|
+
var toRemove = tokenDifference(oldClasses, newClasses);
|
5425
|
+
|
5426
|
+
if(toAdd.length === 0) {
|
5427
|
+
$animate.removeClass(this.$$element, toRemove);
|
5428
|
+
} else if(toRemove.length === 0) {
|
5429
|
+
$animate.addClass(this.$$element, toAdd);
|
5430
|
+
} else {
|
5431
|
+
$animate.setClass(this.$$element, toAdd, toRemove);
|
5432
|
+
}
|
5332
5433
|
},
|
5333
5434
|
|
5334
5435
|
/**
|
@@ -5460,6 +5561,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5460
5561
|
var compositeLinkFn =
|
5461
5562
|
compileNodes($compileNodes, transcludeFn, $compileNodes,
|
5462
5563
|
maxPriority, ignoreDirective, previousCompileContext);
|
5564
|
+
safeAddClass($compileNodes, 'ng-scope');
|
5463
5565
|
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers){
|
5464
5566
|
assertArg(scope, 'scope');
|
5465
5567
|
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
|
@@ -5474,12 +5576,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5474
5576
|
|
5475
5577
|
// Attach scope only to non-text nodes.
|
5476
5578
|
for(var i = 0, ii = $linkNode.length; i<ii; i++) {
|
5477
|
-
var node = $linkNode[i]
|
5478
|
-
|
5579
|
+
var node = $linkNode[i],
|
5580
|
+
nodeType = node.nodeType;
|
5581
|
+
if (nodeType === 1 /* element */ || nodeType === 9 /* document */) {
|
5479
5582
|
$linkNode.eq(i).data('$scope', scope);
|
5480
5583
|
}
|
5481
5584
|
}
|
5482
|
-
|
5585
|
+
|
5483
5586
|
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
|
5484
5587
|
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
|
5485
5588
|
return $linkNode;
|
@@ -5507,15 +5610,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5507
5610
|
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
|
5508
5611
|
* the rootElement must be set the jqLite collection of the compile root. This is
|
5509
5612
|
* needed so that the jqLite collection items can be replaced with widgets.
|
5510
|
-
* @param {number=}
|
5613
|
+
* @param {number=} maxPriority Max directive priority.
|
5511
5614
|
* @returns {?function} A composite linking function of all of the matched directives or null.
|
5512
5615
|
*/
|
5513
5616
|
function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
|
5514
5617
|
previousCompileContext) {
|
5515
5618
|
var linkFns = [],
|
5516
|
-
|
5619
|
+
attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound;
|
5517
5620
|
|
5518
|
-
for(var i = 0; i < nodeList.length; i++) {
|
5621
|
+
for (var i = 0; i < nodeList.length; i++) {
|
5519
5622
|
attrs = new Attributes();
|
5520
5623
|
|
5521
5624
|
// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
|
@@ -5527,16 +5630,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5527
5630
|
null, [], [], previousCompileContext)
|
5528
5631
|
: null;
|
5529
5632
|
|
5633
|
+
if (nodeLinkFn && nodeLinkFn.scope) {
|
5634
|
+
safeAddClass(jqLite(nodeList[i]), 'ng-scope');
|
5635
|
+
}
|
5636
|
+
|
5530
5637
|
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
|
5531
|
-
!nodeList[i].childNodes ||
|
5532
|
-
!
|
5638
|
+
!(childNodes = nodeList[i].childNodes) ||
|
5639
|
+
!childNodes.length)
|
5533
5640
|
? null
|
5534
|
-
: compileNodes(
|
5641
|
+
: compileNodes(childNodes,
|
5535
5642
|
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
|
5536
5643
|
|
5537
|
-
linkFns.push(nodeLinkFn);
|
5538
|
-
|
5539
|
-
linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
|
5644
|
+
linkFns.push(nodeLinkFn, childLinkFn);
|
5645
|
+
linkFnFound = linkFnFound || nodeLinkFn || childLinkFn;
|
5540
5646
|
//use the previous context only for the first element in the virtual group
|
5541
5647
|
previousCompileContext = null;
|
5542
5648
|
}
|
@@ -5548,9 +5654,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5548
5654
|
var nodeLinkFn, childLinkFn, node, $node, childScope, childTranscludeFn, i, ii, n;
|
5549
5655
|
|
5550
5656
|
// copy nodeList so that linking doesn't break due to live list updates.
|
5551
|
-
var
|
5552
|
-
|
5553
|
-
|
5657
|
+
var nodeListLength = nodeList.length,
|
5658
|
+
stableNodeList = new Array(nodeListLength);
|
5659
|
+
for (i = 0; i < nodeListLength; i++) {
|
5660
|
+
stableNodeList[i] = nodeList[i];
|
5554
5661
|
}
|
5555
5662
|
|
5556
5663
|
for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
|
@@ -5563,7 +5670,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5563
5670
|
if (nodeLinkFn.scope) {
|
5564
5671
|
childScope = scope.$new();
|
5565
5672
|
$node.data('$scope', childScope);
|
5566
|
-
safeAddClass($node, 'ng-scope');
|
5567
5673
|
} else {
|
5568
5674
|
childScope = scope;
|
5569
5675
|
}
|
@@ -5646,9 +5752,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5646
5752
|
|
5647
5753
|
nName = directiveNormalize(name.toLowerCase());
|
5648
5754
|
attrsMap[nName] = name;
|
5649
|
-
attrs[nName] = value = trim(
|
5650
|
-
? decodeURIComponent(node.getAttribute(name, 2))
|
5651
|
-
: attr.value);
|
5755
|
+
attrs[nName] = value = trim(attr.value);
|
5652
5756
|
if (getBooleanAttrName(node, nName)) {
|
5653
5757
|
attrs[nName] = true; // presence means true
|
5654
5758
|
}
|
@@ -5777,7 +5881,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5777
5881
|
templateDirective = previousCompileContext.templateDirective,
|
5778
5882
|
nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
|
5779
5883
|
hasTranscludeDirective = false,
|
5780
|
-
hasElementTranscludeDirective =
|
5884
|
+
hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
|
5781
5885
|
$compileNode = templateAttrs.$$element = jqLite(compileNode),
|
5782
5886
|
directive,
|
5783
5887
|
directiveName,
|
@@ -5831,7 +5935,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5831
5935
|
hasTranscludeDirective = true;
|
5832
5936
|
|
5833
5937
|
// Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
|
5834
|
-
// This option should only be used by directives that know how to
|
5938
|
+
// This option should only be used by directives that know how to safely handle element transclusion,
|
5835
5939
|
// where the transcluded nodes are added or replaced after linking.
|
5836
5940
|
if (!directive.$$tlb) {
|
5837
5941
|
assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
|
@@ -5878,9 +5982,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5878
5982
|
|
5879
5983
|
if (directive.replace) {
|
5880
5984
|
replaceDirective = directive;
|
5881
|
-
$template =
|
5882
|
-
trim(directiveValue) +
|
5883
|
-
'</div>').contents();
|
5985
|
+
$template = directiveTemplateContents(directiveValue);
|
5884
5986
|
compileNode = $template[0];
|
5885
5987
|
|
5886
5988
|
if ($template.length != 1 || compileNode.nodeType !== 1) {
|
@@ -5951,6 +6053,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5951
6053
|
|
5952
6054
|
nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
|
5953
6055
|
nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
|
6056
|
+
previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
|
5954
6057
|
|
5955
6058
|
// might be normal or delayed nodeLinkFn depending on if templateUrl is present
|
5956
6059
|
return nodeLinkFn;
|
@@ -6278,6 +6381,28 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6278
6381
|
}
|
6279
6382
|
|
6280
6383
|
|
6384
|
+
function directiveTemplateContents(template) {
|
6385
|
+
var type;
|
6386
|
+
template = trim(template);
|
6387
|
+
if ((type = TABLE_CONTENT_REGEXP.exec(template))) {
|
6388
|
+
type = type[1].toLowerCase();
|
6389
|
+
var table = jqLite('<table>' + template + '</table>'),
|
6390
|
+
tbody = table.children('tbody'),
|
6391
|
+
leaf = /(td|th)/.test(type) && table.find('tr');
|
6392
|
+
if (tbody.length && type !== 'tbody') {
|
6393
|
+
table = tbody;
|
6394
|
+
}
|
6395
|
+
if (leaf && leaf.length) {
|
6396
|
+
table = leaf;
|
6397
|
+
}
|
6398
|
+
return table.contents();
|
6399
|
+
}
|
6400
|
+
return jqLite('<div>' +
|
6401
|
+
template +
|
6402
|
+
'</div>').contents();
|
6403
|
+
}
|
6404
|
+
|
6405
|
+
|
6281
6406
|
function compileTemplateUrl(directives, $compileNode, tAttrs,
|
6282
6407
|
$rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
|
6283
6408
|
var linkQueue = [],
|
@@ -6302,7 +6427,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6302
6427
|
content = denormalizeTemplate(content);
|
6303
6428
|
|
6304
6429
|
if (origAsyncDirective.replace) {
|
6305
|
-
$template =
|
6430
|
+
$template = directiveTemplateContents(content);
|
6306
6431
|
compileNode = $template[0];
|
6307
6432
|
|
6308
6433
|
if ($template.length != 1 || compileNode.nodeType !== 1) {
|
@@ -6346,9 +6471,18 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6346
6471
|
linkNode = $compileNode[0];
|
6347
6472
|
|
6348
6473
|
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
|
6349
|
-
|
6350
|
-
|
6474
|
+
var oldClasses = beforeTemplateLinkNode.className;
|
6475
|
+
|
6476
|
+
if (!(previousCompileContext.hasElementTranscludeDirective &&
|
6477
|
+
origAsyncDirective.replace)) {
|
6478
|
+
// it was cloned therefore we have to clone as well.
|
6479
|
+
linkNode = jqLiteClone(compileNode);
|
6480
|
+
}
|
6481
|
+
|
6351
6482
|
replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
|
6483
|
+
|
6484
|
+
// Copy in CSS classes from original node
|
6485
|
+
safeAddClass(jqLite(linkNode), oldClasses);
|
6352
6486
|
}
|
6353
6487
|
if (afterTemplateNodeLinkFn.transclude) {
|
6354
6488
|
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude);
|
@@ -6596,7 +6730,7 @@ function directiveNormalize(name) {
|
|
6596
6730
|
*
|
6597
6731
|
*
|
6598
6732
|
* @param {string} name Normalized element attribute name of the property to modify. The name is
|
6599
|
-
*
|
6733
|
+
* reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
|
6600
6734
|
* property to the original name.
|
6601
6735
|
* @param {string} value Value to set the attribute to. The value can be an interpolated string.
|
6602
6736
|
*/
|
@@ -6734,8 +6868,7 @@ function $ControllerProvider() {
|
|
6734
6868
|
* @requires $window
|
6735
6869
|
*
|
6736
6870
|
* @description
|
6737
|
-
* A {@link angular.element jQuery
|
6738
|
-
* element.
|
6871
|
+
* A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
|
6739
6872
|
*/
|
6740
6873
|
function $DocumentProvider(){
|
6741
6874
|
this.$get = ['$window', function(window){
|
@@ -6894,9 +7027,9 @@ function $HttpProvider() {
|
|
6894
7027
|
common: {
|
6895
7028
|
'Accept': 'application/json, text/plain, */*'
|
6896
7029
|
},
|
6897
|
-
post: CONTENT_TYPE_APPLICATION_JSON,
|
6898
|
-
put: CONTENT_TYPE_APPLICATION_JSON,
|
6899
|
-
patch: CONTENT_TYPE_APPLICATION_JSON
|
7030
|
+
post: copy(CONTENT_TYPE_APPLICATION_JSON),
|
7031
|
+
put: copy(CONTENT_TYPE_APPLICATION_JSON),
|
7032
|
+
patch: copy(CONTENT_TYPE_APPLICATION_JSON)
|
6900
7033
|
},
|
6901
7034
|
|
6902
7035
|
xsrfCookieName: 'XSRF-TOKEN',
|
@@ -7005,32 +7138,15 @@ function $HttpProvider() {
|
|
7005
7138
|
* will result in the success callback being called. Note that if the response is a redirect,
|
7006
7139
|
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
|
7007
7140
|
* called for such responses.
|
7008
|
-
*
|
7009
|
-
* # Calling $http from outside AngularJS
|
7010
|
-
* The `$http` service will not actually send the request until the next `$digest()` is
|
7011
|
-
* executed. Normally this is not an issue, since almost all the time your call to `$http` will
|
7012
|
-
* be from within a `$apply()` block.
|
7013
|
-
* If you are calling `$http` from outside Angular, then you should wrap it in a call to
|
7014
|
-
* `$apply` to cause a $digest to occur and also to handle errors in the block correctly.
|
7015
|
-
*
|
7016
|
-
* ```
|
7017
|
-
* $scope.$apply(function() {
|
7018
|
-
* $http(...);
|
7019
|
-
* });
|
7020
|
-
* ```
|
7021
7141
|
*
|
7022
7142
|
* # Writing Unit Tests that use $http
|
7023
|
-
* When unit testing
|
7024
|
-
*
|
7025
|
-
*
|
7026
|
-
* code that calls the `$http()` method inside a $apply block as explained in the previous
|
7027
|
-
* section.
|
7143
|
+
* When unit testing (using {@link api/ngMock ngMock}), it is necessary to call
|
7144
|
+
* {@link api/ngMock.$httpBackend#methods_flush $httpBackend.flush()} to flush each pending
|
7145
|
+
* request using trained responses.
|
7028
7146
|
*
|
7029
7147
|
* ```
|
7030
7148
|
* $httpBackend.expectGET(...);
|
7031
|
-
* $
|
7032
|
-
* $http.get(...);
|
7033
|
-
* });
|
7149
|
+
* $http.get(...);
|
7034
7150
|
* $httpBackend.flush();
|
7035
7151
|
* ```
|
7036
7152
|
*
|
@@ -7074,7 +7190,15 @@ function $HttpProvider() {
|
|
7074
7190
|
* `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.
|
7075
7191
|
*
|
7076
7192
|
* The defaults can also be set at runtime via the `$http.defaults` object in the same
|
7077
|
-
* fashion.
|
7193
|
+
* fashion. For example:
|
7194
|
+
*
|
7195
|
+
* ```
|
7196
|
+
* module.run(function($http) {
|
7197
|
+
* $http.defaults.headers.common.Authentication = 'Basic YmVlcDpib29w'
|
7198
|
+
* });
|
7199
|
+
* ```
|
7200
|
+
*
|
7201
|
+
* In addition, you can supply a `headers` property in the config object passed when
|
7078
7202
|
* calling `$http(config)`, which overrides the defaults without changing them globally.
|
7079
7203
|
*
|
7080
7204
|
*
|
@@ -7098,7 +7222,9 @@ function $HttpProvider() {
|
|
7098
7222
|
* properties. These properties are by default an array of transform functions, which allows you
|
7099
7223
|
* to `push` or `unshift` a new transformation function into the transformation chain. You can
|
7100
7224
|
* also decide to completely override any default transformations by assigning your
|
7101
|
-
* transformation functions to these properties directly without the array wrapper.
|
7225
|
+
* transformation functions to these properties directly without the array wrapper. These defaults
|
7226
|
+
* are again available on the $http factory at run-time, which may be useful if you have run-time
|
7227
|
+
* services you wish to be involved in your transformations.
|
7102
7228
|
*
|
7103
7229
|
* Similarly, to locally override the request/response transforms, augment the
|
7104
7230
|
* `transformRequest` and/or `transformResponse` properties of the configuration object passed
|
@@ -7192,19 +7318,20 @@ function $HttpProvider() {
|
|
7192
7318
|
* return responseOrNewPromise
|
7193
7319
|
* }
|
7194
7320
|
* return $q.reject(rejection);
|
7195
|
-
* }
|
7196
|
-
* }
|
7321
|
+
* }
|
7322
|
+
* };
|
7197
7323
|
* });
|
7198
7324
|
*
|
7199
7325
|
* $httpProvider.interceptors.push('myHttpInterceptor');
|
7200
7326
|
*
|
7201
7327
|
*
|
7202
|
-
* // register the interceptor via an anonymous factory
|
7328
|
+
* // alternatively, register the interceptor via an anonymous factory
|
7203
7329
|
* $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
|
7204
7330
|
* return {
|
7205
7331
|
* 'request': function(config) {
|
7206
7332
|
* // same as above
|
7207
7333
|
* },
|
7334
|
+
*
|
7208
7335
|
* 'response': function(response) {
|
7209
7336
|
* // same as above
|
7210
7337
|
* }
|
@@ -7311,7 +7438,8 @@ function $HttpProvider() {
|
|
7311
7438
|
* for added security.
|
7312
7439
|
*
|
7313
7440
|
* The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
|
7314
|
-
* properties of either $httpProvider.defaults,
|
7441
|
+
* properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
|
7442
|
+
* or the per-request config object.
|
7315
7443
|
*
|
7316
7444
|
*
|
7317
7445
|
* @param {object} config Object describing the request to be made and how it should be
|
@@ -7375,14 +7503,14 @@ function $HttpProvider() {
|
|
7375
7503
|
<option>JSONP</option>
|
7376
7504
|
</select>
|
7377
7505
|
<input type="text" ng-model="url" size="80"/>
|
7378
|
-
<button ng-click="fetch()">fetch</button><br>
|
7379
|
-
<button ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
|
7380
|
-
<button
|
7506
|
+
<button id="fetchbtn" ng-click="fetch()">fetch</button><br>
|
7507
|
+
<button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
|
7508
|
+
<button id="samplejsonpbtn"
|
7381
7509
|
ng-click="updateModel('JSONP',
|
7382
7510
|
'http://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
|
7383
7511
|
Sample JSONP
|
7384
7512
|
</button>
|
7385
|
-
<button
|
7513
|
+
<button id="invalidjsonpbtn"
|
7386
7514
|
ng-click="updateModel('JSONP', 'http://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
|
7387
7515
|
Invalid JSONP
|
7388
7516
|
</button>
|
@@ -7419,27 +7547,34 @@ function $HttpProvider() {
|
|
7419
7547
|
<file name="http-hello.html">
|
7420
7548
|
Hello, $http!
|
7421
7549
|
</file>
|
7422
|
-
<file name="
|
7550
|
+
<file name="protractorTest.js">
|
7551
|
+
var status = element(by.binding('status'));
|
7552
|
+
var data = element(by.binding('data'));
|
7553
|
+
var fetchBtn = element(by.id('fetchbtn'));
|
7554
|
+
var sampleGetBtn = element(by.id('samplegetbtn'));
|
7555
|
+
var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
|
7556
|
+
var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
|
7557
|
+
|
7423
7558
|
it('should make an xhr GET request', function() {
|
7424
|
-
|
7425
|
-
|
7426
|
-
expect(
|
7427
|
-
expect(
|
7559
|
+
sampleGetBtn.click();
|
7560
|
+
fetchBtn.click();
|
7561
|
+
expect(status.getText()).toMatch('200');
|
7562
|
+
expect(data.getText()).toMatch(/Hello, \$http!/)
|
7428
7563
|
});
|
7429
7564
|
|
7430
7565
|
it('should make a JSONP request to angularjs.org', function() {
|
7431
|
-
|
7432
|
-
|
7433
|
-
expect(
|
7434
|
-
expect(
|
7566
|
+
sampleJsonpBtn.click();
|
7567
|
+
fetchBtn.click();
|
7568
|
+
expect(status.getText()).toMatch('200');
|
7569
|
+
expect(data.getText()).toMatch(/Super Hero!/);
|
7435
7570
|
});
|
7436
7571
|
|
7437
7572
|
it('should make JSONP request to invalid URL and invoke the error handler',
|
7438
7573
|
function() {
|
7439
|
-
|
7440
|
-
|
7441
|
-
expect(
|
7442
|
-
expect(
|
7574
|
+
invalidJsonpBtn.click();
|
7575
|
+
fetchBtn.click();
|
7576
|
+
expect(status.getText()).toMatch('0');
|
7577
|
+
expect(data.getText()).toMatch('Request failed');
|
7443
7578
|
});
|
7444
7579
|
</file>
|
7445
7580
|
</example>
|
@@ -7820,14 +7955,19 @@ function $HttpProvider() {
|
|
7820
7955
|
}];
|
7821
7956
|
}
|
7822
7957
|
|
7823
|
-
|
7824
|
-
|
7825
|
-
|
7826
|
-
|
7827
|
-
|
7828
|
-
|
7829
|
-
|
7958
|
+
function createXhr(method) {
|
7959
|
+
//if IE and the method is not RFC2616 compliant, or if XMLHttpRequest
|
7960
|
+
//is not available, try getting an ActiveXObject. Otherwise, use XMLHttpRequest
|
7961
|
+
//if it is available
|
7962
|
+
if (msie <= 8 && (!method.match(/^(get|post|head|put|delete|options)$/i) ||
|
7963
|
+
!window.XMLHttpRequest)) {
|
7964
|
+
return new window.ActiveXObject("Microsoft.XMLHTTP");
|
7965
|
+
} else if (window.XMLHttpRequest) {
|
7966
|
+
return new window.XMLHttpRequest();
|
7967
|
+
}
|
7830
7968
|
|
7969
|
+
throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest.");
|
7970
|
+
}
|
7831
7971
|
|
7832
7972
|
/**
|
7833
7973
|
* @ngdoc object
|
@@ -7848,11 +7988,11 @@ var XHR = window.XMLHttpRequest || function() {
|
|
7848
7988
|
*/
|
7849
7989
|
function $HttpBackendProvider() {
|
7850
7990
|
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
|
7851
|
-
return createHttpBackend($browser,
|
7991
|
+
return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
|
7852
7992
|
}];
|
7853
7993
|
}
|
7854
7994
|
|
7855
|
-
function createHttpBackend($browser,
|
7995
|
+
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
|
7856
7996
|
var ABORTED = -1;
|
7857
7997
|
|
7858
7998
|
// TODO(vojta): fix the signature
|
@@ -7874,10 +8014,12 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument)
|
|
7874
8014
|
} else {
|
7875
8015
|
completeRequest(callback, status || -2);
|
7876
8016
|
}
|
7877
|
-
|
8017
|
+
callbacks[callbackId] = angular.noop;
|
7878
8018
|
});
|
7879
8019
|
} else {
|
7880
|
-
|
8020
|
+
|
8021
|
+
var xhr = createXhr(method);
|
8022
|
+
|
7881
8023
|
xhr.open(method, url, true);
|
7882
8024
|
forEach(headers, function(value, key) {
|
7883
8025
|
if (isDefined(value)) {
|
@@ -7889,17 +8031,25 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument)
|
|
7889
8031
|
// response is in the cache. the promise api will ensure that to the app code the api is
|
7890
8032
|
// always async
|
7891
8033
|
xhr.onreadystatechange = function() {
|
7892
|
-
|
8034
|
+
// onreadystatechange might get called multiple times with readyState === 4 on mobile webkit caused by
|
8035
|
+
// xhrs that are resolved while the app is in the background (see #5426).
|
8036
|
+
// since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before
|
8037
|
+
// continuing
|
8038
|
+
//
|
8039
|
+
// we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and
|
8040
|
+
// Safari respectively.
|
8041
|
+
if (xhr && xhr.readyState == 4) {
|
7893
8042
|
var responseHeaders = null,
|
7894
8043
|
response = null;
|
7895
8044
|
|
7896
8045
|
if(status !== ABORTED) {
|
7897
8046
|
responseHeaders = xhr.getAllResponseHeaders();
|
7898
|
-
|
8047
|
+
|
8048
|
+
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
|
8049
|
+
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
|
8050
|
+
response = ('response' in xhr) ? xhr.response : xhr.responseText;
|
7899
8051
|
}
|
7900
8052
|
|
7901
|
-
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
|
7902
|
-
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
|
7903
8053
|
completeRequest(callback,
|
7904
8054
|
status || xhr.status,
|
7905
8055
|
response,
|
@@ -7912,7 +8062,20 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument)
|
|
7912
8062
|
}
|
7913
8063
|
|
7914
8064
|
if (responseType) {
|
7915
|
-
|
8065
|
+
try {
|
8066
|
+
xhr.responseType = responseType;
|
8067
|
+
} catch (e) {
|
8068
|
+
// WebKit added support for the json responseType value on 09/03/2013
|
8069
|
+
// https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
|
8070
|
+
// known to throw when setting the value "json" as the response type. Other older
|
8071
|
+
// browsers implementing the responseType
|
8072
|
+
//
|
8073
|
+
// The json response type can be ignored if not supported, because JSON payloads are
|
8074
|
+
// parsed on the client-side regardless.
|
8075
|
+
if (responseType !== 'json') {
|
8076
|
+
throw e;
|
8077
|
+
}
|
8078
|
+
}
|
7916
8079
|
}
|
7917
8080
|
|
7918
8081
|
xhr.send(post || null);
|
@@ -7932,14 +8095,14 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument)
|
|
7932
8095
|
}
|
7933
8096
|
|
7934
8097
|
function completeRequest(callback, status, response, headersString) {
|
7935
|
-
var protocol = urlResolve(url).protocol;
|
7936
|
-
|
7937
8098
|
// cancel timeout and subsequent timeout promise resolution
|
7938
8099
|
timeoutId && $browserDefer.cancel(timeoutId);
|
7939
8100
|
jsonpDone = xhr = null;
|
7940
8101
|
|
7941
|
-
// fix status code
|
7942
|
-
|
8102
|
+
// fix status code when it is 0 (0 status is undocumented).
|
8103
|
+
// Occurs when accessing file resources.
|
8104
|
+
// On Android 4.1 stock browser it occurs while retrieving files from application cache.
|
8105
|
+
status = (status === 0) ? (response ? 200 : 404) : status;
|
7943
8106
|
|
7944
8107
|
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
|
7945
8108
|
status = status == 1223 ? 204 : status;
|
@@ -8011,11 +8174,11 @@ var $interpolateMinErr = minErr('$interpolate');
|
|
8011
8174
|
//demo.label//
|
8012
8175
|
</div>
|
8013
8176
|
</doc:source>
|
8014
|
-
<doc:
|
8015
|
-
|
8016
|
-
|
8017
|
-
|
8018
|
-
</doc:
|
8177
|
+
<doc:protractor>
|
8178
|
+
it('should interpolate binding with custom symbols', function() {
|
8179
|
+
expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
|
8180
|
+
});
|
8181
|
+
</doc:protractor>
|
8019
8182
|
</doc:example>
|
8020
8183
|
*/
|
8021
8184
|
function $InterpolateProvider() {
|
@@ -8207,7 +8370,7 @@ function $InterpolateProvider() {
|
|
8207
8370
|
* @description
|
8208
8371
|
* Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
|
8209
8372
|
*
|
8210
|
-
* Use {@link ng.$interpolateProvider#
|
8373
|
+
* Use {@link ng.$interpolateProvider#methods_endSymbol $interpolateProvider#endSymbol} to change
|
8211
8374
|
* the symbol.
|
8212
8375
|
*
|
8213
8376
|
* @returns {string} start symbol.
|
@@ -8244,6 +8407,14 @@ function $IntervalProvider() {
|
|
8244
8407
|
* move forward by `millis` milliseconds and trigger any functions scheduled to run in that
|
8245
8408
|
* time.
|
8246
8409
|
*
|
8410
|
+
* <div class="alert alert-warning">
|
8411
|
+
* **Note**: Intervals created by this service must be explicitly destroyed when you are finished
|
8412
|
+
* with them. In particular they are not automatically destroyed when a controller's scope or a
|
8413
|
+
* directive's element are destroyed.
|
8414
|
+
* You should take this into consideration and make sure to always cancel the interval at the
|
8415
|
+
* appropriate moment. See the example below for more details on how and when to do this.
|
8416
|
+
* </div>
|
8417
|
+
*
|
8247
8418
|
* @param {function()} fn A function that should be called repeatedly.
|
8248
8419
|
* @param {number} delay Number of milliseconds between each function call.
|
8249
8420
|
* @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
|
@@ -8251,6 +8422,95 @@ function $IntervalProvider() {
|
|
8251
8422
|
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
|
8252
8423
|
* will invoke `fn` within the {@link ng.$rootScope.Scope#methods_$apply $apply} block.
|
8253
8424
|
* @returns {promise} A promise which will be notified on each iteration.
|
8425
|
+
*
|
8426
|
+
* @example
|
8427
|
+
<doc:example module="time">
|
8428
|
+
<doc:source>
|
8429
|
+
<script>
|
8430
|
+
function Ctrl2($scope,$interval) {
|
8431
|
+
$scope.format = 'M/d/yy h:mm:ss a';
|
8432
|
+
$scope.blood_1 = 100;
|
8433
|
+
$scope.blood_2 = 120;
|
8434
|
+
|
8435
|
+
var stop;
|
8436
|
+
$scope.fight = function() {
|
8437
|
+
// Don't start a new fight if we are already fighting
|
8438
|
+
if ( angular.isDefined(stop) ) return;
|
8439
|
+
|
8440
|
+
stop = $interval(function() {
|
8441
|
+
if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
|
8442
|
+
$scope.blood_1 = $scope.blood_1 - 3;
|
8443
|
+
$scope.blood_2 = $scope.blood_2 - 4;
|
8444
|
+
} else {
|
8445
|
+
$scope.stopFight();
|
8446
|
+
}
|
8447
|
+
}, 100);
|
8448
|
+
};
|
8449
|
+
|
8450
|
+
$scope.stopFight = function() {
|
8451
|
+
if (angular.isDefined(stop)) {
|
8452
|
+
$interval.cancel(stop);
|
8453
|
+
stop = undefined;
|
8454
|
+
}
|
8455
|
+
};
|
8456
|
+
|
8457
|
+
$scope.resetFight = function() {
|
8458
|
+
$scope.blood_1 = 100;
|
8459
|
+
$scope.blood_2 = 120;
|
8460
|
+
}
|
8461
|
+
|
8462
|
+
$scope.$on('$destroy', function() {
|
8463
|
+
// Make sure that the interval is destroyed too
|
8464
|
+
$scope.stopFight();
|
8465
|
+
});
|
8466
|
+
}
|
8467
|
+
|
8468
|
+
angular.module('time', [])
|
8469
|
+
// Register the 'myCurrentTime' directive factory method.
|
8470
|
+
// We inject $interval and dateFilter service since the factory method is DI.
|
8471
|
+
.directive('myCurrentTime', function($interval, dateFilter) {
|
8472
|
+
// return the directive link function. (compile function not needed)
|
8473
|
+
return function(scope, element, attrs) {
|
8474
|
+
var format, // date format
|
8475
|
+
stopTime; // so that we can cancel the time updates
|
8476
|
+
|
8477
|
+
// used to update the UI
|
8478
|
+
function updateTime() {
|
8479
|
+
element.text(dateFilter(new Date(), format));
|
8480
|
+
}
|
8481
|
+
|
8482
|
+
// watch the expression, and update the UI on change.
|
8483
|
+
scope.$watch(attrs.myCurrentTime, function(value) {
|
8484
|
+
format = value;
|
8485
|
+
updateTime();
|
8486
|
+
});
|
8487
|
+
|
8488
|
+
stopTime = $interval(updateTime, 1000);
|
8489
|
+
|
8490
|
+
// listen on DOM destroy (removal) event, and cancel the next UI update
|
8491
|
+
// to prevent updating time ofter the DOM element was removed.
|
8492
|
+
element.bind('$destroy', function() {
|
8493
|
+
$interval.cancel(stopTime);
|
8494
|
+
});
|
8495
|
+
}
|
8496
|
+
});
|
8497
|
+
</script>
|
8498
|
+
|
8499
|
+
<div>
|
8500
|
+
<div ng-controller="Ctrl2">
|
8501
|
+
Date format: <input ng-model="format"> <hr/>
|
8502
|
+
Current time is: <span my-current-time="format"></span>
|
8503
|
+
<hr/>
|
8504
|
+
Blood 1 : <font color='red'>{{blood_1}}</font>
|
8505
|
+
Blood 2 : <font color='red'>{{blood_2}}</font>
|
8506
|
+
<button type="button" data-ng-click="fight()">Fight</button>
|
8507
|
+
<button type="button" data-ng-click="stopFight()">StopFight</button>
|
8508
|
+
<button type="button" data-ng-click="resetFight()">resetFight</button>
|
8509
|
+
</div>
|
8510
|
+
</div>
|
8511
|
+
|
8512
|
+
</doc:source>
|
8513
|
+
</doc:example>
|
8254
8514
|
*/
|
8255
8515
|
function interval(fn, delay, count, invokeApply) {
|
8256
8516
|
var setInterval = $window.setInterval,
|
@@ -8259,8 +8519,8 @@ function $IntervalProvider() {
|
|
8259
8519
|
promise = deferred.promise,
|
8260
8520
|
iteration = 0,
|
8261
8521
|
skipApply = (isDefined(invokeApply) && !invokeApply);
|
8262
|
-
|
8263
|
-
count = isDefined(count) ? count : 0
|
8522
|
+
|
8523
|
+
count = isDefined(count) ? count : 0;
|
8264
8524
|
|
8265
8525
|
promise.then(null, null, fn);
|
8266
8526
|
|
@@ -8954,9 +9214,9 @@ function $LocationProvider(){
|
|
8954
9214
|
* @eventType broadcast on root scope
|
8955
9215
|
* @description
|
8956
9216
|
* Broadcasted before a URL will change. This change can be prevented by calling
|
8957
|
-
* `preventDefault` method of the event. See {@link ng.$rootScope.Scope
|
9217
|
+
* `preventDefault` method of the event. See {@link ng.$rootScope.Scope#methods_$on} for more
|
8958
9218
|
* details about event object. Upon successful change
|
8959
|
-
* {@link ng.$location
|
9219
|
+
* {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired.
|
8960
9220
|
*
|
8961
9221
|
* @param {Object} angularEvent Synthetic event object.
|
8962
9222
|
* @param {string} newUrl New URL
|
@@ -9009,6 +9269,13 @@ function $LocationProvider(){
|
|
9009
9269
|
}
|
9010
9270
|
|
9011
9271
|
var absHref = elm.prop('href');
|
9272
|
+
|
9273
|
+
if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
|
9274
|
+
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
|
9275
|
+
// an animation.
|
9276
|
+
absHref = urlResolve(absHref.animVal).href;
|
9277
|
+
}
|
9278
|
+
|
9012
9279
|
var rewrittenUrl = $location.$$rewrite(absHref);
|
9013
9280
|
|
9014
9281
|
if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
|
@@ -9032,16 +9299,17 @@ function $LocationProvider(){
|
|
9032
9299
|
// update $location when $browser url changes
|
9033
9300
|
$browser.onUrlChange(function(newUrl) {
|
9034
9301
|
if ($location.absUrl() != newUrl) {
|
9035
|
-
if ($rootScope.$broadcast('$locationChangeStart', newUrl,
|
9036
|
-
$location.absUrl()).defaultPrevented) {
|
9037
|
-
$browser.url($location.absUrl());
|
9038
|
-
return;
|
9039
|
-
}
|
9040
9302
|
$rootScope.$evalAsync(function() {
|
9041
9303
|
var oldUrl = $location.absUrl();
|
9042
9304
|
|
9043
9305
|
$location.$$parse(newUrl);
|
9044
|
-
|
9306
|
+
if ($rootScope.$broadcast('$locationChangeStart', newUrl,
|
9307
|
+
oldUrl).defaultPrevented) {
|
9308
|
+
$location.$$parse(oldUrl);
|
9309
|
+
$browser.url(oldUrl);
|
9310
|
+
} else {
|
9311
|
+
afterLocationChange(oldUrl);
|
9312
|
+
}
|
9045
9313
|
});
|
9046
9314
|
if (!$rootScope.$$phase) $rootScope.$digest();
|
9047
9315
|
}
|
@@ -9129,7 +9397,7 @@ function $LogProvider(){
|
|
9129
9397
|
* @name ng.$logProvider#debugEnabled
|
9130
9398
|
* @methodOf ng.$logProvider
|
9131
9399
|
* @description
|
9132
|
-
* @param {
|
9400
|
+
* @param {boolean=} flag enable or disable debug level messages
|
9133
9401
|
* @returns {*} current value if used as getter or itself (chaining) if used as setter
|
9134
9402
|
*/
|
9135
9403
|
this.debugEnabled = function(flag) {
|
@@ -9217,9 +9485,16 @@ function $LogProvider(){
|
|
9217
9485
|
|
9218
9486
|
function consoleLog(type) {
|
9219
9487
|
var console = $window.console || {},
|
9220
|
-
logFn = console[type] || console.log || noop
|
9488
|
+
logFn = console[type] || console.log || noop,
|
9489
|
+
hasApply = false;
|
9490
|
+
|
9491
|
+
// Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
|
9492
|
+
// The reason behind this is that console.log has type "object" in IE8...
|
9493
|
+
try {
|
9494
|
+
hasApply = !! logFn.apply;
|
9495
|
+
} catch (e) {}
|
9221
9496
|
|
9222
|
-
if (
|
9497
|
+
if (hasApply) {
|
9223
9498
|
return function() {
|
9224
9499
|
var args = [];
|
9225
9500
|
forEach(arguments, function(arg) {
|
@@ -9945,7 +10220,7 @@ Parser.prototype = {
|
|
9945
10220
|
var getter = getterFn(field, this.options, this.text);
|
9946
10221
|
|
9947
10222
|
return extend(function(scope, locals, self) {
|
9948
|
-
return getter(self || object(scope, locals)
|
10223
|
+
return getter(self || object(scope, locals));
|
9949
10224
|
}, {
|
9950
10225
|
assign: function(scope, value, locals) {
|
9951
10226
|
return setter(object(scope, locals), field, value, parser.text, parser.options);
|
@@ -10129,19 +10404,23 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
|
10129
10404
|
? function cspSafeGetter(scope, locals) {
|
10130
10405
|
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
|
10131
10406
|
|
10132
|
-
if (pathVal
|
10407
|
+
if (pathVal == null) return pathVal;
|
10133
10408
|
pathVal = pathVal[key0];
|
10134
10409
|
|
10135
|
-
if (!key1
|
10410
|
+
if (!key1) return pathVal;
|
10411
|
+
if (pathVal == null) return undefined;
|
10136
10412
|
pathVal = pathVal[key1];
|
10137
10413
|
|
10138
|
-
if (!key2
|
10414
|
+
if (!key2) return pathVal;
|
10415
|
+
if (pathVal == null) return undefined;
|
10139
10416
|
pathVal = pathVal[key2];
|
10140
10417
|
|
10141
|
-
if (!key3
|
10418
|
+
if (!key3) return pathVal;
|
10419
|
+
if (pathVal == null) return undefined;
|
10142
10420
|
pathVal = pathVal[key3];
|
10143
10421
|
|
10144
|
-
if (!key4
|
10422
|
+
if (!key4) return pathVal;
|
10423
|
+
if (pathVal == null) return undefined;
|
10145
10424
|
pathVal = pathVal[key4];
|
10146
10425
|
|
10147
10426
|
return pathVal;
|
@@ -10150,7 +10429,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
|
10150
10429
|
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope,
|
10151
10430
|
promise;
|
10152
10431
|
|
10153
|
-
if (pathVal
|
10432
|
+
if (pathVal == null) return pathVal;
|
10154
10433
|
|
10155
10434
|
pathVal = pathVal[key0];
|
10156
10435
|
if (pathVal && pathVal.then) {
|
@@ -10162,8 +10441,9 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
|
10162
10441
|
}
|
10163
10442
|
pathVal = pathVal.$$v;
|
10164
10443
|
}
|
10165
|
-
if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
|
10166
10444
|
|
10445
|
+
if (!key1) return pathVal;
|
10446
|
+
if (pathVal == null) return undefined;
|
10167
10447
|
pathVal = pathVal[key1];
|
10168
10448
|
if (pathVal && pathVal.then) {
|
10169
10449
|
promiseWarning(fullExp);
|
@@ -10174,8 +10454,9 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
|
10174
10454
|
}
|
10175
10455
|
pathVal = pathVal.$$v;
|
10176
10456
|
}
|
10177
|
-
if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
|
10178
10457
|
|
10458
|
+
if (!key2) return pathVal;
|
10459
|
+
if (pathVal == null) return undefined;
|
10179
10460
|
pathVal = pathVal[key2];
|
10180
10461
|
if (pathVal && pathVal.then) {
|
10181
10462
|
promiseWarning(fullExp);
|
@@ -10186,8 +10467,9 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
|
10186
10467
|
}
|
10187
10468
|
pathVal = pathVal.$$v;
|
10188
10469
|
}
|
10189
|
-
if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
|
10190
10470
|
|
10471
|
+
if (!key3) return pathVal;
|
10472
|
+
if (pathVal == null) return undefined;
|
10191
10473
|
pathVal = pathVal[key3];
|
10192
10474
|
if (pathVal && pathVal.then) {
|
10193
10475
|
promiseWarning(fullExp);
|
@@ -10198,8 +10480,9 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
|
10198
10480
|
}
|
10199
10481
|
pathVal = pathVal.$$v;
|
10200
10482
|
}
|
10201
|
-
if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
|
10202
10483
|
|
10484
|
+
if (!key4) return pathVal;
|
10485
|
+
if (pathVal == null) return undefined;
|
10203
10486
|
pathVal = pathVal[key4];
|
10204
10487
|
if (pathVal && pathVal.then) {
|
10205
10488
|
promiseWarning(fullExp);
|
@@ -10214,6 +10497,26 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
|
10214
10497
|
};
|
10215
10498
|
}
|
10216
10499
|
|
10500
|
+
function simpleGetterFn1(key0, fullExp) {
|
10501
|
+
ensureSafeMemberName(key0, fullExp);
|
10502
|
+
|
10503
|
+
return function simpleGetterFn1(scope, locals) {
|
10504
|
+
if (scope == null) return undefined;
|
10505
|
+
return ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
|
10506
|
+
};
|
10507
|
+
}
|
10508
|
+
|
10509
|
+
function simpleGetterFn2(key0, key1, fullExp) {
|
10510
|
+
ensureSafeMemberName(key0, fullExp);
|
10511
|
+
ensureSafeMemberName(key1, fullExp);
|
10512
|
+
|
10513
|
+
return function simpleGetterFn2(scope, locals) {
|
10514
|
+
if (scope == null) return undefined;
|
10515
|
+
scope = ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
|
10516
|
+
return scope == null ? undefined : scope[key1];
|
10517
|
+
};
|
10518
|
+
}
|
10519
|
+
|
10217
10520
|
function getterFn(path, options, fullExp) {
|
10218
10521
|
// Check whether the cache has this getter already.
|
10219
10522
|
// We can use hasOwnProperty directly on the cache because we ensure,
|
@@ -10226,7 +10529,13 @@ function getterFn(path, options, fullExp) {
|
|
10226
10529
|
pathKeysLength = pathKeys.length,
|
10227
10530
|
fn;
|
10228
10531
|
|
10229
|
-
|
10532
|
+
// When we have only 1 or 2 tokens, use optimized special case closures.
|
10533
|
+
// http://jsperf.com/angularjs-parse-getter/6
|
10534
|
+
if (!options.unwrapPromises && pathKeysLength === 1) {
|
10535
|
+
fn = simpleGetterFn1(pathKeys[0], fullExp);
|
10536
|
+
} else if (!options.unwrapPromises && pathKeysLength === 2) {
|
10537
|
+
fn = simpleGetterFn2(pathKeys[0], pathKeys[1], fullExp);
|
10538
|
+
} else if (options.csp) {
|
10230
10539
|
if (pathKeysLength < 6) {
|
10231
10540
|
fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp,
|
10232
10541
|
options);
|
@@ -10244,11 +10553,10 @@ function getterFn(path, options, fullExp) {
|
|
10244
10553
|
};
|
10245
10554
|
}
|
10246
10555
|
} else {
|
10247
|
-
var code = 'var
|
10556
|
+
var code = 'var p;\n';
|
10248
10557
|
forEach(pathKeys, function(key, index) {
|
10249
10558
|
ensureSafeMemberName(key, fullExp);
|
10250
|
-
code += 'if(s
|
10251
|
-
'l=s;\n' +
|
10559
|
+
code += 'if(s == null) return undefined;\n' +
|
10252
10560
|
's='+ (index
|
10253
10561
|
// we simply dereference 's' on any .dot notation
|
10254
10562
|
? 's'
|
@@ -10271,10 +10579,10 @@ function getterFn(path, options, fullExp) {
|
|
10271
10579
|
/* jshint -W054 */
|
10272
10580
|
var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning
|
10273
10581
|
/* jshint +W054 */
|
10274
|
-
evaledFnGetter.toString =
|
10275
|
-
fn = function(scope, locals) {
|
10582
|
+
evaledFnGetter.toString = valueFn(code);
|
10583
|
+
fn = options.unwrapPromises ? function(scope, locals) {
|
10276
10584
|
return evaledFnGetter(scope, locals, promiseWarning);
|
10277
|
-
};
|
10585
|
+
} : evaledFnGetter;
|
10278
10586
|
}
|
10279
10587
|
|
10280
10588
|
// Only cache the value if it's not going to mess up the cache object
|
@@ -10488,9 +10796,9 @@ function $ParseProvider() {
|
|
10488
10796
|
* asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
|
10489
10797
|
*
|
10490
10798
|
* <pre>
|
10491
|
-
* // for the purpose of this example let's assume that variables `$q` and `
|
10492
|
-
* // available in the current lexical scope (they could have been injected or passed in).
|
10493
|
-
*
|
10799
|
+
* // for the purpose of this example let's assume that variables `$q`, `scope` and `okToGreet`
|
10800
|
+
* // are available in the current lexical scope (they could have been injected or passed in).
|
10801
|
+
*
|
10494
10802
|
* function asyncGreet(name) {
|
10495
10803
|
* var deferred = $q.defer();
|
10496
10804
|
*
|
@@ -10545,7 +10853,7 @@ function $ParseProvider() {
|
|
10545
10853
|
* constructed via `$q.reject`, the promise will be rejected instead.
|
10546
10854
|
* - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
|
10547
10855
|
* resolving it with a rejection constructed via `$q.reject`.
|
10548
|
-
* - `notify(value)` - provides updates on the status of the
|
10856
|
+
* - `notify(value)` - provides updates on the status of the promise's execution. This may be called
|
10549
10857
|
* multiple times before the promise is either resolved or rejected.
|
10550
10858
|
*
|
10551
10859
|
* **Properties**
|
@@ -10695,7 +11003,7 @@ function qFactory(nextTick, exceptionHandler) {
|
|
10695
11003
|
|
10696
11004
|
|
10697
11005
|
reject: function(reason) {
|
10698
|
-
deferred.resolve(
|
11006
|
+
deferred.resolve(createInternalRejectedPromise(reason));
|
10699
11007
|
},
|
10700
11008
|
|
10701
11009
|
|
@@ -10852,6 +11160,12 @@ function qFactory(nextTick, exceptionHandler) {
|
|
10852
11160
|
* @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
|
10853
11161
|
*/
|
10854
11162
|
var reject = function(reason) {
|
11163
|
+
var result = defer();
|
11164
|
+
result.reject(reason);
|
11165
|
+
return result.promise;
|
11166
|
+
};
|
11167
|
+
|
11168
|
+
var createInternalRejectedPromise = function(reason) {
|
10855
11169
|
return {
|
10856
11170
|
then: function(callback, errback) {
|
10857
11171
|
var result = defer();
|
@@ -11119,6 +11433,7 @@ function $RootScopeProvider(){
|
|
11119
11433
|
this.$$asyncQueue = [];
|
11120
11434
|
this.$$postDigestQueue = [];
|
11121
11435
|
this.$$listeners = {};
|
11436
|
+
this.$$listenerCount = {};
|
11122
11437
|
this.$$isolateBindings = {};
|
11123
11438
|
}
|
11124
11439
|
|
@@ -11171,13 +11486,14 @@ function $RootScopeProvider(){
|
|
11171
11486
|
} else {
|
11172
11487
|
ChildScope = function() {}; // should be anonymous; This is so that when the minifier munges
|
11173
11488
|
// the name it does not become random set of chars. This will then show up as class
|
11174
|
-
// name in the
|
11489
|
+
// name in the web inspector.
|
11175
11490
|
ChildScope.prototype = this;
|
11176
11491
|
child = new ChildScope();
|
11177
11492
|
child.$id = nextUid();
|
11178
11493
|
}
|
11179
11494
|
child['this'] = child;
|
11180
11495
|
child.$$listeners = {};
|
11496
|
+
child.$$listenerCount = {};
|
11181
11497
|
child.$parent = this;
|
11182
11498
|
child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
|
11183
11499
|
child.$$prevSibling = this.$$childTail;
|
@@ -11271,7 +11587,7 @@ function $RootScopeProvider(){
|
|
11271
11587
|
// No digest has been run so the counter will be zero
|
11272
11588
|
expect(scope.foodCounter).toEqual(0);
|
11273
11589
|
|
11274
|
-
// Run the digest but since food has not changed
|
11590
|
+
// Run the digest but since food has not changed count will still be zero
|
11275
11591
|
scope.$digest();
|
11276
11592
|
expect(scope.foodCounter).toEqual(0);
|
11277
11593
|
|
@@ -11337,6 +11653,7 @@ function $RootScopeProvider(){
|
|
11337
11653
|
|
11338
11654
|
return function() {
|
11339
11655
|
arrayRemove(array, watcher);
|
11656
|
+
lastDirtyWatch = null;
|
11340
11657
|
};
|
11341
11658
|
},
|
11342
11659
|
|
@@ -11615,7 +11932,7 @@ function $RootScopeProvider(){
|
|
11615
11932
|
|
11616
11933
|
// `break traverseScopesLoop;` takes us to here
|
11617
11934
|
|
11618
|
-
if(dirty && !(ttl--)) {
|
11935
|
+
if((dirty || asyncQueue.length) && !(ttl--)) {
|
11619
11936
|
clearPhase();
|
11620
11937
|
throw $rootScopeMinErr('infdig',
|
11621
11938
|
'{0} $digest() iterations reached. Aborting!\n' +
|
@@ -11682,6 +11999,8 @@ function $RootScopeProvider(){
|
|
11682
11999
|
this.$$destroyed = true;
|
11683
12000
|
if (this === $rootScope) return;
|
11684
12001
|
|
12002
|
+
forEach(this.$$listenerCount, bind(null, decrementListenerCount, this));
|
12003
|
+
|
11685
12004
|
if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
|
11686
12005
|
if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
|
11687
12006
|
if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
|
@@ -11871,8 +12190,18 @@ function $RootScopeProvider(){
|
|
11871
12190
|
}
|
11872
12191
|
namedListeners.push(listener);
|
11873
12192
|
|
12193
|
+
var current = this;
|
12194
|
+
do {
|
12195
|
+
if (!current.$$listenerCount[name]) {
|
12196
|
+
current.$$listenerCount[name] = 0;
|
12197
|
+
}
|
12198
|
+
current.$$listenerCount[name]++;
|
12199
|
+
} while ((current = current.$parent));
|
12200
|
+
|
12201
|
+
var self = this;
|
11874
12202
|
return function() {
|
11875
12203
|
namedListeners[indexOf(namedListeners, listener)] = null;
|
12204
|
+
decrementListenerCount(self, 1, name);
|
11876
12205
|
};
|
11877
12206
|
},
|
11878
12207
|
|
@@ -11897,7 +12226,7 @@ function $RootScopeProvider(){
|
|
11897
12226
|
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
|
11898
12227
|
*
|
11899
12228
|
* @param {string} name Event name to emit.
|
11900
|
-
* @param {...*} args Optional
|
12229
|
+
* @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
|
11901
12230
|
* @return {Object} Event object (see {@link ng.$rootScope.Scope#methods_$on}).
|
11902
12231
|
*/
|
11903
12232
|
$emit: function(name, args) {
|
@@ -11965,7 +12294,7 @@ function $RootScopeProvider(){
|
|
11965
12294
|
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
|
11966
12295
|
*
|
11967
12296
|
* @param {string} name Event name to broadcast.
|
11968
|
-
* @param {...*} args Optional
|
12297
|
+
* @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
|
11969
12298
|
* @return {Object} Event object, see {@link ng.$rootScope.Scope#methods_$on}
|
11970
12299
|
*/
|
11971
12300
|
$broadcast: function(name, args) {
|
@@ -11984,8 +12313,7 @@ function $RootScopeProvider(){
|
|
11984
12313
|
listeners, i, length;
|
11985
12314
|
|
11986
12315
|
//down while you can, then up and next sibling or up and next sibling until back at root
|
11987
|
-
|
11988
|
-
current = next;
|
12316
|
+
while ((current = next)) {
|
11989
12317
|
event.currentScope = current;
|
11990
12318
|
listeners = current.$$listeners[name] || [];
|
11991
12319
|
for (i=0, length = listeners.length; i<length; i++) {
|
@@ -12007,12 +12335,14 @@ function $RootScopeProvider(){
|
|
12007
12335
|
// Insanity Warning: scope depth-first traversal
|
12008
12336
|
// yes, this code is a bit crazy, but it works and we have tests to prove it!
|
12009
12337
|
// this piece should be kept in sync with the traversal in $digest
|
12010
|
-
|
12338
|
+
// (though it differs due to having the extra check for $$listenerCount)
|
12339
|
+
if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
|
12340
|
+
(current !== target && current.$$nextSibling)))) {
|
12011
12341
|
while(current !== target && !(next = current.$$nextSibling)) {
|
12012
12342
|
current = current.$parent;
|
12013
12343
|
}
|
12014
12344
|
}
|
12015
|
-
}
|
12345
|
+
}
|
12016
12346
|
|
12017
12347
|
return event;
|
12018
12348
|
}
|
@@ -12041,6 +12371,16 @@ function $RootScopeProvider(){
|
|
12041
12371
|
return fn;
|
12042
12372
|
}
|
12043
12373
|
|
12374
|
+
function decrementListenerCount(current, count, name) {
|
12375
|
+
do {
|
12376
|
+
current.$$listenerCount[name] -= count;
|
12377
|
+
|
12378
|
+
if (current.$$listenerCount[name] === 0) {
|
12379
|
+
delete current.$$listenerCount[name];
|
12380
|
+
}
|
12381
|
+
} while ((current = current.$parent));
|
12382
|
+
}
|
12383
|
+
|
12044
12384
|
/**
|
12045
12385
|
* function used as an initial value for watchers.
|
12046
12386
|
* because it's unique we can easily tell it apart from other values
|
@@ -12397,7 +12737,7 @@ function $SceDelegateProvider() {
|
|
12397
12737
|
*
|
12398
12738
|
* @description
|
12399
12739
|
* Returns an object that is trusted by angular for use in specified strict
|
12400
|
-
* contextual escaping contexts (such as ng-
|
12740
|
+
* contextual escaping contexts (such as ng-bind-html, ng-include, any src
|
12401
12741
|
* attribute interpolation, any dom event binding attribute interpolation
|
12402
12742
|
* such as for onclick, etc.) that uses the provided value.
|
12403
12743
|
* See {@link ng.$sce $sce} for enabling strict contextual escaping.
|
@@ -12443,7 +12783,7 @@ function $SceDelegateProvider() {
|
|
12443
12783
|
*
|
12444
12784
|
* @param {*} value The result of a prior {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}
|
12445
12785
|
* call or anything else.
|
12446
|
-
* @returns {*} The value
|
12786
|
+
* @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#methods_trustAs
|
12447
12787
|
* `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
|
12448
12788
|
* `value` unchanged.
|
12449
12789
|
*/
|
@@ -12624,8 +12964,8 @@ function $SceDelegateProvider() {
|
|
12624
12964
|
* It's important to remember that SCE only applies to interpolation expressions.
|
12625
12965
|
*
|
12626
12966
|
* If your expressions are constant literals, they're automatically trusted and you don't need to
|
12627
|
-
* call `$sce.trustAs` on them
|
12628
|
-
* `<div ng-
|
12967
|
+
* call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
|
12968
|
+
* `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
|
12629
12969
|
*
|
12630
12970
|
* Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
|
12631
12971
|
* through {@link ng.$sce#methods_getTrusted $sce.getTrusted}. SCE doesn't play a role here.
|
@@ -12685,7 +13025,7 @@ function $SceDelegateProvider() {
|
|
12685
13025
|
* matched against the **entire** *normalized / absolute URL* of the resource being tested
|
12686
13026
|
* (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
|
12687
13027
|
* present on the RegExp (such as multiline, global, ignoreCase) are ignored.
|
12688
|
-
* - If you are generating your
|
13028
|
+
* - If you are generating your JavaScript from some other templating engine (not
|
12689
13029
|
* recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
|
12690
13030
|
* remember to escape your regular expression (and be aware that you might need more than
|
12691
13031
|
* one level of escaping depending on your templating engine and the way you interpolated
|
@@ -12702,7 +13042,7 @@ function $SceDelegateProvider() {
|
|
12702
13042
|
* ## Show me an example using SCE.
|
12703
13043
|
*
|
12704
13044
|
* @example
|
12705
|
-
<example module="mySceApp">
|
13045
|
+
<example module="mySceApp" deps="angular-sanitize.js">
|
12706
13046
|
<file name="index.html">
|
12707
13047
|
<div ng-controller="myAppController as myCtrl">
|
12708
13048
|
<i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
|
@@ -12746,13 +13086,15 @@ function $SceDelegateProvider() {
|
|
12746
13086
|
]
|
12747
13087
|
</file>
|
12748
13088
|
|
12749
|
-
<file name="
|
13089
|
+
<file name="protractorTest.js">
|
12750
13090
|
describe('SCE doc demo', function() {
|
12751
13091
|
it('should sanitize untrusted values', function() {
|
12752
|
-
expect(element('.htmlComment').
|
13092
|
+
expect(element(by.css('.htmlComment')).getInnerHtml())
|
13093
|
+
.toBe('<span>Is <i>anyone</i> reading this?</span>');
|
12753
13094
|
});
|
13095
|
+
|
12754
13096
|
it('should NOT sanitize explicitly trusted values', function() {
|
12755
|
-
expect(element('
|
13097
|
+
expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
|
12756
13098
|
'<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
|
12757
13099
|
'sanitization."">Hover over this text.</span>');
|
12758
13100
|
});
|
@@ -12927,8 +13269,8 @@ function $SceProvider() {
|
|
12927
13269
|
*
|
12928
13270
|
* @description
|
12929
13271
|
* Delegates to {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}. As such,
|
12930
|
-
* returns an
|
12931
|
-
* escaping contexts (such as ng-
|
13272
|
+
* returns an object that is trusted by angular for use in specified strict contextual
|
13273
|
+
* escaping contexts (such as ng-bind-html, ng-include, any src attribute
|
12932
13274
|
* interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
|
12933
13275
|
* that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
|
12934
13276
|
* escaping.
|
@@ -13259,7 +13601,7 @@ function $SnifferProvider() {
|
|
13259
13601
|
// http://code.google.com/p/android/issues/detail?id=17471
|
13260
13602
|
// https://github.com/angular/angular.js/issues/904
|
13261
13603
|
|
13262
|
-
// older
|
13604
|
+
// older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
|
13263
13605
|
// so let's not use the history API also
|
13264
13606
|
// We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
|
13265
13607
|
// jshint -W018
|
@@ -13285,6 +13627,7 @@ function $SnifferProvider() {
|
|
13285
13627
|
vendorPrefix: vendorPrefix,
|
13286
13628
|
transitions : transitions,
|
13287
13629
|
animations : animations,
|
13630
|
+
android: android,
|
13288
13631
|
msie : msie,
|
13289
13632
|
msieDocumentMode: documentMode
|
13290
13633
|
};
|
@@ -13322,113 +13665,26 @@ function $TimeoutProvider() {
|
|
13322
13665
|
* @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
|
13323
13666
|
* promise will be resolved with is the return value of the `fn` function.
|
13324
13667
|
*
|
13325
|
-
|
13326
|
-
|
13327
|
-
|
13328
|
-
|
13329
|
-
|
13330
|
-
|
13331
|
-
$scope.blood_1 = 100;
|
13332
|
-
$scope.blood_2 = 120;
|
13333
|
-
|
13334
|
-
var stop;
|
13335
|
-
$scope.fight = function() {
|
13336
|
-
stop = $timeout(function() {
|
13337
|
-
if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
|
13338
|
-
$scope.blood_1 = $scope.blood_1 - 3;
|
13339
|
-
$scope.blood_2 = $scope.blood_2 - 4;
|
13340
|
-
$scope.fight();
|
13341
|
-
} else {
|
13342
|
-
$timeout.cancel(stop);
|
13343
|
-
}
|
13344
|
-
}, 100);
|
13345
|
-
};
|
13668
|
+
*/
|
13669
|
+
function timeout(fn, delay, invokeApply) {
|
13670
|
+
var deferred = $q.defer(),
|
13671
|
+
promise = deferred.promise,
|
13672
|
+
skipApply = (isDefined(invokeApply) && !invokeApply),
|
13673
|
+
timeoutId;
|
13346
13674
|
|
13347
|
-
|
13348
|
-
|
13349
|
-
|
13675
|
+
timeoutId = $browser.defer(function() {
|
13676
|
+
try {
|
13677
|
+
deferred.resolve(fn());
|
13678
|
+
} catch(e) {
|
13679
|
+
deferred.reject(e);
|
13680
|
+
$exceptionHandler(e);
|
13681
|
+
}
|
13682
|
+
finally {
|
13683
|
+
delete deferreds[promise.$$timeoutId];
|
13684
|
+
}
|
13350
13685
|
|
13351
|
-
|
13352
|
-
|
13353
|
-
$scope.blood_2 = 120;
|
13354
|
-
}
|
13355
|
-
}
|
13356
|
-
|
13357
|
-
angular.module('time', [])
|
13358
|
-
// Register the 'myCurrentTime' directive factory method.
|
13359
|
-
// We inject $timeout and dateFilter service since the factory method is DI.
|
13360
|
-
.directive('myCurrentTime', function($timeout, dateFilter) {
|
13361
|
-
// return the directive link function. (compile function not needed)
|
13362
|
-
return function(scope, element, attrs) {
|
13363
|
-
var format, // date format
|
13364
|
-
timeoutId; // timeoutId, so that we can cancel the time updates
|
13365
|
-
|
13366
|
-
// used to update the UI
|
13367
|
-
function updateTime() {
|
13368
|
-
element.text(dateFilter(new Date(), format));
|
13369
|
-
}
|
13370
|
-
|
13371
|
-
// watch the expression, and update the UI on change.
|
13372
|
-
scope.$watch(attrs.myCurrentTime, function(value) {
|
13373
|
-
format = value;
|
13374
|
-
updateTime();
|
13375
|
-
});
|
13376
|
-
|
13377
|
-
// schedule update in one second
|
13378
|
-
function updateLater() {
|
13379
|
-
// save the timeoutId for canceling
|
13380
|
-
timeoutId = $timeout(function() {
|
13381
|
-
updateTime(); // update DOM
|
13382
|
-
updateLater(); // schedule another update
|
13383
|
-
}, 1000);
|
13384
|
-
}
|
13385
|
-
|
13386
|
-
// listen on DOM destroy (removal) event, and cancel the next UI update
|
13387
|
-
// to prevent updating time ofter the DOM element was removed.
|
13388
|
-
element.bind('$destroy', function() {
|
13389
|
-
$timeout.cancel(timeoutId);
|
13390
|
-
});
|
13391
|
-
|
13392
|
-
updateLater(); // kick off the UI update process.
|
13393
|
-
}
|
13394
|
-
});
|
13395
|
-
</script>
|
13396
|
-
|
13397
|
-
<div>
|
13398
|
-
<div ng-controller="Ctrl2">
|
13399
|
-
Date format: <input ng-model="format"> <hr/>
|
13400
|
-
Current time is: <span my-current-time="format"></span>
|
13401
|
-
<hr/>
|
13402
|
-
Blood 1 : <font color='red'>{{blood_1}}</font>
|
13403
|
-
Blood 2 : <font color='red'>{{blood_2}}</font>
|
13404
|
-
<button type="button" data-ng-click="fight()">Fight</button>
|
13405
|
-
<button type="button" data-ng-click="stopFight()">StopFight</button>
|
13406
|
-
<button type="button" data-ng-click="resetFight()">resetFight</button>
|
13407
|
-
</div>
|
13408
|
-
</div>
|
13409
|
-
|
13410
|
-
</doc:source>
|
13411
|
-
</doc:example>
|
13412
|
-
*/
|
13413
|
-
function timeout(fn, delay, invokeApply) {
|
13414
|
-
var deferred = $q.defer(),
|
13415
|
-
promise = deferred.promise,
|
13416
|
-
skipApply = (isDefined(invokeApply) && !invokeApply),
|
13417
|
-
timeoutId;
|
13418
|
-
|
13419
|
-
timeoutId = $browser.defer(function() {
|
13420
|
-
try {
|
13421
|
-
deferred.resolve(fn());
|
13422
|
-
} catch(e) {
|
13423
|
-
deferred.reject(e);
|
13424
|
-
$exceptionHandler(e);
|
13425
|
-
}
|
13426
|
-
finally {
|
13427
|
-
delete deferreds[promise.$$timeoutId];
|
13428
|
-
}
|
13429
|
-
|
13430
|
-
if (!skipApply) $rootScope.$apply();
|
13431
|
-
}, delay);
|
13686
|
+
if (!skipApply) $rootScope.$apply();
|
13687
|
+
}, delay);
|
13432
13688
|
|
13433
13689
|
promise.$$timeoutId = timeoutId;
|
13434
13690
|
deferreds[timeoutId] = deferred;
|
@@ -13597,13 +13853,13 @@ function urlIsSameOrigin(requestUrl) {
|
|
13597
13853
|
<button ng-click="doGreeting(greeting)">ALERT</button>
|
13598
13854
|
</div>
|
13599
13855
|
</doc:source>
|
13600
|
-
<doc:
|
13856
|
+
<doc:protractor>
|
13601
13857
|
it('should display the greeting in the input box', function() {
|
13602
|
-
|
13858
|
+
element(by.model('greeting')).sendKeys('Hello, E2E Tests');
|
13603
13859
|
// If we click the button it will block the test runner
|
13604
13860
|
// element(':button').click();
|
13605
13861
|
});
|
13606
|
-
</doc:
|
13862
|
+
</doc:protractor>
|
13607
13863
|
</doc:example>
|
13608
13864
|
*/
|
13609
13865
|
function $WindowProvider(){
|
@@ -13756,8 +14012,8 @@ function $FilterProvider($provide) {
|
|
13756
14012
|
*
|
13757
14013
|
* Can be one of:
|
13758
14014
|
*
|
13759
|
-
* - `string`:
|
13760
|
-
*
|
14015
|
+
* - `string`: The string is evaluated as an expression and the resulting value is used for substring match against
|
14016
|
+
* the contents of the `array`. All strings or objects with string properties in `array` that contain this string
|
13761
14017
|
* will be returned. The predicate can be negated by prefixing the string with `!`.
|
13762
14018
|
*
|
13763
14019
|
* - `Object`: A pattern object can be used to filter specific properties on objects contained
|
@@ -13767,21 +14023,21 @@ function $FilterProvider($provide) {
|
|
13767
14023
|
* property of the object. That's equivalent to the simple substring match with a `string`
|
13768
14024
|
* as described above.
|
13769
14025
|
*
|
13770
|
-
* - `function`: A predicate function can be used to write arbitrary filters. The function is
|
14026
|
+
* - `function(value)`: A predicate function can be used to write arbitrary filters. The function is
|
13771
14027
|
* called for each element of `array`. The final result is an array of those elements that
|
13772
14028
|
* the predicate returned true for.
|
13773
14029
|
*
|
13774
|
-
* @param {function(
|
14030
|
+
* @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
|
13775
14031
|
* determining if the expected value (from the filter expression) and actual value (from
|
13776
14032
|
* the object in the array) should be considered a match.
|
13777
14033
|
*
|
13778
14034
|
* Can be one of:
|
13779
14035
|
*
|
13780
|
-
* - `function(
|
14036
|
+
* - `function(actual, expected)`:
|
13781
14037
|
* The function will be given the object value and the predicate value to compare and
|
13782
14038
|
* should return true if the item should be included in filtered result.
|
13783
14039
|
*
|
13784
|
-
* - `true`: A shorthand for `function(
|
14040
|
+
* - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`.
|
13785
14041
|
* this is essentially strict comparison of expected and actual.
|
13786
14042
|
*
|
13787
14043
|
* - `false|undefined`: A short hand for a function which will look for a substring match in case
|
@@ -13812,35 +14068,47 @@ function $FilterProvider($provide) {
|
|
13812
14068
|
Equality <input type="checkbox" ng-model="strict"><br>
|
13813
14069
|
<table id="searchObjResults">
|
13814
14070
|
<tr><th>Name</th><th>Phone</th></tr>
|
13815
|
-
<tr ng-repeat="
|
13816
|
-
<td>{{
|
13817
|
-
<td>{{
|
14071
|
+
<tr ng-repeat="friendObj in friends | filter:search:strict">
|
14072
|
+
<td>{{friendObj.name}}</td>
|
14073
|
+
<td>{{friendObj.phone}}</td>
|
13818
14074
|
</tr>
|
13819
14075
|
</table>
|
13820
14076
|
</doc:source>
|
13821
|
-
<doc:
|
13822
|
-
|
13823
|
-
|
13824
|
-
|
13825
|
-
|
14077
|
+
<doc:protractor>
|
14078
|
+
var expectFriendNames = function(expectedNames, key) {
|
14079
|
+
element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
|
14080
|
+
arr.forEach(function(wd, i) {
|
14081
|
+
expect(wd.getText()).toMatch(expectedNames[i]);
|
14082
|
+
});
|
14083
|
+
});
|
14084
|
+
};
|
13826
14085
|
|
13827
|
-
|
13828
|
-
|
13829
|
-
|
14086
|
+
it('should search across all fields when filtering with a string', function() {
|
14087
|
+
var searchText = element(by.model('searchText'));
|
14088
|
+
searchText.clear();
|
14089
|
+
searchText.sendKeys('m');
|
14090
|
+
expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
|
14091
|
+
|
14092
|
+
searchText.clear();
|
14093
|
+
searchText.sendKeys('76');
|
14094
|
+
expectFriendNames(['John', 'Julie'], 'friend');
|
13830
14095
|
});
|
13831
14096
|
|
13832
14097
|
it('should search in specific fields when filtering with a predicate object', function() {
|
13833
|
-
|
13834
|
-
|
13835
|
-
|
14098
|
+
var searchAny = element(by.model('search.$'));
|
14099
|
+
searchAny.clear();
|
14100
|
+
searchAny.sendKeys('i');
|
14101
|
+
expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
|
13836
14102
|
});
|
13837
14103
|
it('should use a equal comparison when comparator is true', function() {
|
13838
|
-
|
13839
|
-
|
13840
|
-
|
13841
|
-
|
14104
|
+
var searchName = element(by.model('search.name'));
|
14105
|
+
var strict = element(by.model('strict'));
|
14106
|
+
searchName.clear();
|
14107
|
+
searchName.sendKeys('Julie');
|
14108
|
+
strict.click();
|
14109
|
+
expectFriendNames(['Julie'], 'friendObj');
|
13842
14110
|
});
|
13843
|
-
</doc:
|
14111
|
+
</doc:protractor>
|
13844
14112
|
</doc:example>
|
13845
14113
|
*/
|
13846
14114
|
function filterFilter() {
|
@@ -13866,6 +14134,15 @@ function filterFilter() {
|
|
13866
14134
|
};
|
13867
14135
|
} else {
|
13868
14136
|
comparator = function(obj, text) {
|
14137
|
+
if (obj && text && typeof obj === 'object' && typeof text === 'object') {
|
14138
|
+
for (var objKey in obj) {
|
14139
|
+
if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) &&
|
14140
|
+
comparator(obj[objKey], text[objKey])) {
|
14141
|
+
return true;
|
14142
|
+
}
|
14143
|
+
}
|
14144
|
+
return false;
|
14145
|
+
}
|
13869
14146
|
text = (''+text).toLowerCase();
|
13870
14147
|
return (''+obj).toLowerCase().indexOf(text) > -1;
|
13871
14148
|
};
|
@@ -13915,23 +14192,12 @@ function filterFilter() {
|
|
13915
14192
|
case "object":
|
13916
14193
|
// jshint +W086
|
13917
14194
|
for (var key in expression) {
|
13918
|
-
|
13919
|
-
(
|
13920
|
-
|
13921
|
-
|
13922
|
-
|
13923
|
-
|
13924
|
-
});
|
13925
|
-
})();
|
13926
|
-
} else {
|
13927
|
-
(function() {
|
13928
|
-
if (typeof(expression[key]) == 'undefined') { return; }
|
13929
|
-
var path = key;
|
13930
|
-
predicates.push(function(value) {
|
13931
|
-
return search(getter(value,path), expression[path]);
|
13932
|
-
});
|
13933
|
-
})();
|
13934
|
-
}
|
14195
|
+
(function(path) {
|
14196
|
+
if (typeof expression[path] == 'undefined') return;
|
14197
|
+
predicates.push(function(value) {
|
14198
|
+
return search(path == '$' ? value : (value && value[path]), expression[path]);
|
14199
|
+
});
|
14200
|
+
})(key);
|
13935
14201
|
}
|
13936
14202
|
break;
|
13937
14203
|
case 'function':
|
@@ -13975,21 +14241,27 @@ function filterFilter() {
|
|
13975
14241
|
</script>
|
13976
14242
|
<div ng-controller="Ctrl">
|
13977
14243
|
<input type="number" ng-model="amount"> <br>
|
13978
|
-
default currency symbol ($): {{amount | currency}}
|
13979
|
-
custom currency identifier (USD$): {{amount | currency:"USD$"}}
|
14244
|
+
default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
|
14245
|
+
custom currency identifier (USD$): <span>{{amount | currency:"USD$"}}</span>
|
13980
14246
|
</div>
|
13981
14247
|
</doc:source>
|
13982
|
-
<doc:
|
14248
|
+
<doc:protractor>
|
13983
14249
|
it('should init with 1234.56', function() {
|
13984
|
-
expect(
|
13985
|
-
expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56');
|
14250
|
+
expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
|
14251
|
+
expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('USD$1,234.56');
|
13986
14252
|
});
|
13987
14253
|
it('should update', function() {
|
13988
|
-
|
13989
|
-
|
13990
|
-
|
14254
|
+
if (browser.params.browser == 'safari') {
|
14255
|
+
// Safari does not understand the minus key. See
|
14256
|
+
// https://github.com/angular/protractor/issues/481
|
14257
|
+
return;
|
14258
|
+
}
|
14259
|
+
element(by.model('amount')).clear();
|
14260
|
+
element(by.model('amount')).sendKeys('-1234');
|
14261
|
+
expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
|
14262
|
+
expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('(USD$1,234.00)');
|
13991
14263
|
});
|
13992
|
-
</doc:
|
14264
|
+
</doc:protractor>
|
13993
14265
|
</doc:example>
|
13994
14266
|
*/
|
13995
14267
|
currencyFilter.$inject = ['$locale'];
|
@@ -14028,25 +14300,26 @@ function currencyFilter($locale) {
|
|
14028
14300
|
</script>
|
14029
14301
|
<div ng-controller="Ctrl">
|
14030
14302
|
Enter number: <input ng-model='val'><br>
|
14031
|
-
Default formatting: {{val | number}}
|
14032
|
-
No fractions: {{val | number:0}}
|
14033
|
-
Negative number: {{-val | number:4}}
|
14303
|
+
Default formatting: <span id='number-default'>{{val | number}}</span><br>
|
14304
|
+
No fractions: <span>{{val | number:0}}</span><br>
|
14305
|
+
Negative number: <span>{{-val | number:4}}</span>
|
14034
14306
|
</div>
|
14035
14307
|
</doc:source>
|
14036
|
-
<doc:
|
14308
|
+
<doc:protractor>
|
14037
14309
|
it('should format numbers', function() {
|
14038
|
-
expect(
|
14039
|
-
expect(binding('val | number:0')).toBe('1,235');
|
14040
|
-
expect(binding('-val | number:4')).toBe('-1,234.5679');
|
14310
|
+
expect(element(by.id('number-default')).getText()).toBe('1,234.568');
|
14311
|
+
expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
|
14312
|
+
expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
|
14041
14313
|
});
|
14042
14314
|
|
14043
14315
|
it('should update', function() {
|
14044
|
-
|
14045
|
-
|
14046
|
-
expect(
|
14047
|
-
expect(binding('
|
14048
|
-
|
14049
|
-
|
14316
|
+
element(by.model('val')).clear();
|
14317
|
+
element(by.model('val')).sendKeys('3374.333');
|
14318
|
+
expect(element(by.id('number-default')).getText()).toBe('3,374.333');
|
14319
|
+
expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
|
14320
|
+
expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
|
14321
|
+
});
|
14322
|
+
</doc:protractor>
|
14050
14323
|
</doc:example>
|
14051
14324
|
*/
|
14052
14325
|
|
@@ -14276,22 +14549,22 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
|
14276
14549
|
<doc:example>
|
14277
14550
|
<doc:source>
|
14278
14551
|
<span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
|
14279
|
-
{{1288323623006 | date:'medium'}}
|
14552
|
+
<span>{{1288323623006 | date:'medium'}}</span><br>
|
14280
14553
|
<span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
|
14281
|
-
{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}
|
14554
|
+
<span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
|
14282
14555
|
<span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
|
14283
|
-
{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}
|
14556
|
+
<span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
|
14284
14557
|
</doc:source>
|
14285
|
-
<doc:
|
14558
|
+
<doc:protractor>
|
14286
14559
|
it('should format date', function() {
|
14287
|
-
expect(binding("1288323623006 | date:'medium'")).
|
14560
|
+
expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
|
14288
14561
|
toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
|
14289
|
-
expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
|
14562
|
+
expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
|
14290
14563
|
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
|
14291
|
-
expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
|
14564
|
+
expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
|
14292
14565
|
toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
|
14293
14566
|
});
|
14294
|
-
</doc:
|
14567
|
+
</doc:protractor>
|
14295
14568
|
</doc:example>
|
14296
14569
|
*/
|
14297
14570
|
dateFilter.$inject = ['$locale'];
|
@@ -14390,11 +14663,11 @@ function dateFilter($locale) {
|
|
14390
14663
|
<doc:source>
|
14391
14664
|
<pre>{{ {'name':'value'} | json }}</pre>
|
14392
14665
|
</doc:source>
|
14393
|
-
<doc:
|
14666
|
+
<doc:protractor>
|
14394
14667
|
it('should jsonify filtered objects', function() {
|
14395
|
-
expect(binding("{'name':'value'}")).toMatch(/\{\n "name": ?"value"\n}/);
|
14668
|
+
expect(element(by.binding("{'name':'value'}")).getText()).toMatch(/\{\n "name": ?"value"\n}/);
|
14396
14669
|
});
|
14397
|
-
</doc:
|
14670
|
+
</doc:protractor>
|
14398
14671
|
</doc:example>
|
14399
14672
|
*
|
14400
14673
|
*/
|
@@ -14462,28 +14735,37 @@ var uppercaseFilter = valueFn(uppercase);
|
|
14462
14735
|
<p>Output letters: {{ letters | limitTo:letterLimit }}</p>
|
14463
14736
|
</div>
|
14464
14737
|
</doc:source>
|
14465
|
-
<doc:
|
14738
|
+
<doc:protractor>
|
14739
|
+
var numLimitInput = element(by.model('numLimit'));
|
14740
|
+
var letterLimitInput = element(by.model('letterLimit'));
|
14741
|
+
var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
|
14742
|
+
var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
|
14743
|
+
|
14466
14744
|
it('should limit the number array to first three items', function() {
|
14467
|
-
expect(
|
14468
|
-
expect(
|
14469
|
-
expect(
|
14470
|
-
expect(
|
14745
|
+
expect(numLimitInput.getAttribute('value')).toBe('3');
|
14746
|
+
expect(letterLimitInput.getAttribute('value')).toBe('3');
|
14747
|
+
expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
|
14748
|
+
expect(limitedLetters.getText()).toEqual('Output letters: abc');
|
14471
14749
|
});
|
14472
14750
|
|
14473
14751
|
it('should update the output when -3 is entered', function() {
|
14474
|
-
|
14475
|
-
|
14476
|
-
|
14477
|
-
|
14752
|
+
numLimitInput.clear();
|
14753
|
+
numLimitInput.sendKeys('-3');
|
14754
|
+
letterLimitInput.clear();
|
14755
|
+
letterLimitInput.sendKeys('-3');
|
14756
|
+
expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
|
14757
|
+
expect(limitedLetters.getText()).toEqual('Output letters: ghi');
|
14478
14758
|
});
|
14479
14759
|
|
14480
14760
|
it('should not exceed the maximum size of input array', function() {
|
14481
|
-
|
14482
|
-
|
14483
|
-
|
14484
|
-
|
14761
|
+
numLimitInput.clear();
|
14762
|
+
numLimitInput.sendKeys('100');
|
14763
|
+
letterLimitInput.clear();
|
14764
|
+
letterLimitInput.sendKeys('100');
|
14765
|
+
expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
|
14766
|
+
expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
|
14485
14767
|
});
|
14486
|
-
</doc:
|
14768
|
+
</doc:protractor>
|
14487
14769
|
</doc:example>
|
14488
14770
|
*/
|
14489
14771
|
function limitToFilter(){
|
@@ -14584,29 +14866,6 @@ function limitToFilter(){
|
|
14584
14866
|
</table>
|
14585
14867
|
</div>
|
14586
14868
|
</doc:source>
|
14587
|
-
<doc:scenario>
|
14588
|
-
it('should be reverse ordered by aged', function() {
|
14589
|
-
expect(binding('predicate')).toBe('-age');
|
14590
|
-
expect(repeater('table.friend', 'friend in friends').column('friend.age')).
|
14591
|
-
toEqual(['35', '29', '21', '19', '10']);
|
14592
|
-
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
|
14593
|
-
toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']);
|
14594
|
-
});
|
14595
|
-
|
14596
|
-
it('should reorder the table when user selects different predicate', function() {
|
14597
|
-
element('.doc-example-live a:contains("Name")').click();
|
14598
|
-
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
|
14599
|
-
toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']);
|
14600
|
-
expect(repeater('table.friend', 'friend in friends').column('friend.age')).
|
14601
|
-
toEqual(['35', '10', '29', '19', '21']);
|
14602
|
-
|
14603
|
-
element('.doc-example-live a:contains("Phone")').click();
|
14604
|
-
expect(repeater('table.friend', 'friend in friends').column('friend.phone')).
|
14605
|
-
toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']);
|
14606
|
-
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
|
14607
|
-
toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']);
|
14608
|
-
});
|
14609
|
-
</doc:scenario>
|
14610
14869
|
</doc:example>
|
14611
14870
|
*/
|
14612
14871
|
orderByFilter.$inject = ['$parse'];
|
@@ -14703,11 +14962,14 @@ var htmlAnchorDirective = valueFn({
|
|
14703
14962
|
element.append(document.createComment('IE fix'));
|
14704
14963
|
}
|
14705
14964
|
|
14706
|
-
if (!attr.href && !attr.name) {
|
14965
|
+
if (!attr.href && !attr.xlinkHref && !attr.name) {
|
14707
14966
|
return function(scope, element) {
|
14967
|
+
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
|
14968
|
+
var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
|
14969
|
+
'xlink:href' : 'href';
|
14708
14970
|
element.on('click', function(event){
|
14709
14971
|
// if we have no href url, then don't navigate anywhere.
|
14710
|
-
if (!element.attr(
|
14972
|
+
if (!element.attr(href)) {
|
14711
14973
|
event.preventDefault();
|
14712
14974
|
}
|
14713
14975
|
});
|
@@ -14720,6 +14982,7 @@ var htmlAnchorDirective = valueFn({
|
|
14720
14982
|
* @ngdoc directive
|
14721
14983
|
* @name ng.directive:ngHref
|
14722
14984
|
* @restrict A
|
14985
|
+
* @priority 99
|
14723
14986
|
*
|
14724
14987
|
* @description
|
14725
14988
|
* Using Angular markup like `{{hash}}` in an href attribute will
|
@@ -14756,46 +15019,55 @@ var htmlAnchorDirective = valueFn({
|
|
14756
15019
|
<a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
|
14757
15020
|
<a id="link-6" ng-href="{{value}}">link</a> (link, change location)
|
14758
15021
|
</doc:source>
|
14759
|
-
<doc:
|
15022
|
+
<doc:protractor>
|
14760
15023
|
it('should execute ng-click but not reload when href without value', function() {
|
14761
|
-
element('
|
14762
|
-
expect(
|
14763
|
-
expect(element('
|
15024
|
+
element(by.id('link-1')).click();
|
15025
|
+
expect(element(by.model('value')).getAttribute('value')).toEqual('1');
|
15026
|
+
expect(element(by.id('link-1')).getAttribute('href')).toBe('');
|
14764
15027
|
});
|
14765
15028
|
|
14766
15029
|
it('should execute ng-click but not reload when href empty string', function() {
|
14767
|
-
element('
|
14768
|
-
expect(
|
14769
|
-
expect(element('
|
15030
|
+
element(by.id('link-2')).click();
|
15031
|
+
expect(element(by.model('value')).getAttribute('value')).toEqual('2');
|
15032
|
+
expect(element(by.id('link-2')).getAttribute('href')).toBe('');
|
14770
15033
|
});
|
14771
15034
|
|
14772
15035
|
it('should execute ng-click and change url when ng-href specified', function() {
|
14773
|
-
expect(element('
|
15036
|
+
expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
|
15037
|
+
|
15038
|
+
element(by.id('link-3')).click();
|
14774
15039
|
|
14775
|
-
|
14776
|
-
|
15040
|
+
// At this point, we navigate away from an Angular page, so we need
|
15041
|
+
// to use browser.driver to get the base webdriver.
|
15042
|
+
|
15043
|
+
browser.wait(function() {
|
15044
|
+
return browser.driver.getCurrentUrl().then(function(url) {
|
15045
|
+
return url.match(/\/123$/);
|
15046
|
+
});
|
15047
|
+
}, 1000, 'page should navigate to /123');
|
14777
15048
|
});
|
14778
15049
|
|
14779
15050
|
it('should execute ng-click but not reload when href empty string and name specified', function() {
|
14780
|
-
element('
|
14781
|
-
expect(
|
14782
|
-
expect(element('
|
15051
|
+
element(by.id('link-4')).click();
|
15052
|
+
expect(element(by.model('value')).getAttribute('value')).toEqual('4');
|
15053
|
+
expect(element(by.id('link-4')).getAttribute('href')).toBe('');
|
14783
15054
|
});
|
14784
15055
|
|
14785
15056
|
it('should execute ng-click but not reload when no href but name specified', function() {
|
14786
|
-
element('
|
14787
|
-
expect(
|
14788
|
-
expect(element('
|
15057
|
+
element(by.id('link-5')).click();
|
15058
|
+
expect(element(by.model('value')).getAttribute('value')).toEqual('5');
|
15059
|
+
expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
|
14789
15060
|
});
|
14790
15061
|
|
14791
15062
|
it('should only change url when only ng-href', function() {
|
14792
|
-
|
14793
|
-
|
15063
|
+
element(by.model('value')).clear();
|
15064
|
+
element(by.model('value')).sendKeys('6');
|
15065
|
+
expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
|
14794
15066
|
|
14795
|
-
element('
|
14796
|
-
expect(browser
|
15067
|
+
element(by.id('link-6')).click();
|
15068
|
+
expect(browser.getCurrentUrl()).toMatch(/\/6$/);
|
14797
15069
|
});
|
14798
|
-
</doc:
|
15070
|
+
</doc:protractor>
|
14799
15071
|
</doc:example>
|
14800
15072
|
*/
|
14801
15073
|
|
@@ -14803,6 +15075,7 @@ var htmlAnchorDirective = valueFn({
|
|
14803
15075
|
* @ngdoc directive
|
14804
15076
|
* @name ng.directive:ngSrc
|
14805
15077
|
* @restrict A
|
15078
|
+
* @priority 99
|
14806
15079
|
*
|
14807
15080
|
* @description
|
14808
15081
|
* Using Angular markup like `{{hash}}` in a `src` attribute doesn't
|
@@ -14828,6 +15101,7 @@ var htmlAnchorDirective = valueFn({
|
|
14828
15101
|
* @ngdoc directive
|
14829
15102
|
* @name ng.directive:ngSrcset
|
14830
15103
|
* @restrict A
|
15104
|
+
* @priority 99
|
14831
15105
|
*
|
14832
15106
|
* @description
|
14833
15107
|
* Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
|
@@ -14853,6 +15127,7 @@ var htmlAnchorDirective = valueFn({
|
|
14853
15127
|
* @ngdoc directive
|
14854
15128
|
* @name ng.directive:ngDisabled
|
14855
15129
|
* @restrict A
|
15130
|
+
* @priority 100
|
14856
15131
|
*
|
14857
15132
|
* @description
|
14858
15133
|
*
|
@@ -14877,13 +15152,13 @@ var htmlAnchorDirective = valueFn({
|
|
14877
15152
|
Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
|
14878
15153
|
<button ng-model="button" ng-disabled="checked">Button</button>
|
14879
15154
|
</doc:source>
|
14880
|
-
<doc:
|
15155
|
+
<doc:protractor>
|
14881
15156
|
it('should toggle button', function() {
|
14882
|
-
expect(element('.doc-example-live
|
14883
|
-
|
14884
|
-
expect(element('.doc-example-live
|
15157
|
+
expect(element(by.css('.doc-example-live button')).getAttribute('disabled')).toBeFalsy();
|
15158
|
+
element(by.model('checked')).click();
|
15159
|
+
expect(element(by.css('.doc-example-live button')).getAttribute('disabled')).toBeTruthy();
|
14885
15160
|
});
|
14886
|
-
</doc:
|
15161
|
+
</doc:protractor>
|
14887
15162
|
</doc:example>
|
14888
15163
|
*
|
14889
15164
|
* @element INPUT
|
@@ -14896,6 +15171,7 @@ var htmlAnchorDirective = valueFn({
|
|
14896
15171
|
* @ngdoc directive
|
14897
15172
|
* @name ng.directive:ngChecked
|
14898
15173
|
* @restrict A
|
15174
|
+
* @priority 100
|
14899
15175
|
*
|
14900
15176
|
* @description
|
14901
15177
|
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
@@ -14911,13 +15187,13 @@ var htmlAnchorDirective = valueFn({
|
|
14911
15187
|
Check me to check both: <input type="checkbox" ng-model="master"><br/>
|
14912
15188
|
<input id="checkSlave" type="checkbox" ng-checked="master">
|
14913
15189
|
</doc:source>
|
14914
|
-
<doc:
|
15190
|
+
<doc:protractor>
|
14915
15191
|
it('should check both checkBoxes', function() {
|
14916
|
-
expect(element('
|
14917
|
-
|
14918
|
-
expect(element('
|
15192
|
+
expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
|
15193
|
+
element(by.model('master')).click();
|
15194
|
+
expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
|
14919
15195
|
});
|
14920
|
-
</doc:
|
15196
|
+
</doc:protractor>
|
14921
15197
|
</doc:example>
|
14922
15198
|
*
|
14923
15199
|
* @element INPUT
|
@@ -14930,6 +15206,7 @@ var htmlAnchorDirective = valueFn({
|
|
14930
15206
|
* @ngdoc directive
|
14931
15207
|
* @name ng.directive:ngReadonly
|
14932
15208
|
* @restrict A
|
15209
|
+
* @priority 100
|
14933
15210
|
*
|
14934
15211
|
* @description
|
14935
15212
|
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
@@ -14939,20 +15216,19 @@ var htmlAnchorDirective = valueFn({
|
|
14939
15216
|
* The `ngReadonly` directive solves this problem for the `readonly` attribute.
|
14940
15217
|
* This complementary directive is not removed by the browser and so provides
|
14941
15218
|
* a permanent reliable place to store the binding information.
|
14942
|
-
|
14943
15219
|
* @example
|
14944
15220
|
<doc:example>
|
14945
15221
|
<doc:source>
|
14946
15222
|
Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
|
14947
15223
|
<input type="text" ng-readonly="checked" value="I'm Angular"/>
|
14948
15224
|
</doc:source>
|
14949
|
-
<doc:
|
15225
|
+
<doc:protractor>
|
14950
15226
|
it('should toggle readonly attr', function() {
|
14951
|
-
expect(element('.doc-example-live
|
14952
|
-
|
14953
|
-
expect(element('.doc-example-live
|
15227
|
+
expect(element(by.css('.doc-example-live [type="text"]')).getAttribute('readonly')).toBeFalsy();
|
15228
|
+
element(by.model('checked')).click();
|
15229
|
+
expect(element(by.css('.doc-example-live [type="text"]')).getAttribute('readonly')).toBeTruthy();
|
14954
15230
|
});
|
14955
|
-
</doc:
|
15231
|
+
</doc:protractor>
|
14956
15232
|
</doc:example>
|
14957
15233
|
*
|
14958
15234
|
* @element INPUT
|
@@ -14965,6 +15241,7 @@ var htmlAnchorDirective = valueFn({
|
|
14965
15241
|
* @ngdoc directive
|
14966
15242
|
* @name ng.directive:ngSelected
|
14967
15243
|
* @restrict A
|
15244
|
+
* @priority 100
|
14968
15245
|
*
|
14969
15246
|
* @description
|
14970
15247
|
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
@@ -14974,6 +15251,7 @@ var htmlAnchorDirective = valueFn({
|
|
14974
15251
|
* The `ngSelected` directive solves this problem for the `selected` atttribute.
|
14975
15252
|
* This complementary directive is not removed by the browser and so provides
|
14976
15253
|
* a permanent reliable place to store the binding information.
|
15254
|
+
*
|
14977
15255
|
* @example
|
14978
15256
|
<doc:example>
|
14979
15257
|
<doc:source>
|
@@ -14983,13 +15261,13 @@ var htmlAnchorDirective = valueFn({
|
|
14983
15261
|
<option id="greet" ng-selected="selected">Greetings!</option>
|
14984
15262
|
</select>
|
14985
15263
|
</doc:source>
|
14986
|
-
<doc:
|
15264
|
+
<doc:protractor>
|
14987
15265
|
it('should select Greetings!', function() {
|
14988
|
-
expect(element('
|
14989
|
-
|
14990
|
-
expect(element('
|
15266
|
+
expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
|
15267
|
+
element(by.model('selected')).click();
|
15268
|
+
expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
|
14991
15269
|
});
|
14992
|
-
</doc:
|
15270
|
+
</doc:protractor>
|
14993
15271
|
</doc:example>
|
14994
15272
|
*
|
14995
15273
|
* @element OPTION
|
@@ -15001,6 +15279,7 @@ var htmlAnchorDirective = valueFn({
|
|
15001
15279
|
* @ngdoc directive
|
15002
15280
|
* @name ng.directive:ngOpen
|
15003
15281
|
* @restrict A
|
15282
|
+
* @priority 100
|
15004
15283
|
*
|
15005
15284
|
* @description
|
15006
15285
|
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
@@ -15010,8 +15289,6 @@ var htmlAnchorDirective = valueFn({
|
|
15010
15289
|
* The `ngOpen` directive solves this problem for the `open` attribute.
|
15011
15290
|
* This complementary directive is not removed by the browser and so provides
|
15012
15291
|
* a permanent reliable place to store the binding information.
|
15013
|
-
|
15014
|
-
*
|
15015
15292
|
* @example
|
15016
15293
|
<doc:example>
|
15017
15294
|
<doc:source>
|
@@ -15020,13 +15297,13 @@ var htmlAnchorDirective = valueFn({
|
|
15020
15297
|
<summary>Show/Hide me</summary>
|
15021
15298
|
</details>
|
15022
15299
|
</doc:source>
|
15023
|
-
<doc:
|
15300
|
+
<doc:protractor>
|
15024
15301
|
it('should toggle open', function() {
|
15025
|
-
expect(element('
|
15026
|
-
|
15027
|
-
expect(element('
|
15302
|
+
expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
|
15303
|
+
element(by.model('open')).click();
|
15304
|
+
expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
|
15028
15305
|
});
|
15029
|
-
</doc:
|
15306
|
+
</doc:protractor>
|
15030
15307
|
</doc:example>
|
15031
15308
|
*
|
15032
15309
|
* @element DETAILS
|
@@ -15046,12 +15323,10 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) {
|
|
15046
15323
|
ngAttributeAliasDirectives[normalized] = function() {
|
15047
15324
|
return {
|
15048
15325
|
priority: 100,
|
15049
|
-
|
15050
|
-
|
15051
|
-
|
15052
|
-
|
15053
|
-
});
|
15054
|
-
};
|
15326
|
+
link: function(scope, element, attr) {
|
15327
|
+
scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
|
15328
|
+
attr.$set(attrName, !!value);
|
15329
|
+
});
|
15055
15330
|
}
|
15056
15331
|
};
|
15057
15332
|
};
|
@@ -15331,10 +15606,10 @@ function FormController(element, attrs) {
|
|
15331
15606
|
*
|
15332
15607
|
*
|
15333
15608
|
* # CSS classes
|
15334
|
-
* - `ng-valid`
|
15335
|
-
* - `ng-invalid`
|
15336
|
-
* - `ng-pristine`
|
15337
|
-
* - `ng-dirty`
|
15609
|
+
* - `ng-valid` is set if the form is valid.
|
15610
|
+
* - `ng-invalid` is set if the form is invalid.
|
15611
|
+
* - `ng-pristine` is set if the form is pristine.
|
15612
|
+
* - `ng-dirty` is set if the form is dirty.
|
15338
15613
|
*
|
15339
15614
|
*
|
15340
15615
|
* # Submitting a form and preventing the default action
|
@@ -15387,18 +15662,27 @@ function FormController(element, attrs) {
|
|
15387
15662
|
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
|
15388
15663
|
</form>
|
15389
15664
|
</doc:source>
|
15390
|
-
<doc:
|
15665
|
+
<doc:protractor>
|
15391
15666
|
it('should initialize to model', function() {
|
15392
|
-
|
15393
|
-
|
15667
|
+
var userType = element(by.binding('userType'));
|
15668
|
+
var valid = element(by.binding('myForm.input.$valid'));
|
15669
|
+
|
15670
|
+
expect(userType.getText()).toContain('guest');
|
15671
|
+
expect(valid.getText()).toContain('true');
|
15394
15672
|
});
|
15395
15673
|
|
15396
15674
|
it('should be invalid if empty', function() {
|
15397
|
-
|
15398
|
-
|
15399
|
-
|
15675
|
+
var userType = element(by.binding('userType'));
|
15676
|
+
var valid = element(by.binding('myForm.input.$valid'));
|
15677
|
+
var userInput = element(by.model('userType'));
|
15678
|
+
|
15679
|
+
userInput.clear();
|
15680
|
+
userInput.sendKeys('');
|
15681
|
+
|
15682
|
+
expect(userType.getText()).toEqual('userType =');
|
15683
|
+
expect(valid.getText()).toContain('false');
|
15400
15684
|
});
|
15401
|
-
</doc:
|
15685
|
+
</doc:protractor>
|
15402
15686
|
</doc:example>
|
15403
15687
|
*/
|
15404
15688
|
var formDirectiveFactory = function(isNgForm) {
|
@@ -15470,7 +15754,7 @@ var ngFormDirective = formDirectiveFactory(true);
|
|
15470
15754
|
*/
|
15471
15755
|
|
15472
15756
|
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
|
15473
|
-
var EMAIL_REGEXP = /^[
|
15757
|
+
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i;
|
15474
15758
|
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
|
15475
15759
|
|
15476
15760
|
var inputType = {
|
@@ -15523,29 +15807,31 @@ var inputType = {
|
|
15523
15807
|
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
15524
15808
|
</form>
|
15525
15809
|
</doc:source>
|
15526
|
-
<doc:
|
15810
|
+
<doc:protractor>
|
15811
|
+
var text = element(by.binding('text'));
|
15812
|
+
var valid = element(by.binding('myForm.input.$valid'));
|
15813
|
+
var input = element(by.model('text'));
|
15814
|
+
|
15527
15815
|
it('should initialize to model', function() {
|
15528
|
-
expect(
|
15529
|
-
expect(
|
15816
|
+
expect(text.getText()).toContain('guest');
|
15817
|
+
expect(valid.getText()).toContain('true');
|
15530
15818
|
});
|
15531
15819
|
|
15532
15820
|
it('should be invalid if empty', function() {
|
15533
|
-
input
|
15534
|
-
|
15535
|
-
|
15821
|
+
input.clear();
|
15822
|
+
input.sendKeys('');
|
15823
|
+
|
15824
|
+
expect(text.getText()).toEqual('text =');
|
15825
|
+
expect(valid.getText()).toContain('false');
|
15536
15826
|
});
|
15537
15827
|
|
15538
15828
|
it('should be invalid if multi word', function() {
|
15539
|
-
input
|
15540
|
-
|
15541
|
-
});
|
15829
|
+
input.clear();
|
15830
|
+
input.sendKeys('hello world');
|
15542
15831
|
|
15543
|
-
|
15544
|
-
input('text').enter('untrimmed ');
|
15545
|
-
expect(binding('text')).toEqual('untrimmed ');
|
15546
|
-
expect(binding('myForm.input.$valid')).toEqual('true');
|
15832
|
+
expect(valid.getText()).toContain('false');
|
15547
15833
|
});
|
15548
|
-
</doc:
|
15834
|
+
</doc:protractor>
|
15549
15835
|
</doc:example>
|
15550
15836
|
*/
|
15551
15837
|
'text': textInputType,
|
@@ -15599,24 +15885,30 @@ var inputType = {
|
|
15599
15885
|
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
15600
15886
|
</form>
|
15601
15887
|
</doc:source>
|
15602
|
-
<doc:
|
15888
|
+
<doc:protractor>
|
15889
|
+
var value = element(by.binding('value'));
|
15890
|
+
var valid = element(by.binding('myForm.input.$valid'));
|
15891
|
+
var input = element(by.model('value'));
|
15892
|
+
|
15603
15893
|
it('should initialize to model', function() {
|
15604
|
-
|
15605
|
-
|
15894
|
+
expect(value.getText()).toContain('12');
|
15895
|
+
expect(valid.getText()).toContain('true');
|
15606
15896
|
});
|
15607
15897
|
|
15608
15898
|
it('should be invalid if empty', function() {
|
15609
|
-
|
15610
|
-
|
15611
|
-
|
15899
|
+
input.clear();
|
15900
|
+
input.sendKeys('');
|
15901
|
+
expect(value.getText()).toEqual('value =');
|
15902
|
+
expect(valid.getText()).toContain('false');
|
15612
15903
|
});
|
15613
15904
|
|
15614
15905
|
it('should be invalid if over max', function() {
|
15615
|
-
|
15616
|
-
|
15617
|
-
|
15906
|
+
input.clear();
|
15907
|
+
input.sendKeys('123');
|
15908
|
+
expect(value.getText()).toEqual('value =');
|
15909
|
+
expect(valid.getText()).toContain('false');
|
15618
15910
|
});
|
15619
|
-
</doc:
|
15911
|
+
</doc:protractor>
|
15620
15912
|
</doc:example>
|
15621
15913
|
*/
|
15622
15914
|
'number': numberInputType,
|
@@ -15668,23 +15960,31 @@ var inputType = {
|
|
15668
15960
|
<tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
|
15669
15961
|
</form>
|
15670
15962
|
</doc:source>
|
15671
|
-
<doc:
|
15963
|
+
<doc:protractor>
|
15964
|
+
var text = element(by.binding('text'));
|
15965
|
+
var valid = element(by.binding('myForm.input.$valid'));
|
15966
|
+
var input = element(by.model('text'));
|
15967
|
+
|
15672
15968
|
it('should initialize to model', function() {
|
15673
|
-
expect(
|
15674
|
-
expect(
|
15969
|
+
expect(text.getText()).toContain('http://google.com');
|
15970
|
+
expect(valid.getText()).toContain('true');
|
15675
15971
|
});
|
15676
15972
|
|
15677
15973
|
it('should be invalid if empty', function() {
|
15678
|
-
input
|
15679
|
-
|
15680
|
-
|
15974
|
+
input.clear();
|
15975
|
+
input.sendKeys('');
|
15976
|
+
|
15977
|
+
expect(text.getText()).toEqual('text =');
|
15978
|
+
expect(valid.getText()).toContain('false');
|
15681
15979
|
});
|
15682
15980
|
|
15683
15981
|
it('should be invalid if not url', function() {
|
15684
|
-
input
|
15685
|
-
|
15982
|
+
input.clear();
|
15983
|
+
input.sendKeys('box');
|
15984
|
+
|
15985
|
+
expect(valid.getText()).toContain('false');
|
15686
15986
|
});
|
15687
|
-
</doc:
|
15987
|
+
</doc:protractor>
|
15688
15988
|
</doc:example>
|
15689
15989
|
*/
|
15690
15990
|
'url': urlInputType,
|
@@ -15736,23 +16036,30 @@ var inputType = {
|
|
15736
16036
|
<tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
|
15737
16037
|
</form>
|
15738
16038
|
</doc:source>
|
15739
|
-
<doc:
|
16039
|
+
<doc:protractor>
|
16040
|
+
var text = element(by.binding('text'));
|
16041
|
+
var valid = element(by.binding('myForm.input.$valid'));
|
16042
|
+
var input = element(by.model('text'));
|
16043
|
+
|
15740
16044
|
it('should initialize to model', function() {
|
15741
|
-
expect(
|
15742
|
-
expect(
|
16045
|
+
expect(text.getText()).toContain('me@example.com');
|
16046
|
+
expect(valid.getText()).toContain('true');
|
15743
16047
|
});
|
15744
16048
|
|
15745
16049
|
it('should be invalid if empty', function() {
|
15746
|
-
input
|
15747
|
-
|
15748
|
-
expect(
|
16050
|
+
input.clear();
|
16051
|
+
input.sendKeys('');
|
16052
|
+
expect(text.getText()).toEqual('text =');
|
16053
|
+
expect(valid.getText()).toContain('false');
|
15749
16054
|
});
|
15750
16055
|
|
15751
16056
|
it('should be invalid if not email', function() {
|
15752
|
-
input
|
15753
|
-
|
16057
|
+
input.clear();
|
16058
|
+
input.sendKeys('xxx');
|
16059
|
+
|
16060
|
+
expect(valid.getText()).toContain('false');
|
15754
16061
|
});
|
15755
|
-
</doc:
|
16062
|
+
</doc:protractor>
|
15756
16063
|
</doc:example>
|
15757
16064
|
*/
|
15758
16065
|
'email': emailInputType,
|
@@ -15770,6 +16077,8 @@ var inputType = {
|
|
15770
16077
|
* @param {string=} name Property name of the form under which the control is published.
|
15771
16078
|
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
15772
16079
|
* interaction with the input element.
|
16080
|
+
* @param {string} ngValue Angular expression which sets the value to which the expression should
|
16081
|
+
* be set when selected.
|
15773
16082
|
*
|
15774
16083
|
* @example
|
15775
16084
|
<doc:example>
|
@@ -15777,23 +16086,31 @@ var inputType = {
|
|
15777
16086
|
<script>
|
15778
16087
|
function Ctrl($scope) {
|
15779
16088
|
$scope.color = 'blue';
|
16089
|
+
$scope.specialValue = {
|
16090
|
+
"id": "12345",
|
16091
|
+
"value": "green"
|
16092
|
+
};
|
15780
16093
|
}
|
15781
16094
|
</script>
|
15782
16095
|
<form name="myForm" ng-controller="Ctrl">
|
15783
16096
|
<input type="radio" ng-model="color" value="red"> Red <br/>
|
15784
|
-
<input type="radio" ng-model="color" value="
|
16097
|
+
<input type="radio" ng-model="color" ng-value="specialValue"> Green <br/>
|
15785
16098
|
<input type="radio" ng-model="color" value="blue"> Blue <br/>
|
15786
|
-
<tt>color = {{color}}</tt><br/>
|
16099
|
+
<tt>color = {{color | json}}</tt><br/>
|
15787
16100
|
</form>
|
16101
|
+
Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
|
15788
16102
|
</doc:source>
|
15789
|
-
<doc:
|
16103
|
+
<doc:protractor>
|
15790
16104
|
it('should change state', function() {
|
15791
|
-
|
16105
|
+
var color = element(by.binding('color'));
|
15792
16106
|
|
15793
|
-
|
15794
|
-
|
16107
|
+
expect(color.getText()).toContain('blue');
|
16108
|
+
|
16109
|
+
element.all(by.model('color')).get(0).click();
|
16110
|
+
|
16111
|
+
expect(color.getText()).toContain('red');
|
15795
16112
|
});
|
15796
|
-
</doc:
|
16113
|
+
</doc:protractor>
|
15797
16114
|
</doc:example>
|
15798
16115
|
*/
|
15799
16116
|
'radio': radioInputType,
|
@@ -15830,17 +16147,21 @@ var inputType = {
|
|
15830
16147
|
<tt>value2 = {{value2}}</tt><br/>
|
15831
16148
|
</form>
|
15832
16149
|
</doc:source>
|
15833
|
-
<doc:
|
16150
|
+
<doc:protractor>
|
15834
16151
|
it('should change state', function() {
|
15835
|
-
|
15836
|
-
|
16152
|
+
var value1 = element(by.binding('value1'));
|
16153
|
+
var value2 = element(by.binding('value2'));
|
15837
16154
|
|
15838
|
-
|
15839
|
-
|
15840
|
-
|
15841
|
-
|
16155
|
+
expect(value1.getText()).toContain('true');
|
16156
|
+
expect(value2.getText()).toContain('YES');
|
16157
|
+
|
16158
|
+
element(by.model('value1')).click();
|
16159
|
+
element(by.model('value2')).click();
|
16160
|
+
|
16161
|
+
expect(value1.getText()).toContain('false');
|
16162
|
+
expect(value2.getText()).toContain('NO');
|
15842
16163
|
});
|
15843
|
-
</doc:
|
16164
|
+
</doc:protractor>
|
15844
16165
|
</doc:example>
|
15845
16166
|
*/
|
15846
16167
|
'checkbox': checkboxInputType,
|
@@ -15848,23 +16169,33 @@ var inputType = {
|
|
15848
16169
|
'hidden': noop,
|
15849
16170
|
'button': noop,
|
15850
16171
|
'submit': noop,
|
15851
|
-
'reset': noop
|
16172
|
+
'reset': noop,
|
16173
|
+
'file': noop
|
15852
16174
|
};
|
15853
16175
|
|
16176
|
+
// A helper function to call $setValidity and return the value / undefined,
|
16177
|
+
// a pattern that is repeated a lot in the input validation logic.
|
16178
|
+
function validate(ctrl, validatorName, validity, value){
|
16179
|
+
ctrl.$setValidity(validatorName, validity);
|
16180
|
+
return validity ? value : undefined;
|
16181
|
+
}
|
15854
16182
|
|
15855
16183
|
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
15856
16184
|
// In composition mode, users are still inputing intermediate text buffer,
|
15857
16185
|
// hold the listener until composition is done.
|
15858
16186
|
// More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
|
15859
|
-
|
16187
|
+
if (!$sniffer.android) {
|
16188
|
+
var composing = false;
|
15860
16189
|
|
15861
|
-
|
15862
|
-
|
15863
|
-
|
16190
|
+
element.on('compositionstart', function(data) {
|
16191
|
+
composing = true;
|
16192
|
+
});
|
15864
16193
|
|
15865
|
-
|
15866
|
-
|
15867
|
-
|
16194
|
+
element.on('compositionend', function() {
|
16195
|
+
composing = false;
|
16196
|
+
listener();
|
16197
|
+
});
|
16198
|
+
}
|
15868
16199
|
|
15869
16200
|
var listener = function() {
|
15870
16201
|
if (composing) return;
|
@@ -15878,9 +16209,13 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
15878
16209
|
}
|
15879
16210
|
|
15880
16211
|
if (ctrl.$viewValue !== value) {
|
15881
|
-
scope
|
16212
|
+
if (scope.$$phase) {
|
15882
16213
|
ctrl.$setViewValue(value);
|
15883
|
-
}
|
16214
|
+
} else {
|
16215
|
+
scope.$apply(function() {
|
16216
|
+
ctrl.$setViewValue(value);
|
16217
|
+
});
|
16218
|
+
}
|
15884
16219
|
}
|
15885
16220
|
};
|
15886
16221
|
|
@@ -15929,22 +16264,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
15929
16264
|
patternValidator,
|
15930
16265
|
match;
|
15931
16266
|
|
15932
|
-
var validate = function(regexp, value) {
|
15933
|
-
if (ctrl.$isEmpty(value) || regexp.test(value)) {
|
15934
|
-
ctrl.$setValidity('pattern', true);
|
15935
|
-
return value;
|
15936
|
-
} else {
|
15937
|
-
ctrl.$setValidity('pattern', false);
|
15938
|
-
return undefined;
|
15939
|
-
}
|
15940
|
-
};
|
15941
|
-
|
15942
16267
|
if (pattern) {
|
16268
|
+
var validateRegex = function(regexp, value) {
|
16269
|
+
return validate(ctrl, 'pattern', ctrl.$isEmpty(value) || regexp.test(value), value);
|
16270
|
+
};
|
15943
16271
|
match = pattern.match(/^\/(.*)\/([gim]*)$/);
|
15944
16272
|
if (match) {
|
15945
16273
|
pattern = new RegExp(match[1], match[2]);
|
15946
16274
|
patternValidator = function(value) {
|
15947
|
-
return
|
16275
|
+
return validateRegex(pattern, value);
|
15948
16276
|
};
|
15949
16277
|
} else {
|
15950
16278
|
patternValidator = function(value) {
|
@@ -15955,7 +16283,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
15955
16283
|
'Expected {0} to be a RegExp but was {1}. Element: {2}', pattern,
|
15956
16284
|
patternObj, startingTag(element));
|
15957
16285
|
}
|
15958
|
-
return
|
16286
|
+
return validateRegex(patternObj, value);
|
15959
16287
|
};
|
15960
16288
|
}
|
15961
16289
|
|
@@ -15967,13 +16295,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
15967
16295
|
if (attr.ngMinlength) {
|
15968
16296
|
var minlength = int(attr.ngMinlength);
|
15969
16297
|
var minLengthValidator = function(value) {
|
15970
|
-
|
15971
|
-
ctrl.$setValidity('minlength', false);
|
15972
|
-
return undefined;
|
15973
|
-
} else {
|
15974
|
-
ctrl.$setValidity('minlength', true);
|
15975
|
-
return value;
|
15976
|
-
}
|
16298
|
+
return validate(ctrl, 'minlength', ctrl.$isEmpty(value) || value.length >= minlength, value);
|
15977
16299
|
};
|
15978
16300
|
|
15979
16301
|
ctrl.$parsers.push(minLengthValidator);
|
@@ -15984,13 +16306,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
15984
16306
|
if (attr.ngMaxlength) {
|
15985
16307
|
var maxlength = int(attr.ngMaxlength);
|
15986
16308
|
var maxLengthValidator = function(value) {
|
15987
|
-
|
15988
|
-
ctrl.$setValidity('maxlength', false);
|
15989
|
-
return undefined;
|
15990
|
-
} else {
|
15991
|
-
ctrl.$setValidity('maxlength', true);
|
15992
|
-
return value;
|
15993
|
-
}
|
16309
|
+
return validate(ctrl, 'maxlength', ctrl.$isEmpty(value) || value.length <= maxlength, value);
|
15994
16310
|
};
|
15995
16311
|
|
15996
16312
|
ctrl.$parsers.push(maxLengthValidator);
|
@@ -16019,13 +16335,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
16019
16335
|
if (attr.min) {
|
16020
16336
|
var minValidator = function(value) {
|
16021
16337
|
var min = parseFloat(attr.min);
|
16022
|
-
|
16023
|
-
ctrl.$setValidity('min', false);
|
16024
|
-
return undefined;
|
16025
|
-
} else {
|
16026
|
-
ctrl.$setValidity('min', true);
|
16027
|
-
return value;
|
16028
|
-
}
|
16338
|
+
return validate(ctrl, 'min', ctrl.$isEmpty(value) || value >= min, value);
|
16029
16339
|
};
|
16030
16340
|
|
16031
16341
|
ctrl.$parsers.push(minValidator);
|
@@ -16035,13 +16345,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
16035
16345
|
if (attr.max) {
|
16036
16346
|
var maxValidator = function(value) {
|
16037
16347
|
var max = parseFloat(attr.max);
|
16038
|
-
|
16039
|
-
ctrl.$setValidity('max', false);
|
16040
|
-
return undefined;
|
16041
|
-
} else {
|
16042
|
-
ctrl.$setValidity('max', true);
|
16043
|
-
return value;
|
16044
|
-
}
|
16348
|
+
return validate(ctrl, 'max', ctrl.$isEmpty(value) || value <= max, value);
|
16045
16349
|
};
|
16046
16350
|
|
16047
16351
|
ctrl.$parsers.push(maxValidator);
|
@@ -16049,14 +16353,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
16049
16353
|
}
|
16050
16354
|
|
16051
16355
|
ctrl.$formatters.push(function(value) {
|
16052
|
-
|
16053
|
-
if (ctrl.$isEmpty(value) || isNumber(value)) {
|
16054
|
-
ctrl.$setValidity('number', true);
|
16055
|
-
return value;
|
16056
|
-
} else {
|
16057
|
-
ctrl.$setValidity('number', false);
|
16058
|
-
return undefined;
|
16059
|
-
}
|
16356
|
+
return validate(ctrl, 'number', ctrl.$isEmpty(value) || isNumber(value), value);
|
16060
16357
|
});
|
16061
16358
|
}
|
16062
16359
|
|
@@ -16064,13 +16361,7 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
16064
16361
|
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
16065
16362
|
|
16066
16363
|
var urlValidator = function(value) {
|
16067
|
-
|
16068
|
-
ctrl.$setValidity('url', true);
|
16069
|
-
return value;
|
16070
|
-
} else {
|
16071
|
-
ctrl.$setValidity('url', false);
|
16072
|
-
return undefined;
|
16073
|
-
}
|
16364
|
+
return validate(ctrl, 'url', ctrl.$isEmpty(value) || URL_REGEXP.test(value), value);
|
16074
16365
|
};
|
16075
16366
|
|
16076
16367
|
ctrl.$formatters.push(urlValidator);
|
@@ -16081,13 +16372,7 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
16081
16372
|
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
16082
16373
|
|
16083
16374
|
var emailValidator = function(value) {
|
16084
|
-
|
16085
|
-
ctrl.$setValidity('email', true);
|
16086
|
-
return value;
|
16087
|
-
} else {
|
16088
|
-
ctrl.$setValidity('email', false);
|
16089
|
-
return undefined;
|
16090
|
-
}
|
16375
|
+
return validate(ctrl, 'email', ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value), value);
|
16091
16376
|
};
|
16092
16377
|
|
16093
16378
|
ctrl.$formatters.push(emailValidator);
|
@@ -16231,44 +16516,59 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
|
16231
16516
|
<tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
|
16232
16517
|
</div>
|
16233
16518
|
</doc:source>
|
16234
|
-
<doc:
|
16519
|
+
<doc:protractor>
|
16520
|
+
var user = element(by.binding('{{user}}'));
|
16521
|
+
var userNameValid = element(by.binding('myForm.userName.$valid'));
|
16522
|
+
var lastNameValid = element(by.binding('myForm.lastName.$valid'));
|
16523
|
+
var lastNameError = element(by.binding('myForm.lastName.$error'));
|
16524
|
+
var formValid = element(by.binding('myForm.$valid'));
|
16525
|
+
var userNameInput = element(by.model('user.name'));
|
16526
|
+
var userLastInput = element(by.model('user.last'));
|
16527
|
+
|
16235
16528
|
it('should initialize to model', function() {
|
16236
|
-
expect(
|
16237
|
-
expect(
|
16238
|
-
expect(
|
16529
|
+
expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
|
16530
|
+
expect(userNameValid.getText()).toContain('true');
|
16531
|
+
expect(formValid.getText()).toContain('true');
|
16239
16532
|
});
|
16240
16533
|
|
16241
16534
|
it('should be invalid if empty when required', function() {
|
16242
|
-
|
16243
|
-
|
16244
|
-
|
16245
|
-
expect(
|
16535
|
+
userNameInput.clear();
|
16536
|
+
userNameInput.sendKeys('');
|
16537
|
+
|
16538
|
+
expect(user.getText()).toContain('{"last":"visitor"}');
|
16539
|
+
expect(userNameValid.getText()).toContain('false');
|
16540
|
+
expect(formValid.getText()).toContain('false');
|
16246
16541
|
});
|
16247
16542
|
|
16248
16543
|
it('should be valid if empty when min length is set', function() {
|
16249
|
-
|
16250
|
-
|
16251
|
-
|
16252
|
-
expect(
|
16544
|
+
userLastInput.clear();
|
16545
|
+
userLastInput.sendKeys('');
|
16546
|
+
|
16547
|
+
expect(user.getText()).toContain('{"name":"guest","last":""}');
|
16548
|
+
expect(lastNameValid.getText()).toContain('true');
|
16549
|
+
expect(formValid.getText()).toContain('true');
|
16253
16550
|
});
|
16254
16551
|
|
16255
16552
|
it('should be invalid if less than required min length', function() {
|
16256
|
-
|
16257
|
-
|
16258
|
-
|
16259
|
-
expect(
|
16260
|
-
expect(
|
16553
|
+
userLastInput.clear();
|
16554
|
+
userLastInput.sendKeys('xx');
|
16555
|
+
|
16556
|
+
expect(user.getText()).toContain('{"name":"guest"}');
|
16557
|
+
expect(lastNameValid.getText()).toContain('false');
|
16558
|
+
expect(lastNameError.getText()).toContain('minlength');
|
16559
|
+
expect(formValid.getText()).toContain('false');
|
16261
16560
|
});
|
16262
16561
|
|
16263
16562
|
it('should be invalid if longer than max length', function() {
|
16264
|
-
|
16265
|
-
|
16266
|
-
|
16267
|
-
expect(
|
16268
|
-
expect(
|
16269
|
-
expect(
|
16563
|
+
userLastInput.clear();
|
16564
|
+
userLastInput.sendKeys('some ridiculously long name');
|
16565
|
+
|
16566
|
+
expect(user.getText()).toContain('{"name":"guest"}');
|
16567
|
+
expect(lastNameValid.getText()).toContain('false');
|
16568
|
+
expect(lastNameError.getText()).toContain('maxlength');
|
16569
|
+
expect(formValid.getText()).toContain('false');
|
16270
16570
|
});
|
16271
|
-
</doc:
|
16571
|
+
</doc:protractor>
|
16272
16572
|
</doc:example>
|
16273
16573
|
*/
|
16274
16574
|
var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {
|
@@ -16400,14 +16700,23 @@ var VALID_CLASS = 'ng-valid',
|
|
16400
16700
|
<textarea ng-model="userContent"></textarea>
|
16401
16701
|
</form>
|
16402
16702
|
</file>
|
16403
|
-
<file name="
|
16703
|
+
<file name="protractorTest.js">
|
16404
16704
|
it('should data-bind and become invalid', function() {
|
16405
|
-
|
16705
|
+
if (browser.params.browser = 'safari') {
|
16706
|
+
// SafariDriver can't handle contenteditable.
|
16707
|
+
return;
|
16708
|
+
};
|
16709
|
+
var contentEditable = element(by.css('.doc-example-live [contenteditable]'));
|
16710
|
+
|
16711
|
+
expect(contentEditable.getText()).toEqual('Change me!');
|
16712
|
+
|
16713
|
+
// Firefox driver doesn't trigger the proper events on 'clear', so do this hack
|
16714
|
+
contentEditable.click();
|
16715
|
+
contentEditable.sendKeys(protractor.Key.chord(protractor.Key.COMMAND, "a"));
|
16716
|
+
contentEditable.sendKeys(protractor.Key.BACK_SPACE);
|
16406
16717
|
|
16407
|
-
expect(contentEditable.
|
16408
|
-
|
16409
|
-
expect(contentEditable.text()).toEqual('');
|
16410
|
-
expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/);
|
16718
|
+
expect(contentEditable.getText()).toEqual('');
|
16719
|
+
expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
|
16411
16720
|
});
|
16412
16721
|
</file>
|
16413
16722
|
* </example>
|
@@ -16460,6 +16769,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
16460
16769
|
* You can override this for input directives whose concept of being empty is different to the
|
16461
16770
|
* default. The `checkboxInputType` directive does this because in its case a value of `false`
|
16462
16771
|
* implies empty.
|
16772
|
+
*
|
16773
|
+
* @param {*} value Reference to check.
|
16774
|
+
* @returns {boolean} True if `value` is empty.
|
16463
16775
|
*/
|
16464
16776
|
this.$isEmpty = function(value) {
|
16465
16777
|
return isUndefined(value) || value === '' || value === null || value !== value;
|
@@ -16687,7 +16999,10 @@ var ngModelDirective = function() {
|
|
16687
16999
|
* @name ng.directive:ngChange
|
16688
17000
|
*
|
16689
17001
|
* @description
|
16690
|
-
* Evaluate given expression when user changes the input.
|
17002
|
+
* Evaluate the given expression when the user changes the input.
|
17003
|
+
* The expression is evaluated immediately, unlike the JavaScript onchange event
|
17004
|
+
* which only triggers at the end of a change (usually, when the user leaves the
|
17005
|
+
* form element or presses the return key).
|
16691
17006
|
* The expression is not evaluated when the value change is coming from the model.
|
16692
17007
|
*
|
16693
17008
|
* Note, this directive requires `ngModel` to be present.
|
@@ -16711,24 +17026,30 @@ var ngModelDirective = function() {
|
|
16711
17026
|
* <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
|
16712
17027
|
* <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
|
16713
17028
|
* <label for="ng-change-example2">Confirmed</label><br />
|
16714
|
-
* debug = {{confirmed}}
|
16715
|
-
* counter = {{counter}}
|
17029
|
+
* <tt>debug = {{confirmed}}</tt><br/>
|
17030
|
+
* <tt>counter = {{counter}}</tt><br/>
|
16716
17031
|
* </div>
|
16717
17032
|
* </doc:source>
|
16718
|
-
* <doc:
|
17033
|
+
* <doc:protractor>
|
17034
|
+
* var counter = element(by.binding('counter'));
|
17035
|
+
* var debug = element(by.binding('confirmed'));
|
17036
|
+
*
|
16719
17037
|
* it('should evaluate the expression if changing from view', function() {
|
16720
|
-
* expect(
|
16721
|
-
*
|
16722
|
-
*
|
16723
|
-
*
|
17038
|
+
* expect(counter.getText()).toContain('0');
|
17039
|
+
*
|
17040
|
+
* element(by.id('ng-change-example1')).click();
|
17041
|
+
*
|
17042
|
+
* expect(counter.getText()).toContain('1');
|
17043
|
+
* expect(debug.getText()).toContain('true');
|
16724
17044
|
* });
|
16725
17045
|
*
|
16726
17046
|
* it('should not evaluate the expression if changing from model', function() {
|
16727
|
-
* element('
|
16728
|
-
|
16729
|
-
* expect(
|
17047
|
+
* element(by.id('ng-change-example2')).click();
|
17048
|
+
|
17049
|
+
* expect(counter.getText()).toContain('0');
|
17050
|
+
* expect(debug.getText()).toContain('true');
|
16730
17051
|
* });
|
16731
|
-
* </doc:
|
17052
|
+
* </doc:protractor>
|
16732
17053
|
* </doc:example>
|
16733
17054
|
*/
|
16734
17055
|
var ngChangeDirective = valueFn({
|
@@ -16801,20 +17122,26 @@ var requiredDirective = function() {
|
|
16801
17122
|
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
16802
17123
|
</form>
|
16803
17124
|
</doc:source>
|
16804
|
-
<doc:
|
17125
|
+
<doc:protractor>
|
17126
|
+
var listInput = element(by.model('names'));
|
17127
|
+
var names = element(by.binding('{{names}}'));
|
17128
|
+
var valid = element(by.binding('myForm.namesInput.$valid'));
|
17129
|
+
var error = element(by.css('span.error'));
|
17130
|
+
|
16805
17131
|
it('should initialize to model', function() {
|
16806
|
-
expect(
|
16807
|
-
expect(
|
16808
|
-
expect(
|
17132
|
+
expect(names.getText()).toContain('["igor","misko","vojta"]');
|
17133
|
+
expect(valid.getText()).toContain('true');
|
17134
|
+
expect(error.getCssValue('display')).toBe('none');
|
16809
17135
|
});
|
16810
17136
|
|
16811
17137
|
it('should be invalid if empty', function() {
|
16812
|
-
|
16813
|
-
|
16814
|
-
|
16815
|
-
expect(
|
16816
|
-
|
16817
|
-
|
17138
|
+
listInput.clear();
|
17139
|
+
listInput.sendKeys('');
|
17140
|
+
|
17141
|
+
expect(names.getText()).toContain('');
|
17142
|
+
expect(valid.getText()).toContain('false');
|
17143
|
+
expect(error.getCssValue('display')).not.toBe('none'); });
|
17144
|
+
</doc:protractor>
|
16818
17145
|
</doc:example>
|
16819
17146
|
*/
|
16820
17147
|
var ngListDirective = function() {
|
@@ -16896,15 +17223,17 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
|
|
16896
17223
|
<div>You chose {{my.favorite}}</div>
|
16897
17224
|
</form>
|
16898
17225
|
</doc:source>
|
16899
|
-
<doc:
|
17226
|
+
<doc:protractor>
|
17227
|
+
var favorite = element(by.binding('my.favorite'));
|
17228
|
+
|
16900
17229
|
it('should initialize to model', function() {
|
16901
|
-
expect(
|
17230
|
+
expect(favorite.getText()).toContain('unicorns');
|
16902
17231
|
});
|
16903
17232
|
it('should bind the values to the inputs', function() {
|
16904
|
-
|
16905
|
-
expect(
|
17233
|
+
element.all(by.model('my.favorite')).get(0).click();
|
17234
|
+
expect(favorite.getText()).toContain('pizza');
|
16906
17235
|
});
|
16907
|
-
</doc:
|
17236
|
+
</doc:protractor>
|
16908
17237
|
</doc:example>
|
16909
17238
|
*/
|
16910
17239
|
var ngValueDirective = function() {
|
@@ -16964,13 +17293,17 @@ var ngValueDirective = function() {
|
|
16964
17293
|
Hello <span ng-bind="name"></span>!
|
16965
17294
|
</div>
|
16966
17295
|
</doc:source>
|
16967
|
-
<doc:
|
17296
|
+
<doc:protractor>
|
16968
17297
|
it('should check ng-bind', function() {
|
16969
|
-
|
16970
|
-
|
16971
|
-
|
17298
|
+
var exampleContainer = $('.doc-example-live');
|
17299
|
+
var nameInput = element(by.model('name'));
|
17300
|
+
|
17301
|
+
expect(exampleContainer.findElement(by.binding('name')).getText()).toBe('Whirled');
|
17302
|
+
nameInput.clear();
|
17303
|
+
nameInput.sendKeys('world');
|
17304
|
+
expect(exampleContainer.findElement(by.binding('name')).getText()).toBe('world');
|
16972
17305
|
});
|
16973
|
-
</doc:
|
17306
|
+
</doc:protractor>
|
16974
17307
|
</doc:example>
|
16975
17308
|
*/
|
16976
17309
|
var ngBindDirective = ngDirective(function(scope, element, attr) {
|
@@ -17016,20 +17349,22 @@ var ngBindDirective = ngDirective(function(scope, element, attr) {
|
|
17016
17349
|
<pre ng-bind-template="{{salutation}} {{name}}!"></pre>
|
17017
17350
|
</div>
|
17018
17351
|
</doc:source>
|
17019
|
-
<doc:
|
17352
|
+
<doc:protractor>
|
17020
17353
|
it('should check ng-bind', function() {
|
17021
|
-
|
17022
|
-
|
17023
|
-
|
17024
|
-
|
17025
|
-
|
17026
|
-
|
17027
|
-
|
17028
|
-
|
17029
|
-
|
17030
|
-
|
17354
|
+
var salutationElem = element(by.binding('salutation'));
|
17355
|
+
var salutationInput = element(by.model('salutation'));
|
17356
|
+
var nameInput = element(by.model('name'));
|
17357
|
+
|
17358
|
+
expect(salutationElem.getText()).toBe('Hello World!');
|
17359
|
+
|
17360
|
+
salutationInput.clear();
|
17361
|
+
salutationInput.sendKeys('Greetings');
|
17362
|
+
nameInput.clear();
|
17363
|
+
nameInput.sendKeys('user');
|
17364
|
+
|
17365
|
+
expect(salutationElem.getText()).toBe('Greetings user!');
|
17031
17366
|
});
|
17032
|
-
</doc:
|
17367
|
+
</doc:protractor>
|
17033
17368
|
</doc:example>
|
17034
17369
|
*/
|
17035
17370
|
var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
|
@@ -17082,12 +17417,10 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
|
|
17082
17417
|
}]);
|
17083
17418
|
</file>
|
17084
17419
|
|
17085
|
-
<file name="
|
17420
|
+
<file name="protractorTest.js">
|
17086
17421
|
it('should check ng-bind-html', function() {
|
17087
|
-
expect(
|
17088
|
-
|
17089
|
-
'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>'
|
17090
|
-
);
|
17422
|
+
expect(element(by.binding('myHTML')).getText()).toBe(
|
17423
|
+
'I am an HTMLstring with links! and other stuff');
|
17091
17424
|
});
|
17092
17425
|
</file>
|
17093
17426
|
</example>
|
@@ -17219,31 +17552,34 @@ function classDirective(name, selector) {
|
|
17219
17552
|
color: red;
|
17220
17553
|
}
|
17221
17554
|
</file>
|
17222
|
-
<file name="
|
17555
|
+
<file name="protractorTest.js">
|
17556
|
+
var ps = element.all(by.css('.doc-example-live p'));
|
17557
|
+
|
17223
17558
|
it('should let you toggle the class', function() {
|
17224
17559
|
|
17225
|
-
expect(
|
17226
|
-
expect(
|
17560
|
+
expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
|
17561
|
+
expect(ps.first().getAttribute('class')).not.toMatch(/red/);
|
17227
17562
|
|
17228
|
-
|
17229
|
-
expect(
|
17563
|
+
element(by.model('important')).click();
|
17564
|
+
expect(ps.first().getAttribute('class')).toMatch(/bold/);
|
17230
17565
|
|
17231
|
-
|
17232
|
-
expect(
|
17566
|
+
element(by.model('error')).click();
|
17567
|
+
expect(ps.first().getAttribute('class')).toMatch(/red/);
|
17233
17568
|
});
|
17234
17569
|
|
17235
17570
|
it('should let you toggle string example', function() {
|
17236
|
-
expect(
|
17237
|
-
|
17238
|
-
|
17571
|
+
expect(ps.get(1).getAttribute('class')).toBe('');
|
17572
|
+
element(by.model('style')).clear();
|
17573
|
+
element(by.model('style')).sendKeys('red');
|
17574
|
+
expect(ps.get(1).getAttribute('class')).toBe('red');
|
17239
17575
|
});
|
17240
17576
|
|
17241
17577
|
it('array example should have 3 classes', function() {
|
17242
|
-
expect(
|
17243
|
-
|
17244
|
-
|
17245
|
-
|
17246
|
-
expect(
|
17578
|
+
expect(ps.last().getAttribute('class')).toBe('');
|
17579
|
+
element(by.model('style1')).sendKeys('bold');
|
17580
|
+
element(by.model('style2')).sendKeys('strike');
|
17581
|
+
element(by.model('style3')).sendKeys('red');
|
17582
|
+
expect(ps.last().getAttribute('class')).toBe('bold strike red');
|
17247
17583
|
});
|
17248
17584
|
</file>
|
17249
17585
|
</example>
|
@@ -17254,8 +17590,8 @@ function classDirective(name, selector) {
|
|
17254
17590
|
|
17255
17591
|
<example animations="true">
|
17256
17592
|
<file name="index.html">
|
17257
|
-
<input type="button" value="set" ng-click="myVar='my-class'">
|
17258
|
-
<input type="button" value="clear" ng-click="myVar=''">
|
17593
|
+
<input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
|
17594
|
+
<input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
|
17259
17595
|
<br>
|
17260
17596
|
<span class="base-class" ng-class="myVar">Sample Text</span>
|
17261
17597
|
</file>
|
@@ -17270,19 +17606,19 @@ function classDirective(name, selector) {
|
|
17270
17606
|
font-size:3em;
|
17271
17607
|
}
|
17272
17608
|
</file>
|
17273
|
-
<file name="
|
17609
|
+
<file name="protractorTest.js">
|
17274
17610
|
it('should check ng-class', function() {
|
17275
|
-
expect(element('.
|
17611
|
+
expect(element(by.css('.base-class')).getAttribute('class')).not.
|
17276
17612
|
toMatch(/my-class/);
|
17277
17613
|
|
17278
|
-
|
17614
|
+
element(by.id('setbtn')).click();
|
17279
17615
|
|
17280
|
-
expect(element('.
|
17616
|
+
expect(element(by.css('.base-class')).getAttribute('class')).
|
17281
17617
|
toMatch(/my-class/);
|
17282
17618
|
|
17283
|
-
|
17619
|
+
element(by.id('clearbtn')).click();
|
17284
17620
|
|
17285
|
-
expect(element('.
|
17621
|
+
expect(element(by.css('.base-class')).getAttribute('class')).not.
|
17286
17622
|
toMatch(/my-class/);
|
17287
17623
|
});
|
17288
17624
|
</file>
|
@@ -17334,11 +17670,11 @@ var ngClassDirective = classDirective('', true);
|
|
17334
17670
|
color: blue;
|
17335
17671
|
}
|
17336
17672
|
</file>
|
17337
|
-
<file name="
|
17673
|
+
<file name="protractorTest.js">
|
17338
17674
|
it('should check ng-class-odd and ng-class-even', function() {
|
17339
|
-
expect(element('
|
17675
|
+
expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
|
17340
17676
|
toMatch(/odd/);
|
17341
|
-
expect(element('
|
17677
|
+
expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
|
17342
17678
|
toMatch(/even/);
|
17343
17679
|
});
|
17344
17680
|
</file>
|
@@ -17382,11 +17718,11 @@ var ngClassOddDirective = classDirective('Odd', 0);
|
|
17382
17718
|
color: blue;
|
17383
17719
|
}
|
17384
17720
|
</file>
|
17385
|
-
<file name="
|
17721
|
+
<file name="protractorTest.js">
|
17386
17722
|
it('should check ng-class-odd and ng-class-even', function() {
|
17387
|
-
expect(element('
|
17723
|
+
expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
|
17388
17724
|
toMatch(/odd/);
|
17389
|
-
expect(element('
|
17725
|
+
expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
|
17390
17726
|
toMatch(/even/);
|
17391
17727
|
});
|
17392
17728
|
</file>
|
@@ -17429,7 +17765,7 @@ var ngClassEvenDirective = classDirective('Even', 1);
|
|
17429
17765
|
*
|
17430
17766
|
* Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
|
17431
17767
|
* cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
|
17432
|
-
* class `
|
17768
|
+
* class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below.
|
17433
17769
|
*
|
17434
17770
|
* @element ANY
|
17435
17771
|
*
|
@@ -17439,14 +17775,14 @@ var ngClassEvenDirective = classDirective('Even', 1);
|
|
17439
17775
|
<div id="template1" ng-cloak>{{ 'hello' }}</div>
|
17440
17776
|
<div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div>
|
17441
17777
|
</doc:source>
|
17442
|
-
<doc:
|
17778
|
+
<doc:protractor>
|
17443
17779
|
it('should remove the template directive and css class', function() {
|
17444
|
-
expect(
|
17445
|
-
|
17446
|
-
expect(
|
17447
|
-
|
17780
|
+
expect($('.doc-example-live #template1').getAttribute('ng-cloak')).
|
17781
|
+
toBeNull();
|
17782
|
+
expect($('.doc-example-live #template2').getAttribute('ng-cloak')).
|
17783
|
+
toBeNull();
|
17448
17784
|
});
|
17449
|
-
</doc:
|
17785
|
+
</doc:protractor>
|
17450
17786
|
</doc:example>
|
17451
17787
|
*
|
17452
17788
|
*/
|
@@ -17539,22 +17875,36 @@ var ngCloakDirective = ngDirective({
|
|
17539
17875
|
</ul>
|
17540
17876
|
</div>
|
17541
17877
|
</doc:source>
|
17542
|
-
<doc:
|
17878
|
+
<doc:protractor>
|
17543
17879
|
it('should check controller as', function() {
|
17544
|
-
|
17545
|
-
|
17546
|
-
|
17547
|
-
|
17548
|
-
|
17549
|
-
|
17550
|
-
|
17551
|
-
|
17552
|
-
|
17553
|
-
|
17554
|
-
expect(
|
17555
|
-
|
17880
|
+
var container = element(by.id('ctrl-as-exmpl'));
|
17881
|
+
|
17882
|
+
expect(container.findElement(by.model('settings.name'))
|
17883
|
+
.getAttribute('value')).toBe('John Smith');
|
17884
|
+
|
17885
|
+
var firstRepeat =
|
17886
|
+
container.findElement(by.repeater('contact in settings.contacts').row(0));
|
17887
|
+
var secondRepeat =
|
17888
|
+
container.findElement(by.repeater('contact in settings.contacts').row(1));
|
17889
|
+
|
17890
|
+
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
17891
|
+
.toBe('408 555 1212');
|
17892
|
+
expect(secondRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
17893
|
+
.toBe('john.smith@example.org');
|
17894
|
+
|
17895
|
+
firstRepeat.findElement(by.linkText('clear')).click()
|
17896
|
+
|
17897
|
+
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
17898
|
+
.toBe('');
|
17899
|
+
|
17900
|
+
container.findElement(by.linkText('add')).click();
|
17901
|
+
|
17902
|
+
expect(container.findElement(by.repeater('contact in settings.contacts').row(2))
|
17903
|
+
.findElement(by.model('contact.value'))
|
17904
|
+
.getAttribute('value'))
|
17905
|
+
.toBe('yourname@example.org');
|
17556
17906
|
});
|
17557
|
-
</doc:
|
17907
|
+
</doc:protractor>
|
17558
17908
|
</doc:example>
|
17559
17909
|
<doc:example>
|
17560
17910
|
<doc:source>
|
@@ -17602,22 +17952,36 @@ var ngCloakDirective = ngDirective({
|
|
17602
17952
|
</ul>
|
17603
17953
|
</div>
|
17604
17954
|
</doc:source>
|
17605
|
-
<doc:
|
17955
|
+
<doc:protractor>
|
17606
17956
|
it('should check controller', function() {
|
17607
|
-
|
17608
|
-
|
17609
|
-
|
17610
|
-
|
17611
|
-
|
17612
|
-
|
17613
|
-
|
17614
|
-
|
17615
|
-
|
17616
|
-
|
17617
|
-
expect(
|
17618
|
-
|
17957
|
+
var container = element(by.id('ctrl-exmpl'));
|
17958
|
+
|
17959
|
+
expect(container.findElement(by.model('name'))
|
17960
|
+
.getAttribute('value')).toBe('John Smith');
|
17961
|
+
|
17962
|
+
var firstRepeat =
|
17963
|
+
container.findElement(by.repeater('contact in contacts').row(0));
|
17964
|
+
var secondRepeat =
|
17965
|
+
container.findElement(by.repeater('contact in contacts').row(1));
|
17966
|
+
|
17967
|
+
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
17968
|
+
.toBe('408 555 1212');
|
17969
|
+
expect(secondRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
17970
|
+
.toBe('john.smith@example.org');
|
17971
|
+
|
17972
|
+
firstRepeat.findElement(by.linkText('clear')).click()
|
17973
|
+
|
17974
|
+
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
17975
|
+
.toBe('');
|
17976
|
+
|
17977
|
+
container.findElement(by.linkText('add')).click();
|
17978
|
+
|
17979
|
+
expect(container.findElement(by.repeater('contact in contacts').row(2))
|
17980
|
+
.findElement(by.model('contact.value'))
|
17981
|
+
.getAttribute('value'))
|
17982
|
+
.toBe('yourname@example.org');
|
17619
17983
|
});
|
17620
|
-
</doc:
|
17984
|
+
</doc:protractor>
|
17621
17985
|
</doc:example>
|
17622
17986
|
|
17623
17987
|
*/
|
@@ -17680,6 +18044,7 @@ var ngControllerDirective = [function() {
|
|
17680
18044
|
* an element is clicked.
|
17681
18045
|
*
|
17682
18046
|
* @element ANY
|
18047
|
+
* @priority 0
|
17683
18048
|
* @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
|
17684
18049
|
* click. (Event object is available as `$event`)
|
17685
18050
|
*
|
@@ -17691,13 +18056,13 @@ var ngControllerDirective = [function() {
|
|
17691
18056
|
</button>
|
17692
18057
|
count: {{count}}
|
17693
18058
|
</doc:source>
|
17694
|
-
<doc:
|
18059
|
+
<doc:protractor>
|
17695
18060
|
it('should check ng-click', function() {
|
17696
|
-
expect(binding('count')).
|
17697
|
-
element('.doc-example-live
|
17698
|
-
expect(binding('count')).
|
18061
|
+
expect(element(by.binding('count')).getText()).toMatch('0');
|
18062
|
+
element(by.css('.doc-example-live button')).click();
|
18063
|
+
expect(element(by.binding('count')).getText()).toMatch('1');
|
17699
18064
|
});
|
17700
|
-
</doc:
|
18065
|
+
</doc:protractor>
|
17701
18066
|
</doc:example>
|
17702
18067
|
*/
|
17703
18068
|
/*
|
@@ -17736,11 +18101,19 @@ forEach(
|
|
17736
18101
|
* The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
|
17737
18102
|
*
|
17738
18103
|
* @element ANY
|
18104
|
+
* @priority 0
|
17739
18105
|
* @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
|
17740
18106
|
* a dblclick. (The Event object is available as `$event`)
|
17741
18107
|
*
|
17742
18108
|
* @example
|
17743
|
-
|
18109
|
+
<doc:example>
|
18110
|
+
<doc:source>
|
18111
|
+
<button ng-dblclick="count = count + 1" ng-init="count=0">
|
18112
|
+
Increment (on double click)
|
18113
|
+
</button>
|
18114
|
+
count: {{count}}
|
18115
|
+
</doc:source>
|
18116
|
+
</doc:example>
|
17744
18117
|
*/
|
17745
18118
|
|
17746
18119
|
|
@@ -17752,11 +18125,19 @@ forEach(
|
|
17752
18125
|
* The ngMousedown directive allows you to specify custom behavior on mousedown event.
|
17753
18126
|
*
|
17754
18127
|
* @element ANY
|
18128
|
+
* @priority 0
|
17755
18129
|
* @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
|
17756
18130
|
* mousedown. (Event object is available as `$event`)
|
17757
18131
|
*
|
17758
18132
|
* @example
|
17759
|
-
|
18133
|
+
<doc:example>
|
18134
|
+
<doc:source>
|
18135
|
+
<button ng-mousedown="count = count + 1" ng-init="count=0">
|
18136
|
+
Increment (on mouse down)
|
18137
|
+
</button>
|
18138
|
+
count: {{count}}
|
18139
|
+
</doc:source>
|
18140
|
+
</doc:example>
|
17760
18141
|
*/
|
17761
18142
|
|
17762
18143
|
|
@@ -17768,11 +18149,19 @@ forEach(
|
|
17768
18149
|
* Specify custom behavior on mouseup event.
|
17769
18150
|
*
|
17770
18151
|
* @element ANY
|
18152
|
+
* @priority 0
|
17771
18153
|
* @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
|
17772
18154
|
* mouseup. (Event object is available as `$event`)
|
17773
18155
|
*
|
17774
18156
|
* @example
|
17775
|
-
|
18157
|
+
<doc:example>
|
18158
|
+
<doc:source>
|
18159
|
+
<button ng-mouseup="count = count + 1" ng-init="count=0">
|
18160
|
+
Increment (on mouse up)
|
18161
|
+
</button>
|
18162
|
+
count: {{count}}
|
18163
|
+
</doc:source>
|
18164
|
+
</doc:example>
|
17776
18165
|
*/
|
17777
18166
|
|
17778
18167
|
/**
|
@@ -17783,11 +18172,19 @@ forEach(
|
|
17783
18172
|
* Specify custom behavior on mouseover event.
|
17784
18173
|
*
|
17785
18174
|
* @element ANY
|
18175
|
+
* @priority 0
|
17786
18176
|
* @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
|
17787
18177
|
* mouseover. (Event object is available as `$event`)
|
17788
18178
|
*
|
17789
18179
|
* @example
|
17790
|
-
|
18180
|
+
<doc:example>
|
18181
|
+
<doc:source>
|
18182
|
+
<button ng-mouseover="count = count + 1" ng-init="count=0">
|
18183
|
+
Increment (when mouse is over)
|
18184
|
+
</button>
|
18185
|
+
count: {{count}}
|
18186
|
+
</doc:source>
|
18187
|
+
</doc:example>
|
17791
18188
|
*/
|
17792
18189
|
|
17793
18190
|
|
@@ -17799,11 +18196,19 @@ forEach(
|
|
17799
18196
|
* Specify custom behavior on mouseenter event.
|
17800
18197
|
*
|
17801
18198
|
* @element ANY
|
18199
|
+
* @priority 0
|
17802
18200
|
* @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
|
17803
18201
|
* mouseenter. (Event object is available as `$event`)
|
17804
18202
|
*
|
17805
18203
|
* @example
|
17806
|
-
|
18204
|
+
<doc:example>
|
18205
|
+
<doc:source>
|
18206
|
+
<button ng-mouseenter="count = count + 1" ng-init="count=0">
|
18207
|
+
Increment (when mouse enters)
|
18208
|
+
</button>
|
18209
|
+
count: {{count}}
|
18210
|
+
</doc:source>
|
18211
|
+
</doc:example>
|
17807
18212
|
*/
|
17808
18213
|
|
17809
18214
|
|
@@ -17815,11 +18220,19 @@ forEach(
|
|
17815
18220
|
* Specify custom behavior on mouseleave event.
|
17816
18221
|
*
|
17817
18222
|
* @element ANY
|
18223
|
+
* @priority 0
|
17818
18224
|
* @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
|
17819
18225
|
* mouseleave. (Event object is available as `$event`)
|
17820
18226
|
*
|
17821
18227
|
* @example
|
17822
|
-
|
18228
|
+
<doc:example>
|
18229
|
+
<doc:source>
|
18230
|
+
<button ng-mouseleave="count = count + 1" ng-init="count=0">
|
18231
|
+
Increment (when mouse leaves)
|
18232
|
+
</button>
|
18233
|
+
count: {{count}}
|
18234
|
+
</doc:source>
|
18235
|
+
</doc:example>
|
17823
18236
|
*/
|
17824
18237
|
|
17825
18238
|
|
@@ -17831,11 +18244,19 @@ forEach(
|
|
17831
18244
|
* Specify custom behavior on mousemove event.
|
17832
18245
|
*
|
17833
18246
|
* @element ANY
|
18247
|
+
* @priority 0
|
17834
18248
|
* @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
|
17835
18249
|
* mousemove. (Event object is available as `$event`)
|
17836
18250
|
*
|
17837
18251
|
* @example
|
17838
|
-
|
18252
|
+
<doc:example>
|
18253
|
+
<doc:source>
|
18254
|
+
<button ng-mousemove="count = count + 1" ng-init="count=0">
|
18255
|
+
Increment (when mouse moves)
|
18256
|
+
</button>
|
18257
|
+
count: {{count}}
|
18258
|
+
</doc:source>
|
18259
|
+
</doc:example>
|
17839
18260
|
*/
|
17840
18261
|
|
17841
18262
|
|
@@ -17847,11 +18268,17 @@ forEach(
|
|
17847
18268
|
* Specify custom behavior on keydown event.
|
17848
18269
|
*
|
17849
18270
|
* @element ANY
|
18271
|
+
* @priority 0
|
17850
18272
|
* @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
|
17851
18273
|
* keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
17852
18274
|
*
|
17853
18275
|
* @example
|
17854
|
-
|
18276
|
+
<doc:example>
|
18277
|
+
<doc:source>
|
18278
|
+
<input ng-keydown="count = count + 1" ng-init="count=0">
|
18279
|
+
key down count: {{count}}
|
18280
|
+
</doc:source>
|
18281
|
+
</doc:example>
|
17855
18282
|
*/
|
17856
18283
|
|
17857
18284
|
|
@@ -17863,11 +18290,17 @@ forEach(
|
|
17863
18290
|
* Specify custom behavior on keyup event.
|
17864
18291
|
*
|
17865
18292
|
* @element ANY
|
18293
|
+
* @priority 0
|
17866
18294
|
* @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
|
17867
18295
|
* keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
17868
18296
|
*
|
17869
18297
|
* @example
|
17870
|
-
|
18298
|
+
<doc:example>
|
18299
|
+
<doc:source>
|
18300
|
+
<input ng-keyup="count = count + 1" ng-init="count=0">
|
18301
|
+
key up count: {{count}}
|
18302
|
+
</doc:source>
|
18303
|
+
</doc:example>
|
17871
18304
|
*/
|
17872
18305
|
|
17873
18306
|
|
@@ -17883,7 +18316,12 @@ forEach(
|
|
17883
18316
|
* keypress. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
17884
18317
|
*
|
17885
18318
|
* @example
|
17886
|
-
|
18319
|
+
<doc:example>
|
18320
|
+
<doc:source>
|
18321
|
+
<input ng-keypress="count = count + 1" ng-init="count=0">
|
18322
|
+
key press count: {{count}}
|
18323
|
+
</doc:source>
|
18324
|
+
</doc:example>
|
17887
18325
|
*/
|
17888
18326
|
|
17889
18327
|
|
@@ -17895,10 +18333,11 @@ forEach(
|
|
17895
18333
|
* Enables binding angular expressions to onsubmit events.
|
17896
18334
|
*
|
17897
18335
|
* Additionally it prevents the default action (which for form means sending the request to the
|
17898
|
-
* server and reloading the current page)
|
17899
|
-
*
|
18336
|
+
* server and reloading the current page), but only if the form does not contain `action`,
|
18337
|
+
* `data-action`, or `x-action` attributes.
|
17900
18338
|
*
|
17901
18339
|
* @element form
|
18340
|
+
* @priority 0
|
17902
18341
|
* @param {expression} ngSubmit {@link guide/expression Expression} to eval. (Event object is available as `$event`)
|
17903
18342
|
*
|
17904
18343
|
* @example
|
@@ -17923,20 +18362,20 @@ forEach(
|
|
17923
18362
|
<pre>list={{list}}</pre>
|
17924
18363
|
</form>
|
17925
18364
|
</doc:source>
|
17926
|
-
<doc:
|
18365
|
+
<doc:protractor>
|
17927
18366
|
it('should check ng-submit', function() {
|
17928
|
-
expect(binding('list')).toBe('[]');
|
17929
|
-
element('.doc-example-live #submit').click();
|
17930
|
-
expect(binding('list')).
|
17931
|
-
expect(input('text').
|
18367
|
+
expect(element(by.binding('list')).getText()).toBe('list=[]');
|
18368
|
+
element(by.css('.doc-example-live #submit')).click();
|
18369
|
+
expect(element(by.binding('list')).getText()).toContain('hello');
|
18370
|
+
expect(element(by.input('text')).getAttribute('value')).toBe('');
|
17932
18371
|
});
|
17933
18372
|
it('should ignore empty strings', function() {
|
17934
|
-
expect(binding('list')).toBe('[]');
|
17935
|
-
element('.doc-example-live #submit').click();
|
17936
|
-
element('.doc-example-live #submit').click();
|
17937
|
-
expect(binding('list')).
|
17938
|
-
|
17939
|
-
</doc:
|
18373
|
+
expect(element(by.binding('list')).getText()).toBe('list=[]');
|
18374
|
+
element(by.css('.doc-example-live #submit')).click();
|
18375
|
+
element(by.css('.doc-example-live #submit')).click();
|
18376
|
+
expect(element(by.binding('list')).getText()).toContain('hello');
|
18377
|
+
});
|
18378
|
+
</doc:protractor>
|
17940
18379
|
</doc:example>
|
17941
18380
|
*/
|
17942
18381
|
|
@@ -17948,6 +18387,7 @@ forEach(
|
|
17948
18387
|
* Specify custom behavior on focus event.
|
17949
18388
|
*
|
17950
18389
|
* @element window, input, select, textarea, a
|
18390
|
+
* @priority 0
|
17951
18391
|
* @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
|
17952
18392
|
* focus. (Event object is available as `$event`)
|
17953
18393
|
*
|
@@ -17963,6 +18403,7 @@ forEach(
|
|
17963
18403
|
* Specify custom behavior on blur event.
|
17964
18404
|
*
|
17965
18405
|
* @element window, input, select, textarea, a
|
18406
|
+
* @priority 0
|
17966
18407
|
* @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
|
17967
18408
|
* blur. (Event object is available as `$event`)
|
17968
18409
|
*
|
@@ -17978,11 +18419,17 @@ forEach(
|
|
17978
18419
|
* Specify custom behavior on copy event.
|
17979
18420
|
*
|
17980
18421
|
* @element window, input, select, textarea, a
|
18422
|
+
* @priority 0
|
17981
18423
|
* @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
|
17982
18424
|
* copy. (Event object is available as `$event`)
|
17983
18425
|
*
|
17984
18426
|
* @example
|
17985
|
-
|
18427
|
+
<doc:example>
|
18428
|
+
<doc:source>
|
18429
|
+
<input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
|
18430
|
+
copied: {{copied}}
|
18431
|
+
</doc:source>
|
18432
|
+
</doc:example>
|
17986
18433
|
*/
|
17987
18434
|
|
17988
18435
|
/**
|
@@ -17993,11 +18440,17 @@ forEach(
|
|
17993
18440
|
* Specify custom behavior on cut event.
|
17994
18441
|
*
|
17995
18442
|
* @element window, input, select, textarea, a
|
18443
|
+
* @priority 0
|
17996
18444
|
* @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
|
17997
18445
|
* cut. (Event object is available as `$event`)
|
17998
18446
|
*
|
17999
18447
|
* @example
|
18000
|
-
|
18448
|
+
<doc:example>
|
18449
|
+
<doc:source>
|
18450
|
+
<input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
|
18451
|
+
cut: {{cut}}
|
18452
|
+
</doc:source>
|
18453
|
+
</doc:example>
|
18001
18454
|
*/
|
18002
18455
|
|
18003
18456
|
/**
|
@@ -18008,11 +18461,17 @@ forEach(
|
|
18008
18461
|
* Specify custom behavior on paste event.
|
18009
18462
|
*
|
18010
18463
|
* @element window, input, select, textarea, a
|
18464
|
+
* @priority 0
|
18011
18465
|
* @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
|
18012
18466
|
* paste. (Event object is available as `$event`)
|
18013
18467
|
*
|
18014
18468
|
* @example
|
18015
|
-
|
18469
|
+
<doc:example>
|
18470
|
+
<doc:source>
|
18471
|
+
<input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
|
18472
|
+
pasted: {{paste}}
|
18473
|
+
</doc:source>
|
18474
|
+
</doc:example>
|
18016
18475
|
*/
|
18017
18476
|
|
18018
18477
|
/**
|
@@ -18074,9 +18533,6 @@ forEach(
|
|
18074
18533
|
padding:10px;
|
18075
18534
|
}
|
18076
18535
|
|
18077
|
-
/*
|
18078
|
-
The transition styles can also be placed on the CSS base class above
|
18079
|
-
*/
|
18080
18536
|
.animate-if.ng-enter, .animate-if.ng-leave {
|
18081
18537
|
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
18082
18538
|
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
@@ -18246,19 +18702,33 @@ var ngIfDirective = ['$animate', function($animate) {
|
|
18246
18702
|
top:50px;
|
18247
18703
|
}
|
18248
18704
|
</file>
|
18249
|
-
<file name="
|
18705
|
+
<file name="protractorTest.js">
|
18706
|
+
var templateSelect = element(by.model('template'));
|
18707
|
+
var includeElem = element(by.css('.doc-example-live [ng-include]'));
|
18708
|
+
|
18250
18709
|
it('should load template1.html', function() {
|
18251
|
-
|
18252
|
-
toMatch(/Content of template1.html/);
|
18710
|
+
expect(includeElem.getText()).toMatch(/Content of template1.html/);
|
18253
18711
|
});
|
18712
|
+
|
18254
18713
|
it('should load template2.html', function() {
|
18255
|
-
|
18256
|
-
|
18257
|
-
|
18714
|
+
if (browser.params.browser == 'firefox') {
|
18715
|
+
// Firefox can't handle using selects
|
18716
|
+
// See https://github.com/angular/protractor/issues/480
|
18717
|
+
return;
|
18718
|
+
}
|
18719
|
+
templateSelect.click();
|
18720
|
+
templateSelect.element.all(by.css('option')).get(2).click();
|
18721
|
+
expect(includeElem.getText()).toMatch(/Content of template2.html/);
|
18258
18722
|
});
|
18723
|
+
|
18259
18724
|
it('should change to blank', function() {
|
18260
|
-
|
18261
|
-
|
18725
|
+
if (browser.params.browser == 'firefox') {
|
18726
|
+
// Firefox can't handle using selects
|
18727
|
+
return;
|
18728
|
+
}
|
18729
|
+
templateSelect.click();
|
18730
|
+
templateSelect.element.all(by.css('option')).get(0).click();
|
18731
|
+
expect(includeElem.isPresent()).toBe(false);
|
18262
18732
|
});
|
18263
18733
|
</file>
|
18264
18734
|
</example>
|
@@ -18384,11 +18854,18 @@ var ngIncludeFillContentDirective = ['$compile',
|
|
18384
18854
|
* current scope.
|
18385
18855
|
*
|
18386
18856
|
* <div class="alert alert-error">
|
18387
|
-
* The only appropriate use of `ngInit` for aliasing special properties of
|
18857
|
+
* The only appropriate use of `ngInit` is for aliasing special properties of
|
18388
18858
|
* {@link api/ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
|
18389
18859
|
* should use {@link guide/controller controllers} rather than `ngInit`
|
18390
18860
|
* to initialize values on a scope.
|
18391
18861
|
* </div>
|
18862
|
+
* <div class="alert alert-warning">
|
18863
|
+
* **Note**: If you have assignment in `ngInit` along with {@link api/ng.$filter `$filter`}, make
|
18864
|
+
* sure you have parenthesis for correct precedence:
|
18865
|
+
* <pre class="prettyprint">
|
18866
|
+
* <div ng-init="test1 = (data | orderBy:'name')"></div>
|
18867
|
+
* </pre>
|
18868
|
+
* </div>
|
18392
18869
|
*
|
18393
18870
|
* @priority 450
|
18394
18871
|
*
|
@@ -18411,15 +18888,15 @@ var ngIncludeFillContentDirective = ['$compile',
|
|
18411
18888
|
</div>
|
18412
18889
|
</div>
|
18413
18890
|
</doc:source>
|
18414
|
-
<doc:
|
18891
|
+
<doc:protractor>
|
18415
18892
|
it('should alias index positions', function() {
|
18416
|
-
|
18417
|
-
|
18418
|
-
|
18419
|
-
|
18420
|
-
|
18893
|
+
var elements = element.all(by.css('.example-init'));
|
18894
|
+
expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
|
18895
|
+
expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
|
18896
|
+
expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
|
18897
|
+
expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
|
18421
18898
|
});
|
18422
|
-
</doc:
|
18899
|
+
</doc:protractor>
|
18423
18900
|
</doc:example>
|
18424
18901
|
*/
|
18425
18902
|
var ngInitDirective = ngDirective({
|
@@ -18457,13 +18934,12 @@ var ngInitDirective = ngDirective({
|
|
18457
18934
|
<div>Normal: {{1 + 2}}</div>
|
18458
18935
|
<div ng-non-bindable>Ignored: {{1 + 2}}</div>
|
18459
18936
|
</doc:source>
|
18460
|
-
<doc:
|
18937
|
+
<doc:protractor>
|
18461
18938
|
it('should check ng-non-bindable', function() {
|
18462
|
-
expect(
|
18463
|
-
expect(
|
18464
|
-
toMatch(/1 \+ 2/);
|
18939
|
+
expect(element(by.binding('1 + 2')).getText()).toContain('3');
|
18940
|
+
expect(element.all(by.css('.doc-example-live div')).last().getText()).toMatch(/1 \+ 2/);
|
18465
18941
|
});
|
18466
|
-
</doc:
|
18942
|
+
</doc:protractor>
|
18467
18943
|
</doc:example>
|
18468
18944
|
*/
|
18469
18945
|
var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
|
@@ -18591,49 +19067,53 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
|
|
18591
19067
|
</ng-pluralize>
|
18592
19068
|
</div>
|
18593
19069
|
</doc:source>
|
18594
|
-
<doc:
|
19070
|
+
<doc:protractor>
|
18595
19071
|
it('should show correct pluralized string', function() {
|
18596
|
-
|
18597
|
-
|
18598
|
-
|
18599
|
-
|
18600
|
-
|
18601
|
-
|
18602
|
-
|
18603
|
-
|
18604
|
-
|
18605
|
-
|
18606
|
-
|
18607
|
-
|
18608
|
-
|
18609
|
-
|
18610
|
-
|
18611
|
-
toBe('Igor and Misko are viewing.');
|
18612
|
-
|
18613
|
-
using('.doc-example-live').input('personCount').enter('3');
|
18614
|
-
expect(element('.doc-example-live ng-pluralize:first').text()).
|
18615
|
-
toBe('3 people are viewing.');
|
18616
|
-
expect(element('.doc-example-live ng-pluralize:last').text()).
|
18617
|
-
toBe('Igor, Misko and one other person are viewing.');
|
18618
|
-
|
18619
|
-
using('.doc-example-live').input('personCount').enter('4');
|
18620
|
-
expect(element('.doc-example-live ng-pluralize:first').text()).
|
18621
|
-
toBe('4 people are viewing.');
|
18622
|
-
expect(element('.doc-example-live ng-pluralize:last').text()).
|
18623
|
-
toBe('Igor, Misko and 2 other people are viewing.');
|
18624
|
-
});
|
19072
|
+
var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
|
19073
|
+
var withOffset = element.all(by.css('ng-pluralize')).get(1);
|
19074
|
+
var countInput = element(by.model('personCount'));
|
19075
|
+
|
19076
|
+
expect(withoutOffset.getText()).toEqual('1 person is viewing.');
|
19077
|
+
expect(withOffset.getText()).toEqual('Igor is viewing.');
|
19078
|
+
|
19079
|
+
countInput.clear();
|
19080
|
+
countInput.sendKeys('0');
|
19081
|
+
|
19082
|
+
expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
|
19083
|
+
expect(withOffset.getText()).toEqual('Nobody is viewing.');
|
19084
|
+
|
19085
|
+
countInput.clear();
|
19086
|
+
countInput.sendKeys('2');
|
18625
19087
|
|
18626
|
-
|
18627
|
-
|
18628
|
-
expect(element('.doc-example-live ng-pluralize:last').text()).
|
18629
|
-
toBe('Igor, Misko and 2 other people are viewing.');
|
19088
|
+
expect(withoutOffset.getText()).toEqual('2 people are viewing.');
|
19089
|
+
expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
|
18630
19090
|
|
18631
|
-
|
18632
|
-
|
18633
|
-
|
18634
|
-
|
19091
|
+
countInput.clear();
|
19092
|
+
countInput.sendKeys('3');
|
19093
|
+
|
19094
|
+
expect(withoutOffset.getText()).toEqual('3 people are viewing.');
|
19095
|
+
expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
|
19096
|
+
|
19097
|
+
countInput.clear();
|
19098
|
+
countInput.sendKeys('4');
|
19099
|
+
|
19100
|
+
expect(withoutOffset.getText()).toEqual('4 people are viewing.');
|
19101
|
+
expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
|
18635
19102
|
});
|
18636
|
-
|
19103
|
+
it('should show data-bound names', function() {
|
19104
|
+
var withOffset = element.all(by.css('ng-pluralize')).get(1);
|
19105
|
+
var personCount = element(by.model('personCount'));
|
19106
|
+
var person1 = element(by.model('person1'));
|
19107
|
+
var person2 = element(by.model('person2'));
|
19108
|
+
personCount.clear();
|
19109
|
+
personCount.sendKeys('4');
|
19110
|
+
person1.clear();
|
19111
|
+
person1.sendKeys('Di');
|
19112
|
+
person2.clear();
|
19113
|
+
person2.sendKeys('Vojta');
|
19114
|
+
expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
|
19115
|
+
});
|
19116
|
+
</doc:protractor>
|
18637
19117
|
</doc:example>
|
18638
19118
|
*/
|
18639
19119
|
var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
|
@@ -18700,6 +19180,8 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
|
|
18700
19180
|
* | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
|
18701
19181
|
* | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
|
18702
19182
|
*
|
19183
|
+
* Creating aliases for these properties is possible with {@link api/ng.directive:ngInit `ngInit`}.
|
19184
|
+
* This may be useful when, for instance, nesting ngRepeats.
|
18703
19185
|
*
|
18704
19186
|
* # Special repeat start and end points
|
18705
19187
|
* To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
|
@@ -18850,25 +19332,27 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
|
|
18850
19332
|
max-height:40px;
|
18851
19333
|
}
|
18852
19334
|
</file>
|
18853
|
-
<file name="
|
18854
|
-
|
18855
|
-
|
18856
|
-
|
18857
|
-
|
18858
|
-
|
18859
|
-
|
18860
|
-
|
18861
|
-
|
19335
|
+
<file name="protractorTest.js">
|
19336
|
+
var friends = element(by.css('.doc-example-live'))
|
19337
|
+
.element.all(by.repeater('friend in friends'));
|
19338
|
+
|
19339
|
+
it('should render initial data set', function() {
|
19340
|
+
expect(friends.count()).toBe(10);
|
19341
|
+
expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
|
19342
|
+
expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
|
19343
|
+
expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
|
19344
|
+
expect(element(by.binding('friends.length')).getText())
|
19345
|
+
.toMatch("I have 10 friends. They are:");
|
19346
|
+
});
|
18862
19347
|
|
18863
19348
|
it('should update repeater when filter predicate changes', function() {
|
18864
|
-
|
18865
|
-
expect(r.count()).toBe(10);
|
19349
|
+
expect(friends.count()).toBe(10);
|
18866
19350
|
|
18867
|
-
|
19351
|
+
element(by.css('.doc-example-live')).element(by.model('q')).sendKeys('ma');
|
18868
19352
|
|
18869
|
-
expect(
|
18870
|
-
expect(
|
18871
|
-
expect(
|
19353
|
+
expect(friends.count()).toBe(2);
|
19354
|
+
expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
|
19355
|
+
expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
|
18872
19356
|
});
|
18873
19357
|
</file>
|
18874
19358
|
</example>
|
@@ -18883,7 +19367,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
|
18883
19367
|
$$tlb: true,
|
18884
19368
|
link: function($scope, $element, $attr, ctrl, $transclude){
|
18885
19369
|
var expression = $attr.ngRepeat;
|
18886
|
-
var match = expression.match(/^\s*(
|
19370
|
+
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
|
18887
19371
|
trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
|
18888
19372
|
lhs, rhs, valueIdentifier, keyIdentifier,
|
18889
19373
|
hashFnLocals = {$id: hashKey};
|
@@ -18895,7 +19379,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
|
18895
19379
|
|
18896
19380
|
lhs = match[1];
|
18897
19381
|
rhs = match[2];
|
18898
|
-
trackByExp = match[
|
19382
|
+
trackByExp = match[3];
|
18899
19383
|
|
18900
19384
|
if (trackByExp) {
|
18901
19385
|
trackByExpGetter = $parse(trackByExp);
|
@@ -19122,6 +19606,11 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
|
19122
19606
|
*
|
19123
19607
|
* Just remember to include the important flag so the CSS override will function.
|
19124
19608
|
*
|
19609
|
+
* <div class="alert alert-warning">
|
19610
|
+
* **Note:** Here is a list of values that ngShow will consider as a falsy value (case insensitive):<br />
|
19611
|
+
* "f" / "0" / "false" / "no" / "n" / "[]"
|
19612
|
+
* </div>
|
19613
|
+
*
|
19125
19614
|
* ## A note about animations with ngShow
|
19126
19615
|
*
|
19127
19616
|
* Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
|
@@ -19197,16 +19686,19 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
|
19197
19686
|
background:white;
|
19198
19687
|
}
|
19199
19688
|
</file>
|
19200
|
-
<file name="
|
19201
|
-
|
19202
|
-
|
19203
|
-
expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
|
19689
|
+
<file name="protractorTest.js">
|
19690
|
+
var thumbsUp = element(by.css('.doc-example-live span.icon-thumbs-up'));
|
19691
|
+
var thumbsDown = element(by.css('.doc-example-live span.icon-thumbs-down'));
|
19204
19692
|
|
19205
|
-
|
19693
|
+
it('should check ng-show / ng-hide', function() {
|
19694
|
+
expect(thumbsUp.isDisplayed()).toBeFalsy();
|
19695
|
+
expect(thumbsDown.isDisplayed()).toBeTruthy();
|
19206
19696
|
|
19207
|
-
|
19208
|
-
|
19209
|
-
|
19697
|
+
element(by.model('checked')).click();
|
19698
|
+
|
19699
|
+
expect(thumbsUp.isDisplayed()).toBeTruthy();
|
19700
|
+
expect(thumbsDown.isDisplayed()).toBeFalsy();
|
19701
|
+
});
|
19210
19702
|
</file>
|
19211
19703
|
</example>
|
19212
19704
|
*/
|
@@ -19270,6 +19762,11 @@ var ngShowDirective = ['$animate', function($animate) {
|
|
19270
19762
|
* </pre>
|
19271
19763
|
*
|
19272
19764
|
* Just remember to include the important flag so the CSS override will function.
|
19765
|
+
*
|
19766
|
+
* <div class="alert alert-warning">
|
19767
|
+
* **Note:** Here is a list of values that ngHide will consider as a falsy value (case insensitive):<br />
|
19768
|
+
* "f" / "0" / "false" / "no" / "n" / "[]"
|
19769
|
+
* </div>
|
19273
19770
|
*
|
19274
19771
|
* ## A note about animations with ngHide
|
19275
19772
|
*
|
@@ -19346,16 +19843,19 @@ var ngShowDirective = ['$animate', function($animate) {
|
|
19346
19843
|
background:white;
|
19347
19844
|
}
|
19348
19845
|
</file>
|
19349
|
-
<file name="
|
19350
|
-
|
19351
|
-
|
19352
|
-
expect(element('.doc-example-live .check-element:last:visible').count()).toEqual(1);
|
19846
|
+
<file name="protractorTest.js">
|
19847
|
+
var thumbsUp = element(by.css('.doc-example-live span.icon-thumbs-up'));
|
19848
|
+
var thumbsDown = element(by.css('.doc-example-live span.icon-thumbs-down'));
|
19353
19849
|
|
19354
|
-
|
19850
|
+
it('should check ng-show / ng-hide', function() {
|
19851
|
+
expect(thumbsUp.isDisplayed()).toBeFalsy();
|
19852
|
+
expect(thumbsDown.isDisplayed()).toBeTruthy();
|
19355
19853
|
|
19356
|
-
|
19357
|
-
|
19358
|
-
|
19854
|
+
element(by.model('checked')).click();
|
19855
|
+
|
19856
|
+
expect(thumbsUp.isDisplayed()).toBeTruthy();
|
19857
|
+
expect(thumbsDown.isDisplayed()).toBeFalsy();
|
19858
|
+
});
|
19359
19859
|
</file>
|
19360
19860
|
</example>
|
19361
19861
|
*/
|
@@ -19394,13 +19894,15 @@ var ngHideDirective = ['$animate', function($animate) {
|
|
19394
19894
|
color: black;
|
19395
19895
|
}
|
19396
19896
|
</file>
|
19397
|
-
<file name="
|
19897
|
+
<file name="protractorTest.js">
|
19898
|
+
var colorSpan = element(by.css('.doc-example-live span'));
|
19899
|
+
|
19398
19900
|
it('should check ng-style', function() {
|
19399
|
-
expect(
|
19400
|
-
element('.doc-example-live
|
19401
|
-
expect(
|
19402
|
-
element('.doc-example-live
|
19403
|
-
expect(
|
19901
|
+
expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
|
19902
|
+
element(by.css('.doc-example-live input[value=set]')).click();
|
19903
|
+
expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
|
19904
|
+
element(by.css('.doc-example-live input[value=clear]')).click();
|
19905
|
+
expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
|
19404
19906
|
});
|
19405
19907
|
</file>
|
19406
19908
|
</example>
|
@@ -19425,7 +19927,7 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
|
|
19425
19927
|
* as specified in the template.
|
19426
19928
|
*
|
19427
19929
|
* The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
|
19428
|
-
* from the template cache), `ngSwitch` simply
|
19930
|
+
* from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
|
19429
19931
|
* matches the value obtained from the evaluated expression. In other words, you define a container element
|
19430
19932
|
* (where you place the directive), place an expression on the **`on="..."` attribute**
|
19431
19933
|
* (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
|
@@ -19521,17 +20023,20 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
|
|
19521
20023
|
top:0;
|
19522
20024
|
}
|
19523
20025
|
</file>
|
19524
|
-
<file name="
|
20026
|
+
<file name="protractorTest.js">
|
20027
|
+
var switchElem = element(by.css('.doc-example-live [ng-switch]'));
|
20028
|
+
var select = element(by.model('selection'));
|
20029
|
+
|
19525
20030
|
it('should start in settings', function() {
|
19526
|
-
expect(
|
20031
|
+
expect(switchElem.getText()).toMatch(/Settings Div/);
|
19527
20032
|
});
|
19528
20033
|
it('should change to home', function() {
|
19529
|
-
select('
|
19530
|
-
expect(
|
20034
|
+
select.element.all(by.css('option')).get(1).click();
|
20035
|
+
expect(switchElem.getText()).toMatch(/Home Span/);
|
19531
20036
|
});
|
19532
20037
|
it('should select default', function() {
|
19533
|
-
select('
|
19534
|
-
expect(
|
20038
|
+
select.element.all(by.css('option')).get(2).click();
|
20039
|
+
expect(switchElem.getText()).toMatch(/default/);
|
19535
20040
|
});
|
19536
20041
|
</file>
|
19537
20042
|
</example>
|
@@ -19582,11 +20087,9 @@ var ngSwitchWhenDirective = ngDirective({
|
|
19582
20087
|
transclude: 'element',
|
19583
20088
|
priority: 800,
|
19584
20089
|
require: '^ngSwitch',
|
19585
|
-
|
19586
|
-
|
19587
|
-
|
19588
|
-
ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
|
19589
|
-
};
|
20090
|
+
link: function(scope, element, attrs, ctrl, $transclude) {
|
20091
|
+
ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
|
20092
|
+
ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
|
19590
20093
|
}
|
19591
20094
|
});
|
19592
20095
|
|
@@ -19640,35 +20143,32 @@ var ngSwitchDefaultDirective = ngDirective({
|
|
19640
20143
|
<pane title="{{title}}">{{text}}</pane>
|
19641
20144
|
</div>
|
19642
20145
|
</doc:source>
|
19643
|
-
<doc:
|
20146
|
+
<doc:protractor>
|
19644
20147
|
it('should have transcluded', function() {
|
19645
|
-
|
19646
|
-
|
19647
|
-
|
19648
|
-
|
20148
|
+
var titleElement = element(by.model('title'));
|
20149
|
+
titleElement.clear();
|
20150
|
+
titleElement.sendKeys('TITLE');
|
20151
|
+
var textElement = element(by.model('text'));
|
20152
|
+
textElement.clear();
|
20153
|
+
textElement.sendKeys('TEXT');
|
20154
|
+
expect(element(by.binding('title')).getText()).toEqual('TITLE');
|
20155
|
+
expect(element(by.binding('text')).getText()).toEqual('TEXT');
|
19649
20156
|
});
|
19650
|
-
</doc:
|
20157
|
+
</doc:protractor>
|
19651
20158
|
</doc:example>
|
19652
20159
|
*
|
19653
20160
|
*/
|
19654
20161
|
var ngTranscludeDirective = ngDirective({
|
19655
|
-
|
20162
|
+
link: function($scope, $element, $attrs, controller, $transclude) {
|
19656
20163
|
if (!$transclude) {
|
19657
20164
|
throw minErr('ngTransclude')('orphan',
|
19658
|
-
|
19659
|
-
|
19660
|
-
|
19661
|
-
|
20165
|
+
'Illegal use of ngTransclude directive in the template! ' +
|
20166
|
+
'No parent directive that requires a transclusion found. ' +
|
20167
|
+
'Element: {0}',
|
20168
|
+
startingTag($element));
|
19662
20169
|
}
|
19663
|
-
|
19664
|
-
|
19665
|
-
// the parent element even when the transclusion replaces the current element. (we can't use priority here because
|
19666
|
-
// that applies only to compile fns and not controllers
|
19667
|
-
this.$transclude = $transclude;
|
19668
|
-
}],
|
19669
|
-
|
19670
|
-
link: function($scope, $element, $attrs, controller) {
|
19671
|
-
controller.$transclude(function(clone) {
|
20170
|
+
|
20171
|
+
$transclude(function(clone) {
|
19672
20172
|
$element.empty();
|
19673
20173
|
$element.append(clone);
|
19674
20174
|
});
|
@@ -19681,10 +20181,14 @@ var ngTranscludeDirective = ngDirective({
|
|
19681
20181
|
* @restrict E
|
19682
20182
|
*
|
19683
20183
|
* @description
|
19684
|
-
* Load content of a script
|
19685
|
-
* template can be used by
|
20184
|
+
* Load the content of a `<script>` element into {@link api/ng.$templateCache `$templateCache`}, so that the
|
20185
|
+
* template can be used by {@link api/ng.directive:ngInclude `ngInclude`},
|
20186
|
+
* {@link api/ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
|
20187
|
+
* `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
|
20188
|
+
* assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
|
19686
20189
|
*
|
19687
|
-
* @param {'text/ng-template'} type
|
20190
|
+
* @param {'text/ng-template'} type Must be set to `'text/ng-template'`.
|
20191
|
+
* @param {string} id Cache name of the template.
|
19688
20192
|
*
|
19689
20193
|
* @example
|
19690
20194
|
<doc:example>
|
@@ -19696,12 +20200,12 @@ var ngTranscludeDirective = ngDirective({
|
|
19696
20200
|
<a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
|
19697
20201
|
<div id="tpl-content" ng-include src="currentTpl"></div>
|
19698
20202
|
</doc:source>
|
19699
|
-
<doc:
|
20203
|
+
<doc:protractor>
|
19700
20204
|
it('should load template defined inside script tag', function() {
|
19701
|
-
element('#tpl-link').click();
|
19702
|
-
expect(element('#tpl-content').
|
20205
|
+
element(by.css('#tpl-link')).click();
|
20206
|
+
expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
|
19703
20207
|
});
|
19704
|
-
</doc:
|
20208
|
+
</doc:protractor>
|
19705
20209
|
</doc:example>
|
19706
20210
|
*/
|
19707
20211
|
var scriptDirective = ['$templateCache', function($templateCache) {
|
@@ -19739,14 +20243,21 @@ var ngOptionsMinErr = minErr('ngOptions');
|
|
19739
20243
|
* represented by the selected option will be bound to the model identified by the `ngModel`
|
19740
20244
|
* directive.
|
19741
20245
|
*
|
20246
|
+
* <div class="alert alert-warning">
|
20247
|
+
* **Note:** `ngModel` compares by reference, not value. This is important when binding to an
|
20248
|
+
* array of objects. See an example {@link http://jsfiddle.net/qWzTb/ in this jsfiddle}.
|
20249
|
+
* </div>
|
20250
|
+
*
|
19742
20251
|
* Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
|
19743
20252
|
* be nested into the `<select>` element. This element will then represent the `null` or "not selected"
|
19744
20253
|
* option. See example below for demonstration.
|
19745
20254
|
*
|
19746
|
-
*
|
20255
|
+
* <div class="alert alert-warning">
|
20256
|
+
* **Note:** `ngOptions` provides an iterator facility for the `<option>` element which should be used instead
|
19747
20257
|
* of {@link ng.directive:ngRepeat ngRepeat} when you want the
|
19748
20258
|
* `select` model to be bound to a non-string value. This is because an option element can only
|
19749
20259
|
* be bound to string values at present.
|
20260
|
+
* </div>
|
19750
20261
|
*
|
19751
20262
|
* @param {string} ngModel Assignable angular expression to data-bind to.
|
19752
20263
|
* @param {string=} name Property name of the form under which the control is published.
|
@@ -19833,23 +20344,25 @@ var ngOptionsMinErr = minErr('ngOptions');
|
|
19833
20344
|
</div>
|
19834
20345
|
</div>
|
19835
20346
|
</doc:source>
|
19836
|
-
<doc:
|
20347
|
+
<doc:protractor>
|
19837
20348
|
it('should check ng-options', function() {
|
19838
|
-
expect(binding('{selected_color:color}')).toMatch('red');
|
19839
|
-
select('color').
|
19840
|
-
|
19841
|
-
|
19842
|
-
|
20349
|
+
expect(element(by.binding('{selected_color:color}')).getText()).toMatch('red');
|
20350
|
+
element.all(by.select('color')).first().click();
|
20351
|
+
element.all(by.css('select[ng-model="color"] option')).first().click();
|
20352
|
+
expect(element(by.binding('{selected_color:color}')).getText()).toMatch('black');
|
20353
|
+
element(by.css('.nullable select[ng-model="color"]')).click();
|
20354
|
+
element.all(by.css('.nullable select[ng-model="color"] option')).first().click();
|
20355
|
+
expect(element(by.binding('{selected_color:color}')).getText()).toMatch('null');
|
19843
20356
|
});
|
19844
|
-
</doc:
|
20357
|
+
</doc:protractor>
|
19845
20358
|
</doc:example>
|
19846
20359
|
*/
|
19847
20360
|
|
19848
20361
|
var ngOptionsDirective = valueFn({ terminal: true });
|
19849
20362
|
// jshint maxlen: false
|
19850
20363
|
var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
19851
|
-
//
|
19852
|
-
var NG_OPTIONS_REGEXP = /^\s*(
|
20364
|
+
//000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888
|
20365
|
+
var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,
|
19853
20366
|
nullModelCtrl = {$setViewValue: noop};
|
19854
20367
|
// jshint maxlen: 100
|
19855
20368
|
|
@@ -19941,18 +20454,10 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
19941
20454
|
selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
|
19942
20455
|
|
19943
20456
|
// required validator
|
19944
|
-
if (multiple
|
19945
|
-
|
19946
|
-
|
19947
|
-
return value;
|
20457
|
+
if (multiple) {
|
20458
|
+
ngModelCtrl.$isEmpty = function(value) {
|
20459
|
+
return !value || value.length === 0;
|
19948
20460
|
};
|
19949
|
-
|
19950
|
-
ngModelCtrl.$parsers.push(requiredValidator);
|
19951
|
-
ngModelCtrl.$formatters.unshift(requiredValidator);
|
19952
|
-
|
19953
|
-
attr.$observe('required', function() {
|
19954
|
-
requiredValidator(ngModelCtrl.$viewValue);
|
19955
|
-
});
|
19956
20461
|
}
|
19957
20462
|
|
19958
20463
|
if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);
|
@@ -20158,7 +20663,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
20158
20663
|
|
20159
20664
|
// We now build up the list of options we need (we merge later)
|
20160
20665
|
for (index = 0; length = keys.length, index < length; index++) {
|
20161
|
-
|
20666
|
+
|
20162
20667
|
key = index;
|
20163
20668
|
if (keyName) {
|
20164
20669
|
key = keys[index];
|
@@ -20366,4 +20871,4 @@ var styleDirective = valueFn({
|
|
20366
20871
|
|
20367
20872
|
})(window, document);
|
20368
20873
|
|
20369
|
-
!angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}.ng-animate-
|
20874
|
+
!angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}.ng-animate-block-transitions{transition:0s all!important;-webkit-transition:0s all!important;}</style>');
|