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