Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6d04b3a3d | |||
| e31560cf6b | |||
| 3d38fff8b4 | |||
| bc492c0fc1 | |||
| 162144202c | |||
| 05596527ed | |||
| 5fea3471e8 | |||
| 131e4014b8 | |||
| 01c5be4681 | |||
| fd9a03e147 | |||
| 6c17d02bc4 | |||
| 6a6f71f238 | |||
| cf686285c2 | |||
| 7dfedb732d | |||
| 760f2fb731 | |||
| c9705b7556 | |||
| 53ec33f07f | |||
| 884ef0dbcd | |||
| 010413f90a | |||
| 4f57236614 | |||
| 50bf029625 | |||
| eff52ad877 | |||
| e9ee492d35 | |||
| e415e916e8 | |||
| 07084e1c8b | |||
| 186a591228 | |||
| a80049fd0a | |||
| d158dd131e | |||
| 1147f21999 | |||
| bddd46c8ec | |||
| 80e7a45584 | |||
| 498365f219 | |||
| 056c849352 | |||
| 7d6e5a2d01 | |||
| d1c4766d14 | |||
| 98473835a2 | |||
| 870232bd05 | |||
| c31df32ca0 | |||
| df2b88e230 | |||
| 83451d552e | |||
| af7203e0b8 | |||
| 98ee3719f9 | |||
| 94b5f2dadb | |||
| fe7decd1b0 | |||
| cef084ade9 | |||
| 937caab647 | |||
| 3fc8017119 | |||
| 54637a335f | |||
| 277a5ea05d | |||
| 9865a7c0ad | |||
| efba4731e4 | |||
| a965984733 | |||
| 14d3e559d4 | |||
| 3d156a76e3 | |||
| c7a1d1ab0b | |||
| 26d43cacdc | |||
| 4f5758e666 | |||
| 274a6734ef | |||
| f0e3dfd008 | |||
| bc3ff2cecd | |||
| 8f329ffb82 | |||
| 864b2596b2 | |||
| f3a796e522 | |||
| 09f8962df2 | |||
| a13c4ba770 | |||
| 040e743b39 | |||
| bf816d3ade | |||
| 74b4ab8867 | |||
| 6e2359caa0 | |||
| 41534816a4 | |||
| 30252a0504 | |||
| 3dc18037e8 | |||
| 57d50582aa | |||
| 73c66715c9 | |||
| cb29632a58 | |||
| 5c97731a22 | |||
| b2e472e7a2 | |||
| 6ac773f350 | |||
| 3174f73336 | |||
| 3c62e4244e | |||
| c432999572 | |||
| f8d319c11a | |||
| 4f72433392 | |||
| 2f91cfd0d2 | |||
| d5c5e2b584 | |||
| cbb3ce2c30 | |||
| 45af02de04 | |||
| 6144df52af | |||
| b0474cb984 | |||
| 9a4c9e6487 | |||
| 11fff8fa0d | |||
| da8ab2f928 | |||
| 6d01384a55 | |||
| 109ffac975 | |||
| 8c10db3847 | |||
| 03088d6010 | |||
| 18e0768a2b | |||
| ed4a1fddce | |||
| cfde6f507c |
+129
@@ -1,3 +1,129 @@
|
||||
<a name="1.2.7"></a>
|
||||
# 1.2.7 emoji-clairvoyance (2014-01-03)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- ensue class-based animations are always skipped before structural post-digest tasks are run
|
||||
([bc492c0f](https://github.com/angular/angular.js/commit/bc492c0fc17257ddf2bc5964e205379aa766b3d8),
|
||||
[#5582](https://github.com/angular/angular.js/issues/5582))
|
||||
- remove trailing `s` from computed transition duration styles
|
||||
([50bf0296](https://github.com/angular/angular.js/commit/50bf029625d603fc652f0f413e709f43803743db))
|
||||
- **$http:**
|
||||
([3d38fff8](https://github.com/angular/angular.js/commit/3d38fff8b4ea2fd60fadef2028ea4dcddfccb1a4))
|
||||
- use ActiveX XHR when making PATCH requests on IE8
|
||||
([6c17d02b](https://github.com/angular/angular.js/commit/6c17d02bc4cc02f478775d62e1f9f77da9da82ad),
|
||||
[#2518](https://github.com/angular/angular.js/issues/2518), [#5043](https://github.com/angular/angular.js/issues/5043))
|
||||
- fix 'type mismatch' error on IE8 after each request
|
||||
([fd9a03e1](https://github.com/angular/angular.js/commit/fd9a03e147aac7e952c6dda1f381fd4662276ba2))
|
||||
- Ignore multiple calls to onreadystatechange with readyState=4
|
||||
([4f572366](https://github.com/angular/angular.js/commit/4f57236614415eea919221ea5f99c4d8689b3267),
|
||||
[#5426](https://github.com/angular/angular.js/issues/5426))
|
||||
- **$injector:** remove the `INSTANTIATING` flag properly when done
|
||||
([186a5912](https://github.com/angular/angular.js/commit/186a5912288acfff0ee59dae29af83c37c987921),
|
||||
[#4361](https://github.com/angular/angular.js/issues/4361), [#5577](https://github.com/angular/angular.js/issues/5577))
|
||||
- **$location:**
|
||||
- remove base href domain if the URL begins with '//'
|
||||
([760f2fb7](https://github.com/angular/angular.js/commit/760f2fb73178e56c37397b3c5876f7dac96f0455),
|
||||
[#5606](https://github.com/angular/angular.js/issues/5606))
|
||||
- fix $location.path() behaviour when $locationChangeStart is triggered by the browser
|
||||
([cf686285](https://github.com/angular/angular.js/commit/cf686285c22d528440e173fdb65ad1052d96df3c),
|
||||
[#4989](https://github.com/angular/angular.js/issues/4989), [#5089](https://github.com/angular/angular.js/issues/5089), [#5118](https://github.com/angular/angular.js/issues/5118), [#5580](https://github.com/angular/angular.js/issues/5580))
|
||||
- re-assign history after BFCache back on Android browser
|
||||
([bddd46c8](https://github.com/angular/angular.js/commit/bddd46c8ecf49cfe6c999cd6b4a69b7d7e1f9a33),
|
||||
[#5425](https://github.com/angular/angular.js/issues/5425))
|
||||
- **$resource:** prevent URL template from collapsing into an empty string
|
||||
([131e4014](https://github.com/angular/angular.js/commit/131e4014b831ac81b7979c4523da81ebc5861c70),
|
||||
[#5455](https://github.com/angular/angular.js/issues/5455), [#5493](https://github.com/angular/angular.js/issues/5493))
|
||||
- **$sanitize:** consider the `size` attribute as a valid/allowed attribute
|
||||
([056c8493](https://github.com/angular/angular.js/commit/056c8493521988dbb330c6636135b505737da918),
|
||||
[#5522](https://github.com/angular/angular.js/issues/5522))
|
||||
- **Scope:** don't let watch deregistration mess up the dirty-checking digest loop
|
||||
([884ef0db](https://github.com/angular/angular.js/commit/884ef0dbcdfe614cedc824d079361b53e675d033),
|
||||
[#5525](https://github.com/angular/angular.js/issues/5525))
|
||||
- **input:**
|
||||
- use apply on the change event only when one isn't already in progress
|
||||
([a80049fd](https://github.com/angular/angular.js/commit/a80049fd0ac858eeeb645a4209cb2a661d0b4c33),
|
||||
[#5293](https://github.com/angular/angular.js/issues/5293))
|
||||
- prevent double $digest when using jQuery trigger.
|
||||
([1147f219](https://github.com/angular/angular.js/commit/1147f21999edf9a434cd8d24865a6455e744d858),
|
||||
[#5293](https://github.com/angular/angular.js/issues/5293))
|
||||
- **ngRepeat:** allow for more flexible coding style in ngRepeat expression
|
||||
([c9705b75](https://github.com/angular/angular.js/commit/c9705b755645a4bfe066243f2ba15a733c3787e1),
|
||||
[#5537](https://github.com/angular/angular.js/issues/5537), [#5598](https://github.com/angular/angular.js/issues/5598))
|
||||
- **ngRoute:** instantiate controller when template is empty
|
||||
([498365f2](https://github.com/angular/angular.js/commit/498365f219f65d6c29bdf2f03610a4d3646009bb),
|
||||
[#5550](https://github.com/angular/angular.js/issues/5550))
|
||||
- **ngShow/ngHide, ngIf:** functions with zero args should be truthy
|
||||
([01c5be46](https://github.com/angular/angular.js/commit/01c5be4681e34cdc5f5c461b7a618fefe8038919),
|
||||
[#5414](https://github.com/angular/angular.js/issues/5414))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **Scope:** limit propagation of $broadcast to scopes that have listeners for the event
|
||||
([80e7a455](https://github.com/angular/angular.js/commit/80e7a4558490f7ffd33d142844b9153a5ed00e86),
|
||||
[#5341](https://github.com/angular/angular.js/issues/5341), [#5371](https://github.com/angular/angular.js/issues/5371))
|
||||
|
||||
<a name="1.2.6"></a>
|
||||
# 1.2.6 taco-salsafication (2013-12-19)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:** use a scheduled timeout in favor of a fallback property to close transitions
|
||||
([54637a33](https://github.com/angular/angular.js/commit/54637a335f885110efaa702a3bab29c77644b36c),
|
||||
[#5255](https://github.com/angular/angular.js/issues/5255), [#5241](https://github.com/angular/angular.js/issues/5241), [#5405](https://github.com/angular/angular.js/issues/5405))
|
||||
- **$compile:** remove invalid IE exceptional case for `href`
|
||||
([c7a1d1ab](https://github.com/angular/angular.js/commit/c7a1d1ab0b663edffc1ac7b54deea847e372468d),
|
||||
[#5479](https://github.com/angular/angular.js/issues/5479))
|
||||
- **$location:** parse xlink:href for SVGAElements
|
||||
([bc3ff2ce](https://github.com/angular/angular.js/commit/bc3ff2cecd0861766a9e8606f3cc2c582d9875df),
|
||||
[#5472](https://github.com/angular/angular.js/issues/5472), [#5198](https://github.com/angular/angular.js/issues/5198), [#5199](https://github.com/angular/angular.js/issues/5199), [#4098](https://github.com/angular/angular.js/issues/4098), [#1420](https://github.com/angular/angular.js/issues/1420))
|
||||
- **$log:** should work in IE8
|
||||
([4f5758e6](https://github.com/angular/angular.js/commit/4f5758e6669222369889c9e789601d25ff885530),
|
||||
[#5400](https://github.com/angular/angular.js/issues/5400))
|
||||
- **$parse:** return `undefined` if an intermetiate property's value is `null`
|
||||
([26d43cac](https://github.com/angular/angular.js/commit/26d43cacdc106765bd928d41600352198f887aef),
|
||||
[#5480](https://github.com/angular/angular.js/issues/5480))
|
||||
- **closure:** add type definition for `Scope#$watchCollection`
|
||||
([8f329ffb](https://github.com/angular/angular.js/commit/8f329ffb829410e1fd8f86a766929134e736e3e5),
|
||||
[#5475](https://github.com/angular/angular.js/issues/5475))
|
||||
- **forEach:** allow looping over result of `querySelectorAll` in IE8
|
||||
([274a6734](https://github.com/angular/angular.js/commit/274a6734ef1fff543cc50388a0958d1988baeb57))
|
||||
- **input:** do not hold input for composition on Android
|
||||
([3dc18037](https://github.com/angular/angular.js/commit/3dc18037e8db8766641a4d39f0fee96077db1fcb),
|
||||
[#5308](https://github.com/angular/angular.js/issues/5308))
|
||||
- **jqLite:** support unbind self within handler
|
||||
([2f91cfd0](https://github.com/angular/angular.js/commit/2f91cfd0d2986899c38641100c1851b2f9d3888a))
|
||||
- **ngRepeat:** allow multiline expressions
|
||||
([cbb3ce2c](https://github.com/angular/angular.js/commit/cbb3ce2c309052b951d0cc87e4c6daa9c48a3dd8),
|
||||
[#5000](https://github.com/angular/angular.js/issues/5000))
|
||||
- **select:** invalidate when `multiple`, `required`, and model is `[]`
|
||||
([5c97731a](https://github.com/angular/angular.js/commit/5c97731a22ed87d64712e673efea0e8a05eae65f),
|
||||
[#5337](https://github.com/angular/angular.js/issues/5337))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **jqLite:** provide support for `element.one()`
|
||||
([937caab6](https://github.com/angular/angular.js/commit/937caab6475e53a7ea0206e992f8a52449232e78))
|
||||
- **ngAnimate:** provide configuration support to match specific className values to trigger animations
|
||||
([cef084ad](https://github.com/angular/angular.js/commit/cef084ade9072090259d8c679751cac3ffeaed51),
|
||||
[#5357](https://github.com/angular/angular.js/issues/5357), [#5283](https://github.com/angular/angular.js/issues/5283))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **compile:** add class 'ng-scope' before cloning and other micro-optimizations
|
||||
([f3a796e5](https://github.com/angular/angular.js/commit/f3a796e522afdbd3b640d14426edb2fbfab463c5),
|
||||
[#5471](https://github.com/angular/angular.js/issues/5471))
|
||||
- **$parse:** use a faster path when the number of path parts is low
|
||||
([f4462319](https://github.com/angular/angular.js/commit/864b2596b246470cca9d4e223eaed720f4462319))
|
||||
- use faster check for `$$` prefix
|
||||
([06c5cfc7](https://github.com/angular/angular.js/commit/cb29632a5802e930262919b3db64ca4806c5cfc7))
|
||||
|
||||
<a name="1.2.5"></a>
|
||||
# 1.2.5 singularity-expansion (2013-12-13)
|
||||
|
||||
@@ -4326,3 +4452,6 @@ with the `$route` service
|
||||
[module]: http://docs-next.angularjs.org/api/angular.mock.module
|
||||
[guide2.di]: http://docs-next.angularjs.org/guide/dev_guide.di
|
||||
[jqLite2]: http://docs.angularjs.org/#!/api/angular.element
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
@@ -258,3 +258,6 @@ You can find out more detailed information about contributing in the
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
+11
-1
@@ -4,6 +4,7 @@ var path = require('path');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
//grunt plugins
|
||||
grunt.loadNpmTasks('grunt-bump');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
@@ -268,6 +269,15 @@ module.exports = function(grunt) {
|
||||
write: {
|
||||
versionTXT: {file: 'build/version.txt', val: NG_VERSION.full},
|
||||
versionJSON: {file: 'build/version.json', val: JSON.stringify(NG_VERSION)}
|
||||
},
|
||||
|
||||
bump: {
|
||||
options: {
|
||||
files: ['package.json'],
|
||||
commit: false,
|
||||
createTag: false,
|
||||
push: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -287,6 +297,6 @@ module.exports = function(grunt) {
|
||||
grunt.registerTask('webserver', ['connect:devserver']);
|
||||
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('package-without-bower', ['clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'test:docgen']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
|
||||
@@ -38,3 +38,7 @@ To execute end-to-end (e2e) tests, use:
|
||||
|
||||
To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
|
||||
@@ -59,3 +59,5 @@ The following is done automatically and should not be done manually:
|
||||
|
||||
1. Unassign yourself from the issue
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
Vendored
+10
-1
@@ -762,7 +762,9 @@ angular.Module.requires;
|
||||
* $parent: angular.Scope,
|
||||
* $root: angular.Scope,
|
||||
* $watch: function(
|
||||
* (string|Function), (string|Function)=, boolean=):function()
|
||||
* (string|Function), (string|Function)=, boolean=):function(),
|
||||
* $watchCollection: function(
|
||||
* (string|Function), (string|Function)=):function()
|
||||
* }}
|
||||
*/
|
||||
angular.Scope;
|
||||
@@ -834,6 +836,13 @@ angular.Scope.$root;
|
||||
*/
|
||||
angular.Scope.$watch = function(exp, opt_listener, opt_objectEquality) {};
|
||||
|
||||
/**
|
||||
* @param {string|Function} exp
|
||||
* @param {(string|Function)=} opt_listener
|
||||
* @return {function()}
|
||||
*/
|
||||
angular.Scope.$watchCollection = function(exp, opt_listener) {};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* currentScope: angular.Scope,
|
||||
|
||||
@@ -9,14 +9,3 @@
|
||||
ng\:form {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* The styles below ensure that the CSS transition will ALWAYS
|
||||
* animate and close. A nasty bug occurs with CSS transitions where
|
||||
* when the active class isn't set, or if the active class doesn't
|
||||
* contain any styles to transition to, then, if ngAnimate is used,
|
||||
* it will appear as if the webpage is broken due to the forever hanging
|
||||
* animations. The border-spacing (!ie) and zoom (ie) CSS properties are
|
||||
* used below since they trigger a transition without making the browser
|
||||
* animate anything and they're both highly underused CSS properties */
|
||||
.ng-animate-start { border-spacing:1px 1px; -ms-zoom:1.0001; }
|
||||
.ng-animate-active { border-spacing:0px 0px; -ms-zoom:1; }
|
||||
|
||||
+1
-11
@@ -215,17 +215,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
||||
}];
|
||||
this.html5Mode = angular.noop;
|
||||
});
|
||||
$provide.decorator('$timeout', ['$rootScope', '$delegate', function($rootScope, $delegate) {
|
||||
return angular.extend(function(fn, delay) {
|
||||
if (delay && delay > 50) {
|
||||
return setTimeout(function() {
|
||||
$rootScope.$apply(fn);
|
||||
}, delay);
|
||||
} else {
|
||||
return $delegate.apply(this, arguments);
|
||||
}
|
||||
}, $delegate);
|
||||
}]);
|
||||
|
||||
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
|
||||
embedRootScope = $delegate;
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
@description
|
||||
|
||||
# ng (core module)
|
||||
The ng module is loaded by default when an AngularJS application is started. The module itself contains the essential components to for an AngularJS application to function. The table below lists a high level breakdown of each of the services/factories, filters, directives and testing components available within this core module.
|
||||
The ng module is loaded by default when an AngularJS application is started. The module itself
|
||||
contains the essential components for an AngularJS application to function. The table below
|
||||
lists a high level breakdown of each of the services/factories, filters, directives and testing
|
||||
components available within this core module.
|
||||
|
||||
<div doc-module-components="ng"></div>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $httpBackend:noxhr
|
||||
@fullName Unsupported XHR
|
||||
@description
|
||||
|
||||
This error occurs in browsers that do not support XmlHttpRequest. AngularJS
|
||||
supports Safari, Chrome, Firefox, Opera, IE8 and higher, and mobile browsers
|
||||
(Android, Chrome Mobile, iOS Safari). To avoid this error, use an officially
|
||||
supported browser.
|
||||
@@ -4,4 +4,4 @@
|
||||
@description
|
||||
This error occurs when 'ngPattern' is passed an expression that isn't a regular expression or doesn't have the expected format.
|
||||
|
||||
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:select input} directive docs.
|
||||
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:input input} directive docs.
|
||||
|
||||
@@ -13,7 +13,7 @@ For example the issue can be triggered by this *invalid* code:
|
||||
<div ng-repeat="value in [4, 4]"></div>
|
||||
```
|
||||
|
||||
To resolve this error either ensure that the items in the collection have unique identity of use the `track by` syntax to specify how to track the association between models and DOM.
|
||||
To resolve this error either ensure that the items in the collection have unique identity or use the `track by` syntax to specify how to track the association between models and DOM.
|
||||
|
||||
To resolve the example above can be resolved by using `track by $index`, which will cause the items to be keyed by their position in the array instead of their value:
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ of which depend on other services that are provided by the Angular framework:
|
||||
* @param {*} message Message to be logged.
|
||||
*/
|
||||
function batchLogModule($provide){
|
||||
$provide.factory('batchLog', ['$timeout', '$log', function($timeout, $log) {
|
||||
$provide.factory('batchLog', ['$interval', '$log', function($interval, $log) {
|
||||
var messageQueue = [];
|
||||
|
||||
function log() {
|
||||
@@ -58,11 +58,10 @@ of which depend on other services that are provided by the Angular framework:
|
||||
$log('batchLog messages: ', messageQueue);
|
||||
messageQueue = [];
|
||||
}
|
||||
$timeout(log, 50000);
|
||||
}
|
||||
|
||||
// start periodic checking
|
||||
log();
|
||||
$interval(log, 50000);
|
||||
|
||||
return function(message) {
|
||||
messageQueue.push(message);
|
||||
@@ -82,13 +81,13 @@ of which depend on other services that are provided by the Angular framework:
|
||||
}]);
|
||||
}
|
||||
|
||||
// get the main service to kick of the application
|
||||
// get the main service to kick off the application
|
||||
angular.injector([batchLogModule]).get('routeTemplateMonitor');
|
||||
</pre>
|
||||
|
||||
Things to notice in this example:
|
||||
|
||||
* The `batchLog` service depends on the built-in {@link api/ng.$timeout $timeout} and
|
||||
* The `batchLog` service depends on the built-in {@link api/ng.$interval $interval} and
|
||||
{@link api/ng.$log $log} services, and allows messages to be logged into the
|
||||
`console.log` in batches.
|
||||
* The `routeTemplateMonitor` service depends on the built-in {@link api/ngRoute.$route
|
||||
|
||||
@@ -163,7 +163,7 @@ function MyClass(xhr) {
|
||||
This is the preferred method since the code makes no assumptions about the origin of `xhr` and cares
|
||||
instead about whoever created the class responsible for passing it in. Since the creator of the
|
||||
class should be different code than the user of the class, it separates the responsibility of
|
||||
creation from the logic. This is dependency-injection is in a nutshell.
|
||||
creation from the logic. This is dependency-injection in a nutshell.
|
||||
|
||||
The class above is testable, since in the test we can write:
|
||||
<pre>
|
||||
|
||||
@@ -506,6 +506,8 @@ that you explicitly pass in.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** Normally, a scope prototypically inherits from its parent. An isolated scope does not.
|
||||
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
|
||||
"Isolating the Scope of a Directive"} section for more information about isolate scopes.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success">
|
||||
@@ -528,8 +530,10 @@ where:
|
||||
* `attrs` is an object with the normalized attribute names and their corresponding values.
|
||||
|
||||
In our `link` function, we want to update the displayed time once a second, or whenever a user
|
||||
changes the time formatting string that our directive binds to. We also want to remove the timeout
|
||||
if the directive is deleted so we don't introduce a memory leak.
|
||||
changes the time formatting string that our directive binds to. We will use the `$interval` service
|
||||
to call a handler on a regular basis. This is easier than using `$timeout` but also works better with
|
||||
end 2 end testing, where we want to ensure that all $timeouts have completed before completing the test.
|
||||
We also want to remove the `$interval` if the directive is deleted so we don't introduce a memory leak.
|
||||
|
||||
<example module="docsTimeDirective">
|
||||
<file name="script.js">
|
||||
@@ -537,7 +541,7 @@ if the directive is deleted so we don't introduce a memory leak.
|
||||
.controller('Ctrl2', function($scope) {
|
||||
$scope.format = 'M/d/yy h:mm:ss a';
|
||||
})
|
||||
.directive('myCurrentTime', function($timeout, dateFilter) {
|
||||
.directive('myCurrentTime', function($interval, dateFilter) {
|
||||
|
||||
function link(scope, element, attrs) {
|
||||
var format,
|
||||
@@ -552,20 +556,14 @@ if the directive is deleted so we don't introduce a memory leak.
|
||||
updateTime();
|
||||
});
|
||||
|
||||
function scheduleUpdate() {
|
||||
// save the timeoutId for canceling
|
||||
timeoutId = $timeout(function() {
|
||||
updateTime(); // update DOM
|
||||
scheduleUpdate(); // schedule the next update
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
element.on('$destroy', function() {
|
||||
$timeout.cancel(timeoutId);
|
||||
$interval.cancel(timeoutId);
|
||||
});
|
||||
|
||||
// start the UI update process.
|
||||
scheduleUpdate();
|
||||
// start the UI update process; save the timeoutId for canceling
|
||||
timeoutId = $interval(function() {
|
||||
updateTime(); // update DOM
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -583,7 +581,7 @@ if the directive is deleted so we don't introduce a memory leak.
|
||||
|
||||
There are a couple of things to note here.
|
||||
Just like the `module.controller` API, the function argument in `module.directive` is dependency
|
||||
injected. Because of this, we can use `$timeout` and `dateFilter` inside our directive's `link`
|
||||
injected. Because of this, we can use `$interval` and `dateFilter` inside our directive's `link`
|
||||
function.
|
||||
|
||||
We register an event `element.on('$destroy', ...)`. What fires this `$destroy` event?
|
||||
|
||||
@@ -33,10 +33,10 @@ In addition it provides an {@link api/ng.directive:ngModel.NgModelController API
|
||||
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
$scope.master = {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
$scope.master = angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
@@ -116,7 +116,7 @@ This ensures that the user is not distracted with an error until after interacti
|
||||
A form is an instance of {@link api/ng.directive:form.FormController FormController}.
|
||||
The form instance can optionally be published into the scope using the `name` attribute.
|
||||
|
||||
Similarly, an input control that has the {@link api.ng.directive:ng-model} directive holds an
|
||||
Similarly, an input control that has the {@link api/ng.directive:ngModel} directive holds an
|
||||
instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
|
||||
Such a control instance can be published as a property of the form instance using the `name` attribute
|
||||
on the input control. The name attribute specifies the name of the property on the form instance.
|
||||
@@ -235,7 +235,7 @@ In the following example we create two directives.
|
||||
<script>
|
||||
var app = angular.module('form-example1', []);
|
||||
|
||||
var INTEGER_REGEXP = /^\-?\d*$/;
|
||||
var INTEGER_REGEXP = /^\-?\d+$/;
|
||||
app.directive('integer', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
|
||||
@@ -18,8 +18,12 @@ watch {@link guide/expression expressions} and propagate events.
|
||||
propagate any model changes through the system into the view from outside of the "Angular
|
||||
realm" (controllers, services, Angular event handlers).
|
||||
|
||||
- Scopes can be nested to isolate application components while providing access to shared model
|
||||
properties. A scope (prototypically) inherits properties from its parent scope.
|
||||
- Scopes can be nested to limit access to the properties of application components while providing
|
||||
access to shared model properties. Nested scopes are either "child scopes" or "isolate scopes".
|
||||
A "child scope" (prototypically) inherits properties from its parent scope. An "isolate scope"
|
||||
does not. See {@link
|
||||
guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive isolated
|
||||
scopes} for more information.
|
||||
|
||||
- Scopes provide context against which {@link guide/expression expressions} are evaluated. For
|
||||
example `{{username}}` expression is meaningless, unless it is evaluated against a specific
|
||||
@@ -259,8 +263,8 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
|
||||
For mutations to be properly observed, you should make them only within the {@link
|
||||
api/ng.$rootScope.Scope#methods_$apply scope.$apply()}. (Angular APIs do this
|
||||
implicitly, so no extra `$apply` call is needed when doing synchronous work in controllers,
|
||||
or asynchronous work with {@link api/ng.$http $http} or {@link
|
||||
api/ng.$timeout $timeout} services.
|
||||
or asynchronous work with {@link api/ng.$http $http}, {@link api/ng.$timeout $timeout}
|
||||
or {@link api/ng.$interval $interval} services.
|
||||
|
||||
4. **Mutation observation**
|
||||
|
||||
@@ -306,6 +310,8 @@ api/ng.directive:ngController ng-controller} and {@link
|
||||
api/ng.directive:ngRepeat ng-repeat}, create new child scopes
|
||||
and attach the child scope to the corresponding DOM element. You can retrieve a scope for any DOM
|
||||
element by using an `angular.element(aDomElement).scope()` method call.
|
||||
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
|
||||
directives guide} for more information about isolate scopes.
|
||||
|
||||
### Controllers and Scopes
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ and included in your {@link http://docs.oracle.com/javase/tutorial/essential/env
|
||||
npm install -g bower
|
||||
```
|
||||
|
||||
**Note:** You may need to use sudo (for OSX, *nix, BSD etc) or run your command shell as Administrator (for Windows) to install Grunt &
|
||||
Bower globally.
|
||||
|
||||
## Forking Angular on Github
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ For the purposes of this tutorial, we modified the angular-seed with the followi
|
||||
* Removed the example app
|
||||
* Added phone images to `app/img/phones/`
|
||||
* Added phone data files (JSON) to `app/phones/`
|
||||
* Added [Bootstrap](http://twitter.github.com/bootstrap/) files to `app/css/` and `app/img/`
|
||||
* Added [Bootstrap](http://getbootstrap.com) files to `app/css/` and `app/img/`
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
|
||||
record. This property is used to order phones by age.
|
||||
|
||||
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
|
||||
not set the default value here, the model would stay uninitialized until our user would pick an
|
||||
not set the default value here, the model would stay uninitialized until our user picks an
|
||||
option from the drop down menu.
|
||||
|
||||
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
|
||||
|
||||
@@ -199,7 +199,7 @@ isolated from the work done in other tests.
|
||||
|
||||
* We created a new scope for our controller by calling `$rootScope.$new()`
|
||||
|
||||
* We called the injected `$controller` function passing the name of the`PhoneListCtrl` controller
|
||||
* We called the injected `$controller` function passing the name of the `PhoneListCtrl` controller
|
||||
and the created scope as parameters.
|
||||
|
||||
Because our code now uses the `$http` service to fetch the phone list data in our controller, before
|
||||
|
||||
@@ -11,7 +11,7 @@ In this step, you will improve the way our app fetches data.
|
||||
<div doc-tutorial-reset="11"></div>
|
||||
|
||||
|
||||
The last improvement we will make to our app is to define a custom service that represents a {@link
|
||||
The next improvement we will make to our app is to define a custom service that represents a {@link
|
||||
http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client. Using this client we
|
||||
can make XHR requests for data in an easier way, without having to deal with the lower-level {@link
|
||||
api/ng.$http $http} API, HTTP methods and URLs.
|
||||
@@ -185,7 +185,7 @@ describe('PhoneCat controllers', function() {
|
||||
xyzPhoneData = function() {
|
||||
return {
|
||||
name: 'phone xyz',
|
||||
images: ['image/url1.png', 'image/url2.png']
|
||||
images: ['image/url1.png', 'image/url2.png']
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
exports.appCache = appCache;
|
||||
var fs = require('q-fs');
|
||||
var fs = require('q-io/fs');
|
||||
var Q = require('qq');
|
||||
function identity($) {return $;}
|
||||
|
||||
|
||||
@@ -74,10 +74,10 @@ function writeTheRest(writesFuture) {
|
||||
var versions = ngdoc.ngVersions();
|
||||
var currentVersion = ngdoc.ngCurrentVersion();
|
||||
|
||||
writesFuture.push(writer.symlink('../../docs/content/notes', 'build/docs/notes', 'dir'));
|
||||
writesFuture.push(writer.symlinkTemplate('css', 'dir'));
|
||||
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'dir'));
|
||||
writesFuture.push(writer.symlinkTemplate('js', 'dir'));
|
||||
writesFuture.push(writer.symlink('../../docs/content/notes', 'build/docs/notes', 'directory'));
|
||||
writesFuture.push(writer.symlinkTemplate('css', 'directory'));
|
||||
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'directory'));
|
||||
writesFuture.push(writer.symlinkTemplate('js', 'directory'));
|
||||
|
||||
var manifest = 'manifest="/build/docs/appcache.manifest"';
|
||||
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ exports.collect = collect;
|
||||
|
||||
var ngdoc = require('./ngdoc.js'),
|
||||
Q = require('qq'),
|
||||
qfs = require('q-fs'),
|
||||
qfs = require('q-io/fs'),
|
||||
PATH = require('path');
|
||||
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
@@ -543,6 +543,10 @@ pre ol li {
|
||||
margin-bottom:30px;
|
||||
}
|
||||
|
||||
.definition-table td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.component-heading {
|
||||
text-transform:capitalize;
|
||||
}
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
* for testability
|
||||
*/
|
||||
var pathUtils = require('path');
|
||||
var qfs = require('q-fs');
|
||||
var qfs = require('q-io/fs');
|
||||
var Q = require('qq');
|
||||
var OUTPUT_DIR = pathUtils.join('build','docs');
|
||||
var TEMPLATES_DIR = pathUtils.join('docs','src','templates');
|
||||
@@ -76,7 +76,7 @@ function symlink(from, to, type) {
|
||||
// qfs will normalize the path arguments for us here
|
||||
return qfs.exists(to).then(function(exists) {
|
||||
if (!exists) {
|
||||
return qfs.symbolicLink(to, from, type);
|
||||
return qfs.symbolicLink(to, from, type || 'file');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+6
-2
@@ -1,9 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Jenkins Build ############"
|
||||
echo "#################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
|
||||
|
||||
# Define reasonable set of browsers in case we are running manually from commandline
|
||||
if [[ -z "$BROWSERS" ]]
|
||||
then
|
||||
@@ -25,11 +28,12 @@ rm -f angular.js.size
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
|
||||
# DOCS generator unit tests #
|
||||
grunt test:docgen --no-color
|
||||
|
||||
# UNIT TESTS #
|
||||
grunt test:unit --browsers $BROWSERS --reporters=dots,junit --no-colors --no-color
|
||||
|
||||
|
||||
# END TO END TESTS #
|
||||
grunt test:e2e --browsers $BROWSERS_E2E --reporters=dots,junit --no-colors --no-color
|
||||
|
||||
|
||||
@@ -13,7 +13,10 @@ module.exports = function(config, specificOptions) {
|
||||
// SauceLabs config for local development.
|
||||
sauceLabs: {
|
||||
testName: specificOptions.testName || 'AngularJS',
|
||||
startConnect: true
|
||||
startConnect: true,
|
||||
options: {
|
||||
'selenium-version': '2.37.0'
|
||||
}
|
||||
},
|
||||
|
||||
// BrowserStack config for local development.
|
||||
|
||||
+7
-2
@@ -36,13 +36,12 @@ module.exports = {
|
||||
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
|
||||
var match = package.version.match(/^([^\-]*)(?:\-(.+))?$/);
|
||||
var semver = match[1].split('.');
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
|
||||
var fullVersion = match[1];
|
||||
|
||||
if (match[2]) {
|
||||
fullVersion += '-';
|
||||
fullVersion += (match[2] == 'snapshot') ? hash : match[2];
|
||||
fullVersion += (match[2] == 'snapshot') ? getSnapshotSuffix() : match[2];
|
||||
}
|
||||
|
||||
version = {
|
||||
@@ -55,6 +54,12 @@ module.exports = {
|
||||
};
|
||||
|
||||
return version;
|
||||
|
||||
function getSnapshotSuffix() {
|
||||
var jenkinsBuild = process.env.BUILD_NUMBER || 'local';
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
return 'build.'+jenkinsBuild+'+sha.'+hash;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
+7
-6
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"version": "1.2.5",
|
||||
"cdnVersion": "1.2.4",
|
||||
"codename": "singularity-expansion",
|
||||
"version": "1.2.7",
|
||||
"cdnVersion": "1.2.6",
|
||||
"codename": "emoji-clairvoyance",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
@@ -10,22 +10,23 @@
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.1",
|
||||
"bower": "~1.2.2",
|
||||
"grunt-bump": "~0.0.13",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.5.2",
|
||||
"grunt-contrib-connect": "~0.5.0",
|
||||
"grunt-contrib-copy": "~0.4.1",
|
||||
"jasmine-node": "~1.11.0",
|
||||
"q": "~0.9.2",
|
||||
"q-fs": "~0.1.36",
|
||||
"q-io": "~1.10.6",
|
||||
"qq": "~0.3.5",
|
||||
"shelljs": "~0.2.6",
|
||||
"karma": "~0.11",
|
||||
"karma": "0.11.11",
|
||||
"karma-jasmine": "~0.1.0",
|
||||
"karma-chrome-launcher": "~0.1.0",
|
||||
"karma-firefox-launcher": "~0.1.0",
|
||||
"karma-ng-scenario": "~0.1.0",
|
||||
"karma-junit-reporter": "~0.2.1",
|
||||
"karma-sauce-launcher": "~0.1.1",
|
||||
"karma-sauce-launcher": "~0.2.0",
|
||||
"karma-script-launcher": "~0.1.0",
|
||||
"yaml-js": "~0.0.8",
|
||||
"marked": "0.2.9",
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function catch_errors() {
|
||||
echo "ERROR. That's life."
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap catch_errors ERR
|
||||
|
||||
TMP_FILE='changelog.tmp'
|
||||
CHANGELOG_FILE='CHANGELOG.md'
|
||||
|
||||
echo "Getting current version..."
|
||||
VERSION=`./version.js --current`
|
||||
|
||||
echo "Generating changelog..."
|
||||
./changelog.js $VERSION $TMP_FILE
|
||||
|
||||
cat $CHANGELOG_FILE >> $TMP_FILE
|
||||
mv -f $TMP_FILE $CHANGELOG_FILE
|
||||
|
||||
|
||||
echo "Updating version..."
|
||||
./version.js --remove-snapshot
|
||||
|
||||
echo "CONFIRM TO COMMIT"
|
||||
read WHATEVER
|
||||
|
||||
|
||||
echo "Creating commit..."
|
||||
git commit version.yaml CHANGELOG.md -m "chore(relase): cutting the v$VERSION release"
|
||||
|
||||
echo "Creating tag..."
|
||||
git tag "v$VERSION"
|
||||
+3
-11
@@ -1,23 +1,15 @@
|
||||
# Angular Bower Script
|
||||
|
||||
Script for updating the Angular bower repos from a code.angularjs.org package
|
||||
|
||||
Requires `node` (for parsing `bower.json`) and `wget` (for fetching the `angular.zip` from `code.angularjs.org`)
|
||||
|
||||
Script for updating the Angular bower repos from current local build.
|
||||
|
||||
## Instructions
|
||||
|
||||
You need to run `./init.sh` the first time you use this script to clone all the repos.
|
||||
|
||||
For subsequent updates:
|
||||
`grunt package`: Build angular locally
|
||||
|
||||
```shell
|
||||
./publish.sh NEW_VERSION
|
||||
./publish.sh
|
||||
```
|
||||
|
||||
Where `NEW_VERSION` is a version number like `1.2.3`.
|
||||
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# init all of the bower repos
|
||||
#
|
||||
|
||||
set -e # fail if any command fails
|
||||
|
||||
REPOS=(
|
||||
angular \
|
||||
angular-animate \
|
||||
angular-cookies \
|
||||
angular-i18n \
|
||||
angular-loader \
|
||||
angular-mocks \
|
||||
angular-route \
|
||||
angular-resource \
|
||||
angular-sanitize \
|
||||
angular-scenario \
|
||||
angular-touch \
|
||||
)
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
git clone git@github.com:angular/bower-$repo.git
|
||||
done
|
||||
+33
-35
@@ -1,18 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# update all the things
|
||||
#
|
||||
|
||||
set -e # fail if any command fails
|
||||
echo "#################################"
|
||||
echo "#### Update bower ###############"
|
||||
echo "#################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`
|
||||
|
||||
NEW_VERSION=$1
|
||||
|
||||
ZIP_FILE=angular-$NEW_VERSION.zip
|
||||
ZIP_FILE_URL=http://code.angularjs.org/$NEW_VERSION/angular-$NEW_VERSION.zip
|
||||
ZIP_DIR=angular-$NEW_VERSION
|
||||
SCRIPT_DIR=`pwd`
|
||||
TMP_DIR=../../tmp
|
||||
BUILD_DIR=../../build
|
||||
NEW_VERSION=`cat $BUILD_DIR/version.txt`
|
||||
|
||||
REPOS=(
|
||||
angular \
|
||||
@@ -28,50 +28,42 @@ REPOS=(
|
||||
angular-touch \
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# download and unzip the file
|
||||
# clone repos
|
||||
#
|
||||
|
||||
if [ ! -f $ZIP_FILE ]; then
|
||||
wget $ZIP_FILE_URL
|
||||
unzip $ZIP_FILE
|
||||
fi
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
echo "-- Cloning bower-$repo"
|
||||
git clone git@github.com:angular/bower-$repo.git $TMP_DIR/bower-$repo
|
||||
done
|
||||
|
||||
|
||||
#
|
||||
# move the files from the zip
|
||||
# move the files from the build
|
||||
#
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
if [ -f $ZIP_DIR/$repo.js ] # ignore i18l
|
||||
if [ -f $BUILD_DIR/$repo.js ] # ignore i18l
|
||||
then
|
||||
cd bower-$repo
|
||||
echo "-- Updating files in bower-$repo"
|
||||
cd $TMP_DIR/bower-$repo
|
||||
git reset --hard HEAD
|
||||
git checkout master
|
||||
git fetch --all
|
||||
git reset --hard origin/master
|
||||
cd ..
|
||||
mv $ZIP_DIR/$repo.* bower-$repo/
|
||||
cd $SCRIPT_DIR
|
||||
cp $BUILD_DIR/$repo.* $TMP_DIR/bower-$repo/
|
||||
fi
|
||||
done
|
||||
|
||||
# move i18n files
|
||||
mv $ZIP_DIR/i18n/*.js bower-angular-i18n/
|
||||
cp $BUILD_DIR/i18n/*.js $TMP_DIR/bower-angular-i18n/
|
||||
|
||||
# move csp.css
|
||||
mv $ZIP_DIR/angular-csp.css bower-angular
|
||||
cp $BUILD_DIR/angular-csp.css $TMP_DIR/bower-angular
|
||||
|
||||
|
||||
#
|
||||
# get the old version number
|
||||
#
|
||||
|
||||
OLD_VERSION=$(node -e "console.log(require('./bower-angular/bower').version)" | sed -e 's/\r//g')
|
||||
echo $OLD_VERSION
|
||||
echo $NEW_VERSION
|
||||
|
||||
#
|
||||
# update bower.json
|
||||
# tag each repo
|
||||
@@ -79,12 +71,18 @@ echo $NEW_VERSION
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
cd bower-$repo
|
||||
sed -i '' -e "s/$OLD_VERSION/$NEW_VERSION/g" bower.json
|
||||
echo "-- Updating version in bower-$repo to $NEW_VERSION"
|
||||
cd $TMP_DIR/bower-$repo
|
||||
sed -i .tmp -E 's/"(version)":[ ]*".*"/"\1": "'$NEW_VERSION'"/g' bower.json
|
||||
sed -i .tmp -E 's/"(angular.*)":[ ]*".*"/"\1": "'$NEW_VERSION'"/g' bower.json
|
||||
# delete tmp files
|
||||
rm *.tmp
|
||||
git add -A
|
||||
|
||||
echo "-- Committing, tagging and pushing bower-$repo"
|
||||
git commit -m "v$NEW_VERSION"
|
||||
git tag v$NEW_VERSION
|
||||
git push origin master
|
||||
git push origin v$NEW_VERSION
|
||||
cd ..
|
||||
cd $SCRIPT_DIR
|
||||
done
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# code.angular.js.org Script
|
||||
|
||||
Script for updating code.angularjs.org repo from current local build.
|
||||
|
||||
Note: For a snapshot build, this will fetch the data from the ci server
|
||||
and NOT take the local build!
|
||||
|
||||
## Instructions
|
||||
|
||||
`grunt package`: Build angular locally
|
||||
|
||||
```shell
|
||||
./publish.sh
|
||||
```
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update code.angular.js.org ###"
|
||||
echo "#################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`
|
||||
|
||||
TMP_DIR=../../tmp
|
||||
REPO_DIR=$TMP_DIR/code.angularjs.org
|
||||
BUILD_DIR=../../build
|
||||
SCRIPT_DIR=`pwd`
|
||||
NEW_VERSION=`cat $BUILD_DIR/version.txt`
|
||||
|
||||
#
|
||||
# Snapshot builds are kept in a temp directory in code.angularjs.org
|
||||
# that is filled by calling a php script there.
|
||||
#
|
||||
if [[ "$NEW_VERSION" =~ sha ]] ;then
|
||||
echo "-- updating snapshot version"
|
||||
curl -G --data-urlencode "ver=$NEW_VERSION" http://code.angularjs.org/fetchLatestSnapshot.php
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
#
|
||||
# clone
|
||||
#
|
||||
|
||||
echo "-- Cloning code.angularjs.org"
|
||||
git clone git@github.com:angular/code.angularjs.org.git $REPO_DIR
|
||||
|
||||
#
|
||||
# copy the files from the build
|
||||
#
|
||||
|
||||
echo "-- Updating code.angularjs.org"
|
||||
mkdir $REPO_DIR/$NEW_VERSION
|
||||
cd $REPO_DIR
|
||||
git reset --hard HEAD
|
||||
git checkout master
|
||||
git fetch --all
|
||||
git reset --hard origin/master
|
||||
cd $SCRIPT_DIR
|
||||
cp -r $BUILD_DIR/* $REPO_DIR/$NEW_VERSION/
|
||||
|
||||
#
|
||||
# commit and push
|
||||
#
|
||||
echo "-- Committing and pushing code.angularjs.org"
|
||||
cd $REPO_DIR
|
||||
git add -A
|
||||
git commit -m "v$NEW_VERSION"
|
||||
git push origin master
|
||||
cd $SCRIPT_DIR
|
||||
|
||||
#
|
||||
# refresh code.angularjs.org from github
|
||||
#
|
||||
curl http://code.angularjs.org/gitFetchSite.php
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "############################################"
|
||||
echo "## Increment version and add "-snapshot" ##"
|
||||
echo "############################################"
|
||||
|
||||
if [ "$1" != "patch" -a "$1" != "minor" -a "$1" != "major" ]; then
|
||||
echo "Please specify the next version type: patch|minor|major"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_TYPE=$1
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`/../..
|
||||
|
||||
echo "-- increment version "
|
||||
grunt bump:$BUMP_TYPE
|
||||
NEXT_VERSION=`sed -En 's/.*"version"[ ]*:[ ]*"(.*)".*/\1/p' package.json`
|
||||
sed -i .tmp -E 's/"version": "(.*)"/"version": "\1-snapshot"/' package.json
|
||||
echo "-- new version: `grep '"version"' package.json`"
|
||||
echo "-- commit"
|
||||
git add package.json
|
||||
git commit -m "chore(release): start v$NEXT_VERSION"
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "############################################"
|
||||
echo "## Remove "-snapshot" from version ########"
|
||||
echo "############################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`/../..
|
||||
|
||||
echo "-- old version: `grep '"version"' package.json`"
|
||||
sed -i .tmp -E 's/"version": "(.*)-snapshot"/"version": "\1"/' package.json
|
||||
VERSION=`sed -En 's/.*"version"[ ]*:[ ]*"(.*)".*/\1/p' package.json`
|
||||
echo "-- local version: $VERSION"
|
||||
|
||||
echo "-- commit and tag with v$VERSION"
|
||||
git add package.json
|
||||
git commit -m "chore(release): cut v$VERSION release"
|
||||
git tag -m "v$VERSION" v$VERSION
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Update master ##############"
|
||||
echo "#################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
|
||||
cd `dirname $0`/../..
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Jenkins Build ############"
|
||||
echo "#################################"
|
||||
./jenkins_build.sh
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update code.angular.js.org ###"
|
||||
echo "#################################"
|
||||
./scripts/code.angularjs.org/publish.sh
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Update bower ###############"
|
||||
echo "#################################"
|
||||
./scripts/bower/publish.sh
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Cut release ################"
|
||||
echo "#################################"
|
||||
|
||||
if [ "$1" != "patch" -a "$1" != "minor" -a "$1" != "major" ]; then
|
||||
echo "Please specify the next version type: patch|minor|major"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_TYPE=$1
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
|
||||
# Jump onto the master branch and make sure we are using the latest
|
||||
git checkout -f master
|
||||
git merge --ff-only origin/master
|
||||
|
||||
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`/../..
|
||||
|
||||
|
||||
# Bump versions: remove "-snapshot" suffix
|
||||
./scripts/jenkins/bump-remove-snapshot.sh
|
||||
|
||||
# Build
|
||||
./jenkins_build.sh
|
||||
|
||||
# Bump versions: Increment version and add "-snapshot"
|
||||
./scripts/jenkins/bump-increment.sh $BUMP_TYPE
|
||||
|
||||
echo "-- push to Github"
|
||||
# push the commits to github
|
||||
git push origin master
|
||||
# push the release tag
|
||||
git push origin v`cat build/version.txt`
|
||||
|
||||
# Update code.angularjs.org
|
||||
./scripts/code.angularjs.org/publish.sh
|
||||
|
||||
# Update bower
|
||||
./scripts/bower/publish.sh
|
||||
+7
-3
@@ -213,7 +213,9 @@ function forEach(obj, iterator, context) {
|
||||
if (obj) {
|
||||
if (isFunction(obj)){
|
||||
for (key in obj) {
|
||||
if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) {
|
||||
// Need to check if hasOwnProperty exists,
|
||||
// as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
|
||||
if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
|
||||
iterator.call(context, obj[key], key);
|
||||
}
|
||||
}
|
||||
@@ -769,7 +771,7 @@ function shallowCopy(src, dst) {
|
||||
for(var key in src) {
|
||||
// shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
|
||||
// so we don't need to worry about using our custom hasOwnProperty here
|
||||
if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') {
|
||||
if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
@@ -957,7 +959,9 @@ function fromJson(json) {
|
||||
|
||||
|
||||
function toBoolean(value) {
|
||||
if (value && value.length !== 0) {
|
||||
if (typeof value === 'function') {
|
||||
value = true;
|
||||
} else if (value && value.length !== 0) {
|
||||
var v = lowercase("" + value);
|
||||
value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
|
||||
} else {
|
||||
|
||||
@@ -485,15 +485,15 @@ function annotate(fn) {
|
||||
* {@link AUTO.$provide#methods_service $provide.service(class)} that is defined as a CoffeeScript class.
|
||||
* <pre>
|
||||
* class Ping
|
||||
* constructor: (@$http)->
|
||||
* send: ()=>
|
||||
* constructor: (@$http) ->
|
||||
* send: () =>
|
||||
* @$http.get('/ping')
|
||||
*
|
||||
* $provide.service('ping', ['$http', Ping])
|
||||
* </pre>
|
||||
* You would then inject and use this service like this:
|
||||
* <pre>
|
||||
* someModule.controller 'Ctrl', ['ping', (ping)->
|
||||
* someModule.controller 'Ctrl', ['ping', (ping) ->
|
||||
* ping.send()
|
||||
* ]
|
||||
* </pre>
|
||||
@@ -740,6 +740,11 @@ function createInjector(modulesToLoad) {
|
||||
path.unshift(serviceName);
|
||||
cache[serviceName] = INSTANTIATING;
|
||||
return cache[serviceName] = factory(serviceName);
|
||||
} catch (err) {
|
||||
if (cache[serviceName] === INSTANTIATING) {
|
||||
delete cache[serviceName];
|
||||
}
|
||||
throw err;
|
||||
} finally {
|
||||
path.shift();
|
||||
}
|
||||
|
||||
+18
-1
@@ -54,6 +54,7 @@
|
||||
* - [`next()`](http://api.jquery.com/next/) - Does not support selectors
|
||||
* - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
|
||||
* - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
|
||||
* - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
|
||||
* - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
|
||||
* - [`prepend()`](http://api.jquery.com/prepend/)
|
||||
* - [`prop()`](http://api.jquery.com/prop/)
|
||||
@@ -645,7 +646,10 @@ function createEventHandler(element, events) {
|
||||
return event.defaultPrevented || event.returnValue === false;
|
||||
};
|
||||
|
||||
forEach(events[type || event.type], function(fn) {
|
||||
// Copy event handlers in case event handlers array is modified during execution.
|
||||
var eventHandlersCopy = shallowCopy(events[type || event.type] || []);
|
||||
|
||||
forEach(eventHandlersCopy, function(fn) {
|
||||
fn.call(element, event);
|
||||
});
|
||||
|
||||
@@ -741,6 +745,19 @@ forEach({
|
||||
|
||||
off: jqLiteOff,
|
||||
|
||||
one: function(element, type, fn) {
|
||||
element = jqLite(element);
|
||||
|
||||
//add the listener twice so that when it is called
|
||||
//you can remove the original function and still be
|
||||
//able to call element.off(ev, fn) normally
|
||||
element.on(type, function onFn() {
|
||||
element.off(type, fn);
|
||||
element.off(type, onFn);
|
||||
});
|
||||
element.on(type, fn);
|
||||
},
|
||||
|
||||
replaceWith: function(element, replaceNode) {
|
||||
var index, parent = element.parentNode;
|
||||
jqLiteDealoc(element);
|
||||
|
||||
@@ -61,6 +61,28 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
$provide.factory(key, factory);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$animateProvider#classNameFilter
|
||||
* @methodOf ng.$animateProvider
|
||||
*
|
||||
* @description
|
||||
* Sets and/or returns the CSS class regular expression that is checked when performing
|
||||
* an animation. Upon bootstrap the classNameFilter value is not set at all and will
|
||||
* therefore enable $animate to attempt to perform an animation on any element.
|
||||
* When setting the classNameFilter value, animations will only be performed on elements
|
||||
* that successfully match the filter expression. This in turn can boost performance
|
||||
* for low-powered devices as well as applications containing a lot of structural operations.
|
||||
* @param {RegExp=} expression The className expression which will be checked against all animations
|
||||
* @return {RegExp} The current CSS className expression value. If null then there is no expression value
|
||||
*/
|
||||
this.classNameFilter = function(expression) {
|
||||
if(arguments.length === 1) {
|
||||
this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
|
||||
}
|
||||
return this.$$classNameFilter;
|
||||
};
|
||||
|
||||
this.$get = ['$timeout', function($timeout) {
|
||||
|
||||
/**
|
||||
|
||||
+7
-6
@@ -148,8 +148,9 @@ function Browser(window, document, $log, $sniffer) {
|
||||
* @param {boolean=} replace Should new url replace current history record ?
|
||||
*/
|
||||
self.url = function(url, replace) {
|
||||
// Android Browser BFCache causes location reference to become stale.
|
||||
// Android Browser BFCache causes location, history reference to become stale.
|
||||
if (location !== window.location) location = window.location;
|
||||
if (history !== window.history) history = window.history;
|
||||
|
||||
// setter
|
||||
if (url) {
|
||||
@@ -201,7 +202,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
* @description
|
||||
* Register callback function that will be called, when url changes.
|
||||
*
|
||||
* It's only called when the url is changed by outside of angular:
|
||||
* It's only called when the url is changed from outside of angular:
|
||||
* - user types different url into address bar
|
||||
* - user clicks on history (forward/back) button
|
||||
* - user clicks on a link
|
||||
@@ -243,7 +244,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
/**
|
||||
* @name ng.$browser#baseHref
|
||||
* @methodOf ng.$browser
|
||||
*
|
||||
*
|
||||
* @description
|
||||
* Returns current <base href>
|
||||
* (always relative - without domain)
|
||||
@@ -252,7 +253,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
*/
|
||||
self.baseHref = function() {
|
||||
var href = baseElement.attr('href');
|
||||
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
|
||||
return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
@@ -274,13 +275,13 @@ function Browser(window, document, $log, $sniffer) {
|
||||
* It is not meant to be used directly, use the $cookie service instead.
|
||||
*
|
||||
* The return values vary depending on the arguments that the method was called with as follows:
|
||||
*
|
||||
*
|
||||
* - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
|
||||
* it
|
||||
* - cookies(name, value) -> set name to value, if value is undefined delete the cookie
|
||||
* - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
|
||||
* way)
|
||||
*
|
||||
*
|
||||
* @returns {Object} Hash of all cookies (if called without any parameter)
|
||||
*/
|
||||
self.cookies = function(name, value) {
|
||||
|
||||
+26
-23
@@ -24,7 +24,7 @@
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Compiles a piece of HTML string or DOM into a template and produces a template function, which
|
||||
* Compiles an HTML string or DOM into a template and produces a template function, which
|
||||
* can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
|
||||
*
|
||||
* The compilation is a process of walking the DOM tree and matching DOM elements to
|
||||
@@ -469,14 +469,14 @@
|
||||
* example would not point to the clone, but rather to the original template that was cloned. In
|
||||
* this case, you can access the clone via the cloneAttachFn:
|
||||
* <pre>
|
||||
* var templateHTML = angular.element('<p>{{total}}</p>'),
|
||||
* var templateElement = angular.element('<p>{{total}}</p>'),
|
||||
* scope = ....;
|
||||
*
|
||||
* var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) {
|
||||
* var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
|
||||
* //attach the clone to DOM document at the right place
|
||||
* });
|
||||
*
|
||||
* //now we have reference to the cloned DOM via `clone`
|
||||
* //now we have reference to the cloned DOM via `clonedElement`
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
@@ -818,6 +818,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
var compositeLinkFn =
|
||||
compileNodes($compileNodes, transcludeFn, $compileNodes,
|
||||
maxPriority, ignoreDirective, previousCompileContext);
|
||||
safeAddClass($compileNodes, 'ng-scope');
|
||||
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers){
|
||||
assertArg(scope, 'scope');
|
||||
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
|
||||
@@ -832,12 +833,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
// Attach scope only to non-text nodes.
|
||||
for(var i = 0, ii = $linkNode.length; i<ii; i++) {
|
||||
var node = $linkNode[i];
|
||||
if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
|
||||
var node = $linkNode[i],
|
||||
nodeType = node.nodeType;
|
||||
if (nodeType === 1 /* element */ || nodeType === 9 /* document */) {
|
||||
$linkNode.eq(i).data('$scope', scope);
|
||||
}
|
||||
}
|
||||
safeAddClass($linkNode, 'ng-scope');
|
||||
|
||||
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
|
||||
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
|
||||
return $linkNode;
|
||||
@@ -865,15 +867,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
|
||||
* the rootElement must be set the jqLite collection of the compile root. This is
|
||||
* needed so that the jqLite collection items can be replaced with widgets.
|
||||
* @param {number=} max directive priority
|
||||
* @param {number=} maxPriority Max directive priority.
|
||||
* @returns {?function} A composite linking function of all of the matched directives or null.
|
||||
*/
|
||||
function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
|
||||
previousCompileContext) {
|
||||
var linkFns = [],
|
||||
nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
|
||||
attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound;
|
||||
|
||||
for(var i = 0; i < nodeList.length; i++) {
|
||||
for (var i = 0; i < nodeList.length; i++) {
|
||||
attrs = new Attributes();
|
||||
|
||||
// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
|
||||
@@ -885,16 +887,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
null, [], [], previousCompileContext)
|
||||
: null;
|
||||
|
||||
if (nodeLinkFn && nodeLinkFn.scope) {
|
||||
safeAddClass(jqLite(nodeList[i]), 'ng-scope');
|
||||
}
|
||||
|
||||
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
|
||||
!nodeList[i].childNodes ||
|
||||
!nodeList[i].childNodes.length)
|
||||
!(childNodes = nodeList[i].childNodes) ||
|
||||
!childNodes.length)
|
||||
? null
|
||||
: compileNodes(nodeList[i].childNodes,
|
||||
: compileNodes(childNodes,
|
||||
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
|
||||
|
||||
linkFns.push(nodeLinkFn);
|
||||
linkFns.push(childLinkFn);
|
||||
linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
|
||||
linkFns.push(nodeLinkFn, childLinkFn);
|
||||
linkFnFound = linkFnFound || nodeLinkFn || childLinkFn;
|
||||
//use the previous context only for the first element in the virtual group
|
||||
previousCompileContext = null;
|
||||
}
|
||||
@@ -906,9 +911,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
var nodeLinkFn, childLinkFn, node, $node, childScope, childTranscludeFn, i, ii, n;
|
||||
|
||||
// copy nodeList so that linking doesn't break due to live list updates.
|
||||
var stableNodeList = [];
|
||||
for (i = 0, ii = nodeList.length; i < ii; i++) {
|
||||
stableNodeList.push(nodeList[i]);
|
||||
var nodeListLength = nodeList.length,
|
||||
stableNodeList = new Array(nodeListLength);
|
||||
for (i = 0; i < nodeListLength; i++) {
|
||||
stableNodeList[i] = nodeList[i];
|
||||
}
|
||||
|
||||
for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
|
||||
@@ -921,7 +927,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (nodeLinkFn.scope) {
|
||||
childScope = scope.$new();
|
||||
$node.data('$scope', childScope);
|
||||
safeAddClass($node, 'ng-scope');
|
||||
} else {
|
||||
childScope = scope;
|
||||
}
|
||||
@@ -1004,9 +1009,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
nName = directiveNormalize(name.toLowerCase());
|
||||
attrsMap[nName] = name;
|
||||
attrs[nName] = value = trim((msie && name == 'href')
|
||||
? decodeURIComponent(node.getAttribute(name, 2))
|
||||
: attr.value);
|
||||
attrs[nName] = value = trim(attr.value);
|
||||
if (getBooleanAttrName(node, nName)) {
|
||||
attrs[nName] = true; // presence means true
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngHref
|
||||
* @restrict A
|
||||
* @priority 99
|
||||
*
|
||||
* @description
|
||||
* Using Angular markup like `{{hash}}` in an href attribute will
|
||||
@@ -87,6 +88,7 @@
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngSrc
|
||||
* @restrict A
|
||||
* @priority 99
|
||||
*
|
||||
* @description
|
||||
* Using Angular markup like `{{hash}}` in a `src` attribute doesn't
|
||||
@@ -112,6 +114,7 @@
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngSrcset
|
||||
* @restrict A
|
||||
* @priority 99
|
||||
*
|
||||
* @description
|
||||
* Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
|
||||
@@ -137,6 +140,7 @@
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngDisabled
|
||||
* @restrict A
|
||||
* @priority 100
|
||||
*
|
||||
* @description
|
||||
*
|
||||
@@ -180,6 +184,7 @@
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngChecked
|
||||
* @restrict A
|
||||
* @priority 100
|
||||
*
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
@@ -214,6 +219,7 @@
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngReadonly
|
||||
* @restrict A
|
||||
* @priority 100
|
||||
*
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
@@ -223,7 +229,6 @@
|
||||
* The `ngReadonly` directive solves this problem for the `readonly` attribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
@@ -249,6 +254,7 @@
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngSelected
|
||||
* @restrict A
|
||||
* @priority 100
|
||||
*
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
@@ -258,6 +264,7 @@
|
||||
* The `ngSelected` directive solves this problem for the `selected` atttribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
@@ -285,6 +292,7 @@
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngOpen
|
||||
* @restrict A
|
||||
* @priority 100
|
||||
*
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
@@ -294,8 +302,6 @@
|
||||
* The `ngOpen` directive solves this problem for the `open` attribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
|
||||
@@ -395,15 +395,17 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
// In composition mode, users are still inputing intermediate text buffer,
|
||||
// hold the listener until composition is done.
|
||||
// More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
|
||||
var composing = false;
|
||||
if (!$sniffer.android) {
|
||||
var composing = false;
|
||||
|
||||
element.on('compositionstart', function() {
|
||||
composing = true;
|
||||
});
|
||||
element.on('compositionstart', function(data) {
|
||||
composing = true;
|
||||
});
|
||||
|
||||
element.on('compositionend', function() {
|
||||
composing = false;
|
||||
});
|
||||
element.on('compositionend', function() {
|
||||
composing = false;
|
||||
});
|
||||
}
|
||||
|
||||
var listener = function() {
|
||||
if (composing) return;
|
||||
@@ -417,9 +419,13 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
}
|
||||
|
||||
if (ctrl.$viewValue !== value) {
|
||||
scope.$apply(function() {
|
||||
if (scope.$$phase) {
|
||||
ctrl.$setViewValue(value);
|
||||
});
|
||||
} else {
|
||||
scope.$apply(function() {
|
||||
ctrl.$setViewValue(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
*
|
||||
* Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
|
||||
* cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
|
||||
* class `ngCloak` in addition to the `ngCloak` directive as shown in the example below.
|
||||
* class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below.
|
||||
*
|
||||
* @element ANY
|
||||
*
|
||||
|
||||
@@ -69,7 +69,14 @@ forEach(
|
||||
* a dblclick. (The Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<button ng-dblclick="count = count + 1" ng-init="count=0">
|
||||
Increment (on double click)
|
||||
</button>
|
||||
count: {{count}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
||||
@@ -85,7 +92,14 @@ forEach(
|
||||
* mousedown. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<button ng-mousedown="count = count + 1" ng-init="count=0">
|
||||
Increment (on mouse down)
|
||||
</button>
|
||||
count: {{count}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
||||
@@ -101,7 +115,14 @@ forEach(
|
||||
* mouseup. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<button ng-mouseup="count = count + 1" ng-init="count=0">
|
||||
Increment (on mouse up)
|
||||
</button>
|
||||
count: {{count}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -116,7 +137,14 @@ forEach(
|
||||
* mouseover. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<button ng-mouseover="count = count + 1" ng-init="count=0">
|
||||
Increment (when mouse is over)
|
||||
</button>
|
||||
count: {{count}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
||||
@@ -132,7 +160,14 @@ forEach(
|
||||
* mouseenter. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<button ng-mouseenter="count = count + 1" ng-init="count=0">
|
||||
Increment (when mouse enters)
|
||||
</button>
|
||||
count: {{count}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
||||
@@ -148,7 +183,14 @@ forEach(
|
||||
* mouseleave. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<button ng-mouseleave="count = count + 1" ng-init="count=0">
|
||||
Increment (when mouse leaves)
|
||||
</button>
|
||||
count: {{count}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
||||
@@ -164,7 +206,14 @@ forEach(
|
||||
* mousemove. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<button ng-mousemove="count = count + 1" ng-init="count=0">
|
||||
Increment (when mouse moves)
|
||||
</button>
|
||||
count: {{count}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
||||
@@ -180,7 +229,12 @@ forEach(
|
||||
* keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<input ng-keydown="count = count + 1" ng-init="count=0">
|
||||
key down count: {{count}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
||||
@@ -196,7 +250,12 @@ forEach(
|
||||
* keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<input ng-keyup="count = count + 1" ng-init="count=0">
|
||||
key up count: {{count}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
||||
@@ -212,7 +271,12 @@ forEach(
|
||||
* keypress. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<input ng-keypress="count = count + 1" ng-init="count=0">
|
||||
key press count: {{count}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
||||
@@ -311,7 +375,12 @@ forEach(
|
||||
* copy. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
|
||||
copied: {{copied}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -326,7 +395,12 @@ forEach(
|
||||
* cut. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
|
||||
cut: {{cut}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -341,5 +415,10 @@ forEach(
|
||||
* paste. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
|
||||
pasted: {{paste}}
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
@@ -59,9 +59,6 @@
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
/*
|
||||
The transition styles can also be placed on the CSS base class above
|
||||
*/
|
||||
.animate-if.ng-enter, .animate-if.ng-leave {
|
||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* current scope.
|
||||
*
|
||||
* <div class="alert alert-error">
|
||||
* The only appropriate use of `ngInit` for aliasing special properties of
|
||||
* The only appropriate use of `ngInit` is for aliasing special properties of
|
||||
* {@link api/ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
|
||||
* should use {@link guide/controller controllers} rather than `ngInit`
|
||||
* to initialize values on a scope.
|
||||
|
||||
@@ -203,7 +203,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
$$tlb: true,
|
||||
link: function($scope, $element, $attr, ctrl, $transclude){
|
||||
var expression = $attr.ngRepeat;
|
||||
var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
|
||||
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
|
||||
trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
|
||||
lhs, rhs, valueIdentifier, keyIdentifier,
|
||||
hashFnLocals = {$id: hashKey};
|
||||
@@ -215,7 +215,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
|
||||
lhs = match[1];
|
||||
rhs = match[2];
|
||||
trackByExp = match[4];
|
||||
trackByExp = match[3];
|
||||
|
||||
if (trackByExp) {
|
||||
trackByExpGetter = $parse(trackByExp);
|
||||
|
||||
@@ -221,18 +221,10 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
|
||||
|
||||
// required validator
|
||||
if (multiple && (attr.required || attr.ngRequired)) {
|
||||
var requiredValidator = function(value) {
|
||||
ngModelCtrl.$setValidity('required', !attr.required || (value && value.length));
|
||||
return value;
|
||||
if (multiple) {
|
||||
ngModelCtrl.$isEmpty = function(value) {
|
||||
return !value || value.length === 0;
|
||||
};
|
||||
|
||||
ngModelCtrl.$parsers.push(requiredValidator);
|
||||
ngModelCtrl.$formatters.unshift(requiredValidator);
|
||||
|
||||
attr.$observe('required', function() {
|
||||
requiredValidator(ngModelCtrl.$viewValue);
|
||||
});
|
||||
}
|
||||
|
||||
if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);
|
||||
|
||||
@@ -25,21 +25,21 @@
|
||||
* property of the object. That's equivalent to the simple substring match with a `string`
|
||||
* as described above.
|
||||
*
|
||||
* - `function`: A predicate function can be used to write arbitrary filters. The function is
|
||||
* - `function(value)`: A predicate function can be used to write arbitrary filters. The function is
|
||||
* called for each element of `array`. The final result is an array of those elements that
|
||||
* the predicate returned true for.
|
||||
*
|
||||
* @param {function(expected, actual)|true|undefined} comparator Comparator which is used in
|
||||
* @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
|
||||
* determining if the expected value (from the filter expression) and actual value (from
|
||||
* the object in the array) should be considered a match.
|
||||
*
|
||||
* Can be one of:
|
||||
*
|
||||
* - `function(expected, actual)`:
|
||||
* - `function(actual, expected)`:
|
||||
* The function will be given the object value and the predicate value to compare and
|
||||
* should return true if the item should be included in filtered result.
|
||||
*
|
||||
* - `true`: A shorthand for `function(expected, actual) { return angular.equals(expected, actual)}`.
|
||||
* - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`.
|
||||
* this is essentially strict comparison of expected and actual.
|
||||
*
|
||||
* - `false|undefined`: A short hand for a function which will look for a substring match in case
|
||||
|
||||
+5
-4
@@ -222,7 +222,7 @@ function $HttpProvider() {
|
||||
* will result in the success callback being called. Note that if the response is a redirect,
|
||||
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
|
||||
* called for such responses.
|
||||
*
|
||||
*
|
||||
* # Calling $http from outside AngularJS
|
||||
* The `$http` service will not actually send the request until the next `$digest()` is
|
||||
* executed. Normally this is not an issue, since almost all the time your call to `$http` will
|
||||
@@ -409,19 +409,20 @@ function $HttpProvider() {
|
||||
* return responseOrNewPromise
|
||||
* }
|
||||
* return $q.reject(rejection);
|
||||
* };
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
* });
|
||||
*
|
||||
* $httpProvider.interceptors.push('myHttpInterceptor');
|
||||
*
|
||||
*
|
||||
* // register the interceptor via an anonymous factory
|
||||
* // alternatively, register the interceptor via an anonymous factory
|
||||
* $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
|
||||
* return {
|
||||
* 'request': function(config) {
|
||||
* // same as above
|
||||
* },
|
||||
*
|
||||
* 'response': function(response) {
|
||||
* // same as above
|
||||
* }
|
||||
|
||||
+19
-10
@@ -1,12 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
var XHR = window.XMLHttpRequest || function() {
|
||||
function createXhr(method) {
|
||||
// IE8 doesn't support PATCH method, but the ActiveX object does
|
||||
/* global ActiveXObject */
|
||||
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
|
||||
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
|
||||
try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {}
|
||||
throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest.");
|
||||
};
|
||||
return (msie <= 8 && lowercase(method) === 'patch')
|
||||
? new ActiveXObject('Microsoft.XMLHTTP')
|
||||
: new window.XMLHttpRequest();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -28,11 +28,11 @@ var XHR = window.XMLHttpRequest || function() {
|
||||
*/
|
||||
function $HttpBackendProvider() {
|
||||
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
|
||||
return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks, $document[0]);
|
||||
return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
|
||||
}];
|
||||
}
|
||||
|
||||
function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument) {
|
||||
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
|
||||
var ABORTED = -1;
|
||||
|
||||
// TODO(vojta): fix the signature
|
||||
@@ -57,7 +57,9 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument)
|
||||
delete callbacks[callbackId];
|
||||
});
|
||||
} else {
|
||||
var xhr = new XHR();
|
||||
|
||||
var xhr = createXhr(method);
|
||||
|
||||
xhr.open(method, url, true);
|
||||
forEach(headers, function(value, key) {
|
||||
if (isDefined(value)) {
|
||||
@@ -69,7 +71,14 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument)
|
||||
// response is in the cache. the promise api will ensure that to the app code the api is
|
||||
// always async
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
// onreadystatechange might by called multiple times with readyState === 4 on mobile webkit caused by
|
||||
// xhrs that are resolved while the app is in the background (see #5426).
|
||||
// since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before
|
||||
// continuing
|
||||
//
|
||||
// we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and
|
||||
// Safari respectively.
|
||||
if (xhr && xhr.readyState == 4) {
|
||||
var responseHeaders = null,
|
||||
response = null;
|
||||
|
||||
|
||||
@@ -24,6 +24,14 @@ function $IntervalProvider() {
|
||||
* In tests you can use {@link ngMock.$interval#methods_flush `$interval.flush(millis)`} to
|
||||
* move forward by `millis` milliseconds and trigger any functions scheduled to run in that
|
||||
* time.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note**: Intervals created by this service must be explicitly destroyed when you are finished
|
||||
* with them. In particular they are not automatically destroyed when a controller's scope or a
|
||||
* directive's element are destroyed.
|
||||
* You should take this into consideration and make sure to always cancel the interval at the
|
||||
* appropriate moment. See the example below for more details on how and when to do this.
|
||||
* </div>
|
||||
*
|
||||
* @param {function()} fn A function that should be called repeatedly.
|
||||
* @param {number} delay Number of milliseconds between each function call.
|
||||
@@ -32,6 +40,95 @@ function $IntervalProvider() {
|
||||
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
|
||||
* will invoke `fn` within the {@link ng.$rootScope.Scope#methods_$apply $apply} block.
|
||||
* @returns {promise} A promise which will be notified on each iteration.
|
||||
*
|
||||
* @example
|
||||
<doc:example module="time">
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl2($scope,$interval) {
|
||||
$scope.format = 'M/d/yy h:mm:ss a';
|
||||
$scope.blood_1 = 100;
|
||||
$scope.blood_2 = 120;
|
||||
|
||||
var stop;
|
||||
$scope.fight = function() {
|
||||
// Don't start a new fight if we are already fighting
|
||||
if ( angular.isDefined(stop) ) return;
|
||||
|
||||
stop = $interval(function() {
|
||||
if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
|
||||
$scope.blood_1 = $scope.blood_1 - 3;
|
||||
$scope.blood_2 = $scope.blood_2 - 4;
|
||||
} else {
|
||||
$scope.stopFight();
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.stopFight = function() {
|
||||
if (angular.isDefined(stop)) {
|
||||
$interval.cancel(stop);
|
||||
stop = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.resetFight = function() {
|
||||
$scope.blood_1 = 100;
|
||||
$scope.blood_2 = 120;
|
||||
}
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
// Make sure that the interval is destroyed too
|
||||
$scope.stopFight();
|
||||
});
|
||||
}
|
||||
|
||||
angular.module('time', [])
|
||||
// Register the 'myCurrentTime' directive factory method.
|
||||
// We inject $interval and dateFilter service since the factory method is DI.
|
||||
.directive('myCurrentTime', function($interval, dateFilter) {
|
||||
// return the directive link function. (compile function not needed)
|
||||
return function(scope, element, attrs) {
|
||||
var format, // date format
|
||||
stopTime; // so that we can cancel the time updates
|
||||
|
||||
// used to update the UI
|
||||
function updateTime() {
|
||||
element.text(dateFilter(new Date(), format));
|
||||
}
|
||||
|
||||
// watch the expression, and update the UI on change.
|
||||
scope.$watch(attrs.myCurrentTime, function(value) {
|
||||
format = value;
|
||||
updateTime();
|
||||
});
|
||||
|
||||
stopTime = $interval(updateTime, 1000);
|
||||
|
||||
// listen on DOM destroy (removal) event, and cancel the next UI update
|
||||
// to prevent updating time ofter the DOM element was removed.
|
||||
element.bind('$destroy', function() {
|
||||
$interval.cancel(stopTime);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div ng-controller="Ctrl2">
|
||||
Date format: <input ng-model="format"> <hr/>
|
||||
Current time is: <span my-current-time="format"></span>
|
||||
<hr/>
|
||||
Blood 1 : <font color='red'>{{blood_1}}</font>
|
||||
Blood 2 : <font color='red'>{{blood_2}}</font>
|
||||
<button type="button" data-ng-click="fight()">Fight</button>
|
||||
<button type="button" data-ng-click="stopFight()">StopFight</button>
|
||||
<button type="button" data-ng-click="resetFight()">resetFight</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
function interval(fn, delay, count, invokeApply) {
|
||||
var setInterval = $window.setInterval,
|
||||
|
||||
+14
-6
@@ -629,6 +629,13 @@ function $LocationProvider(){
|
||||
}
|
||||
|
||||
var absHref = elm.prop('href');
|
||||
|
||||
if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
|
||||
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
|
||||
// an animation.
|
||||
absHref = urlResolve(absHref.animVal).href;
|
||||
}
|
||||
|
||||
var rewrittenUrl = $location.$$rewrite(absHref);
|
||||
|
||||
if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
|
||||
@@ -652,16 +659,17 @@ function $LocationProvider(){
|
||||
// update $location when $browser url changes
|
||||
$browser.onUrlChange(function(newUrl) {
|
||||
if ($location.absUrl() != newUrl) {
|
||||
if ($rootScope.$broadcast('$locationChangeStart', newUrl,
|
||||
$location.absUrl()).defaultPrevented) {
|
||||
$browser.url($location.absUrl());
|
||||
return;
|
||||
}
|
||||
$rootScope.$evalAsync(function() {
|
||||
var oldUrl = $location.absUrl();
|
||||
|
||||
$location.$$parse(newUrl);
|
||||
afterLocationChange(oldUrl);
|
||||
if ($rootScope.$broadcast('$locationChangeStart', newUrl,
|
||||
oldUrl).defaultPrevented) {
|
||||
$location.$$parse(oldUrl);
|
||||
$browser.url(oldUrl);
|
||||
} else {
|
||||
afterLocationChange(oldUrl);
|
||||
}
|
||||
});
|
||||
if (!$rootScope.$$phase) $rootScope.$digest();
|
||||
}
|
||||
|
||||
+9
-2
@@ -139,9 +139,16 @@ function $LogProvider(){
|
||||
|
||||
function consoleLog(type) {
|
||||
var console = $window.console || {},
|
||||
logFn = console[type] || console.log || noop;
|
||||
logFn = console[type] || console.log || noop,
|
||||
hasApply = false;
|
||||
|
||||
if (logFn.apply) {
|
||||
// Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
|
||||
// The reason behind this is that console.log has type "object" in IE8...
|
||||
try {
|
||||
hasApply = !! logFn.apply;
|
||||
} catch (e) {}
|
||||
|
||||
if (hasApply) {
|
||||
return function() {
|
||||
var args = [];
|
||||
forEach(arguments, function(arg) {
|
||||
|
||||
+42
-17
@@ -891,19 +891,19 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
? function cspSafeGetter(scope, locals) {
|
||||
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
|
||||
|
||||
if (pathVal === null || pathVal === undefined) return pathVal;
|
||||
if (pathVal == null) return pathVal;
|
||||
pathVal = pathVal[key0];
|
||||
|
||||
if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
|
||||
if (pathVal == null) return key1 ? undefined : pathVal;
|
||||
pathVal = pathVal[key1];
|
||||
|
||||
if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
|
||||
if (pathVal == null) return key2 ? undefined : pathVal;
|
||||
pathVal = pathVal[key2];
|
||||
|
||||
if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
|
||||
if (pathVal == null) return key3 ? undefined : pathVal;
|
||||
pathVal = pathVal[key3];
|
||||
|
||||
if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
|
||||
if (pathVal == null) return key4 ? undefined : pathVal;
|
||||
pathVal = pathVal[key4];
|
||||
|
||||
return pathVal;
|
||||
@@ -912,7 +912,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope,
|
||||
promise;
|
||||
|
||||
if (pathVal === null || pathVal === undefined) return pathVal;
|
||||
if (pathVal == null) return pathVal;
|
||||
|
||||
pathVal = pathVal[key0];
|
||||
if (pathVal && pathVal.then) {
|
||||
@@ -924,7 +924,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
}
|
||||
pathVal = pathVal.$$v;
|
||||
}
|
||||
if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
|
||||
if (pathVal == null) return key1 ? undefined : pathVal;
|
||||
|
||||
pathVal = pathVal[key1];
|
||||
if (pathVal && pathVal.then) {
|
||||
@@ -936,7 +936,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
}
|
||||
pathVal = pathVal.$$v;
|
||||
}
|
||||
if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
|
||||
if (pathVal == null) return key2 ? undefined : pathVal;
|
||||
|
||||
pathVal = pathVal[key2];
|
||||
if (pathVal && pathVal.then) {
|
||||
@@ -948,7 +948,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
}
|
||||
pathVal = pathVal.$$v;
|
||||
}
|
||||
if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
|
||||
if (pathVal == null) return key3 ? undefined : pathVal;
|
||||
|
||||
pathVal = pathVal[key3];
|
||||
if (pathVal && pathVal.then) {
|
||||
@@ -960,7 +960,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
}
|
||||
pathVal = pathVal.$$v;
|
||||
}
|
||||
if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
|
||||
if (pathVal == null) return key4 ? undefined : pathVal;
|
||||
|
||||
pathVal = pathVal[key4];
|
||||
if (pathVal && pathVal.then) {
|
||||
@@ -976,6 +976,26 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
};
|
||||
}
|
||||
|
||||
function simpleGetterFn1(key0, fullExp) {
|
||||
ensureSafeMemberName(key0, fullExp);
|
||||
|
||||
return function simpleGetterFn1(scope, locals) {
|
||||
if (scope == null) return undefined;
|
||||
return ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
|
||||
};
|
||||
}
|
||||
|
||||
function simpleGetterFn2(key0, key1, fullExp) {
|
||||
ensureSafeMemberName(key0, fullExp);
|
||||
ensureSafeMemberName(key1, fullExp);
|
||||
|
||||
return function simpleGetterFn2(scope, locals) {
|
||||
if (scope == null) return undefined;
|
||||
scope = ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
|
||||
return scope == null ? undefined : scope[key1];
|
||||
};
|
||||
}
|
||||
|
||||
function getterFn(path, options, fullExp) {
|
||||
// Check whether the cache has this getter already.
|
||||
// We can use hasOwnProperty directly on the cache because we ensure,
|
||||
@@ -988,7 +1008,13 @@ function getterFn(path, options, fullExp) {
|
||||
pathKeysLength = pathKeys.length,
|
||||
fn;
|
||||
|
||||
if (options.csp) {
|
||||
// When we have only 1 or 2 tokens, use optimized special case closures.
|
||||
// http://jsperf.com/angularjs-parse-getter/6
|
||||
if (!options.unwrapPromises && pathKeysLength === 1) {
|
||||
fn = simpleGetterFn1(pathKeys[0], fullExp);
|
||||
} else if (!options.unwrapPromises && pathKeysLength === 2) {
|
||||
fn = simpleGetterFn2(pathKeys[0], pathKeys[1], fullExp);
|
||||
} else if (options.csp) {
|
||||
if (pathKeysLength < 6) {
|
||||
fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp,
|
||||
options);
|
||||
@@ -1006,11 +1032,10 @@ function getterFn(path, options, fullExp) {
|
||||
};
|
||||
}
|
||||
} else {
|
||||
var code = 'var l, fn, p;\n';
|
||||
var code = 'var p;\n';
|
||||
forEach(pathKeys, function(key, index) {
|
||||
ensureSafeMemberName(key, fullExp);
|
||||
code += 'if(s === null || s === undefined) return s;\n' +
|
||||
'l=s;\n' +
|
||||
code += 'if(s == null) return undefined;\n' +
|
||||
's='+ (index
|
||||
// we simply dereference 's' on any .dot notation
|
||||
? 's'
|
||||
@@ -1033,10 +1058,10 @@ function getterFn(path, options, fullExp) {
|
||||
/* jshint -W054 */
|
||||
var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning
|
||||
/* jshint +W054 */
|
||||
evaledFnGetter.toString = function() { return code; };
|
||||
fn = function(scope, locals) {
|
||||
evaledFnGetter.toString = valueFn(code);
|
||||
fn = options.unwrapPromises ? function(scope, locals) {
|
||||
return evaledFnGetter(scope, locals, promiseWarning);
|
||||
};
|
||||
} : evaledFnGetter;
|
||||
}
|
||||
|
||||
// Only cache the value if it's not going to mess up the cache object
|
||||
|
||||
+30
-4
@@ -133,6 +133,7 @@ function $RootScopeProvider(){
|
||||
this.$$asyncQueue = [];
|
||||
this.$$postDigestQueue = [];
|
||||
this.$$listeners = {};
|
||||
this.$$listenerCount = {};
|
||||
this.$$isolateBindings = {};
|
||||
}
|
||||
|
||||
@@ -192,6 +193,7 @@ function $RootScopeProvider(){
|
||||
}
|
||||
child['this'] = child;
|
||||
child.$$listeners = {};
|
||||
child.$$listenerCount = {};
|
||||
child.$parent = this;
|
||||
child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
|
||||
child.$$prevSibling = this.$$childTail;
|
||||
@@ -351,6 +353,7 @@ function $RootScopeProvider(){
|
||||
|
||||
return function() {
|
||||
arrayRemove(array, watcher);
|
||||
lastDirtyWatch = null;
|
||||
};
|
||||
},
|
||||
|
||||
@@ -696,6 +699,8 @@ function $RootScopeProvider(){
|
||||
this.$$destroyed = true;
|
||||
if (this === $rootScope) return;
|
||||
|
||||
forEach(this.$$listenerCount, bind(null, decrementListenerCount, this));
|
||||
|
||||
if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
|
||||
if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
|
||||
if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
|
||||
@@ -885,8 +890,18 @@ function $RootScopeProvider(){
|
||||
}
|
||||
namedListeners.push(listener);
|
||||
|
||||
var current = this;
|
||||
do {
|
||||
if (!current.$$listenerCount[name]) {
|
||||
current.$$listenerCount[name] = 0;
|
||||
}
|
||||
current.$$listenerCount[name]++;
|
||||
} while ((current = current.$parent));
|
||||
|
||||
var self = this;
|
||||
return function() {
|
||||
namedListeners[indexOf(namedListeners, listener)] = null;
|
||||
decrementListenerCount(self, 1, name);
|
||||
};
|
||||
},
|
||||
|
||||
@@ -998,8 +1013,7 @@ function $RootScopeProvider(){
|
||||
listeners, i, length;
|
||||
|
||||
//down while you can, then up and next sibling or up and next sibling until back at root
|
||||
do {
|
||||
current = next;
|
||||
while ((current = next)) {
|
||||
event.currentScope = current;
|
||||
listeners = current.$$listeners[name] || [];
|
||||
for (i=0, length = listeners.length; i<length; i++) {
|
||||
@@ -1021,12 +1035,14 @@ function $RootScopeProvider(){
|
||||
// Insanity Warning: scope depth-first traversal
|
||||
// yes, this code is a bit crazy, but it works and we have tests to prove it!
|
||||
// this piece should be kept in sync with the traversal in $digest
|
||||
if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
|
||||
// (though it differs due to having the extra check for $$listenerCount)
|
||||
if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
|
||||
(current !== target && current.$$nextSibling)))) {
|
||||
while(current !== target && !(next = current.$$nextSibling)) {
|
||||
current = current.$parent;
|
||||
}
|
||||
}
|
||||
} while ((current = next));
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
@@ -1055,6 +1071,16 @@ function $RootScopeProvider(){
|
||||
return fn;
|
||||
}
|
||||
|
||||
function decrementListenerCount(current, count, name) {
|
||||
do {
|
||||
current.$$listenerCount[name] -= count;
|
||||
|
||||
if (current.$$listenerCount[name] === 0) {
|
||||
delete current.$$listenerCount[name];
|
||||
}
|
||||
} while ((current = current.$parent));
|
||||
}
|
||||
|
||||
/**
|
||||
* function used as an initial value for watchers.
|
||||
* because it's unique we can easily tell it apart from other values
|
||||
|
||||
+2
-1
@@ -59,7 +59,7 @@ function $SnifferProvider() {
|
||||
// http://code.google.com/p/android/issues/detail?id=17471
|
||||
// https://github.com/angular/angular.js/issues/904
|
||||
|
||||
// older webit browser (533.9) on Boxee box has exactly the same problem as Android has
|
||||
// older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
|
||||
// so let's not use the history API also
|
||||
// We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
|
||||
// jshint -W018
|
||||
@@ -85,6 +85,7 @@ function $SnifferProvider() {
|
||||
vendorPrefix: vendorPrefix,
|
||||
transitions : transitions,
|
||||
animations : animations,
|
||||
android: android,
|
||||
msie : msie,
|
||||
msieDocumentMode: documentMode
|
||||
};
|
||||
|
||||
@@ -32,93 +32,6 @@ function $TimeoutProvider() {
|
||||
* @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
|
||||
* promise will be resolved with is the return value of the `fn` function.
|
||||
*
|
||||
* @example
|
||||
<doc:example module="time">
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl2($scope,$timeout) {
|
||||
$scope.format = 'M/d/yy h:mm:ss a';
|
||||
$scope.blood_1 = 100;
|
||||
$scope.blood_2 = 120;
|
||||
|
||||
var stop;
|
||||
$scope.fight = function() {
|
||||
stop = $timeout(function() {
|
||||
if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
|
||||
$scope.blood_1 = $scope.blood_1 - 3;
|
||||
$scope.blood_2 = $scope.blood_2 - 4;
|
||||
$scope.fight();
|
||||
} else {
|
||||
$timeout.cancel(stop);
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.stopFight = function() {
|
||||
$timeout.cancel(stop);
|
||||
};
|
||||
|
||||
$scope.resetFight = function() {
|
||||
$scope.blood_1 = 100;
|
||||
$scope.blood_2 = 120;
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('time', [])
|
||||
// Register the 'myCurrentTime' directive factory method.
|
||||
// We inject $timeout and dateFilter service since the factory method is DI.
|
||||
.directive('myCurrentTime', function($timeout, dateFilter) {
|
||||
// return the directive link function. (compile function not needed)
|
||||
return function(scope, element, attrs) {
|
||||
var format, // date format
|
||||
timeoutId; // timeoutId, so that we can cancel the time updates
|
||||
|
||||
// used to update the UI
|
||||
function updateTime() {
|
||||
element.text(dateFilter(new Date(), format));
|
||||
}
|
||||
|
||||
// watch the expression, and update the UI on change.
|
||||
scope.$watch(attrs.myCurrentTime, function(value) {
|
||||
format = value;
|
||||
updateTime();
|
||||
});
|
||||
|
||||
// schedule update in one second
|
||||
function updateLater() {
|
||||
// save the timeoutId for canceling
|
||||
timeoutId = $timeout(function() {
|
||||
updateTime(); // update DOM
|
||||
updateLater(); // schedule another update
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// listen on DOM destroy (removal) event, and cancel the next UI update
|
||||
// to prevent updating time ofter the DOM element was removed.
|
||||
element.bind('$destroy', function() {
|
||||
$timeout.cancel(timeoutId);
|
||||
});
|
||||
|
||||
updateLater(); // kick off the UI update process.
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div ng-controller="Ctrl2">
|
||||
Date format: <input ng-model="format"> <hr/>
|
||||
Current time is: <span my-current-time="format"></span>
|
||||
<hr/>
|
||||
Blood 1 : <font color='red'>{{blood_1}}</font>
|
||||
Blood 2 : <font color='red'>{{blood_2}}</font>
|
||||
<button type="button" data-ng-click="fight()">Fight</button>
|
||||
<button type="button" data-ng-click="stopFight()">StopFight</button>
|
||||
<button type="button" data-ng-click="resetFight()">resetFight</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
function timeout(fn, delay, invokeApply) {
|
||||
var deferred = $q.defer(),
|
||||
|
||||
+97
-40
@@ -288,6 +288,13 @@ angular.module('ngAnimate', ['ng'])
|
||||
});
|
||||
});
|
||||
|
||||
var classNameFilter = $animateProvider.classNameFilter();
|
||||
var isAnimatableClassName = !classNameFilter
|
||||
? function() { return true; }
|
||||
: function(className) {
|
||||
return classNameFilter.test(className);
|
||||
};
|
||||
|
||||
function lookup(name) {
|
||||
if (name) {
|
||||
var matches = [],
|
||||
@@ -569,17 +576,20 @@ angular.module('ngAnimate', ['ng'])
|
||||
and the onComplete callback will be fired once the animation is fully complete.
|
||||
*/
|
||||
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
|
||||
var node = extractElementNode(element);
|
||||
var currentClassName, classes, node = extractElementNode(element);
|
||||
if(node) {
|
||||
currentClassName = node.className;
|
||||
classes = currentClassName + ' ' + className;
|
||||
}
|
||||
|
||||
//transcluded directives may sometimes fire an animation using only comment nodes
|
||||
//best to catch this early on to prevent any animation operations from occurring
|
||||
if(!node) {
|
||||
if(!node || !isAnimatableClassName(classes)) {
|
||||
fireDOMOperation();
|
||||
closeAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
var currentClassName = node.className;
|
||||
var classes = currentClassName + ' ' + className;
|
||||
var animationLookup = (' ' + classes).replace(/\s+/g,'.');
|
||||
if (!parentElement) {
|
||||
parentElement = afterElement ? afterElement.parent() : element.parent();
|
||||
@@ -600,9 +610,14 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
|
||||
var animations = [];
|
||||
|
||||
//only add animations if the currently running animation is not structural
|
||||
//or if there is no animation running at all
|
||||
if(!ngAnimateState.running || !(isClassBased && ngAnimateState.structural)) {
|
||||
var allowAnimations = isClassBased ?
|
||||
!ngAnimateState.disabled && (!ngAnimateState.running || !ngAnimateState.structural) :
|
||||
true;
|
||||
|
||||
if(allowAnimations) {
|
||||
forEach(matches, function(animation) {
|
||||
//add the animation to the queue to if it is allowed to be cancelled
|
||||
if(!animation.allowCancel || animation.allowCancel(element, animationEvent, className)) {
|
||||
@@ -881,27 +896,73 @@ angular.module('ngAnimate', ['ng'])
|
||||
var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
|
||||
var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey';
|
||||
var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
|
||||
var NG_ANIMATE_FALLBACK_CLASS_NAME = 'ng-animate-start';
|
||||
var NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME = 'ng-animate-active';
|
||||
var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
|
||||
var CLOSING_TIME_BUFFER = 1.5;
|
||||
var ONE_SECOND = 1000;
|
||||
|
||||
var animationCounter = 0;
|
||||
var lookupCache = {};
|
||||
var parentCounter = 0;
|
||||
|
||||
var animationReflowQueue = [], animationTimer, timeOut = false;
|
||||
function afterReflow(callback) {
|
||||
animationReflowQueue.push(callback);
|
||||
var animationReflowQueue = [];
|
||||
var animationElementQueue = [];
|
||||
var animationTimer;
|
||||
var closingAnimationTime = 0;
|
||||
var timeOut = false;
|
||||
function afterReflow(element, callback) {
|
||||
$timeout.cancel(animationTimer);
|
||||
|
||||
animationReflowQueue.push(callback);
|
||||
|
||||
var node = extractElementNode(element);
|
||||
element = angular.element(node);
|
||||
animationElementQueue.push(element);
|
||||
|
||||
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
closingAnimationTime = Math.max(closingAnimationTime,
|
||||
(elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER * ONE_SECOND);
|
||||
|
||||
//by placing a counter we can avoid an accidental
|
||||
//race condition which may close an animation when
|
||||
//a follow-up animation is midway in its animation
|
||||
elementData.animationCount = animationCounter;
|
||||
|
||||
animationTimer = $timeout(function() {
|
||||
forEach(animationReflowQueue, function(fn) {
|
||||
fn();
|
||||
});
|
||||
|
||||
//copy the list of elements so that successive
|
||||
//animations won't conflict if they're added before
|
||||
//the closing animation timeout has run
|
||||
var elementQueueSnapshot = [];
|
||||
var animationCounterSnapshot = animationCounter;
|
||||
forEach(animationElementQueue, function(elm) {
|
||||
elementQueueSnapshot.push(elm);
|
||||
});
|
||||
|
||||
$timeout(function() {
|
||||
closeAllAnimations(elementQueueSnapshot, animationCounterSnapshot);
|
||||
elementQueueSnapshot = null;
|
||||
}, closingAnimationTime, false);
|
||||
|
||||
animationReflowQueue = [];
|
||||
animationElementQueue = [];
|
||||
animationTimer = null;
|
||||
lookupCache = {};
|
||||
closingAnimationTime = 0;
|
||||
animationCounter++;
|
||||
}, 10, false);
|
||||
}
|
||||
|
||||
function closeAllAnimations(elements, count) {
|
||||
forEach(elements, function(element) {
|
||||
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
if(elementData && elementData.animationCount == count) {
|
||||
(elementData.closeAnimationFn || noop)();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getElementAnimationDetails(element, cacheKey) {
|
||||
var data = cacheKey ? lookupCache[cacheKey] : null;
|
||||
if(!data) {
|
||||
@@ -1007,6 +1068,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
timeout is empty (this would cause a flicker bug normally
|
||||
in the page. There is also no point in performing an animation
|
||||
that only has a delay and no duration */
|
||||
var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
|
||||
var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
|
||||
if(maxDuration === 0) {
|
||||
element.removeClass(className);
|
||||
@@ -1016,13 +1078,9 @@ angular.module('ngAnimate', ['ng'])
|
||||
//temporarily disable the transition so that the enter styles
|
||||
//don't animate twice (this is here to avoid a bug in Chrome/FF).
|
||||
var activeClassName = '';
|
||||
if(timings.transitionDuration > 0) {
|
||||
element.addClass(NG_ANIMATE_FALLBACK_CLASS_NAME);
|
||||
activeClassName += NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME + ' ';
|
||||
blockTransitions(element);
|
||||
} else {
|
||||
timings.transitionDuration > 0 ?
|
||||
blockTransitions(element) :
|
||||
blockKeyframeAnimations(element);
|
||||
}
|
||||
|
||||
forEach(className.split(' '), function(klass, i) {
|
||||
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
|
||||
@@ -1032,6 +1090,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
className : className,
|
||||
activeClassName : activeClassName,
|
||||
maxDuration : maxDuration,
|
||||
maxDelay : maxDelay,
|
||||
classes : className + ' ' + activeClassName,
|
||||
timings : timings,
|
||||
stagger : stagger,
|
||||
@@ -1066,30 +1125,28 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
|
||||
function animateRun(element, className, activeAnimationComplete) {
|
||||
var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
var node = extractElementNode(element);
|
||||
if(node.className.indexOf(className) == -1 || !data) {
|
||||
if(node.className.indexOf(className) == -1 || !elementData) {
|
||||
activeAnimationComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
var timings = data.timings;
|
||||
var stagger = data.stagger;
|
||||
var maxDuration = data.maxDuration;
|
||||
var activeClassName = data.activeClassName;
|
||||
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * 1000;
|
||||
var timings = elementData.timings;
|
||||
var stagger = elementData.stagger;
|
||||
var maxDuration = elementData.maxDuration;
|
||||
var activeClassName = elementData.activeClassName;
|
||||
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * ONE_SECOND;
|
||||
var startTime = Date.now();
|
||||
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
|
||||
var ii = data.ii;
|
||||
var ii = elementData.ii;
|
||||
|
||||
var applyFallbackStyle, style = '', appliedStyles = [];
|
||||
var style = '', appliedStyles = [];
|
||||
if(timings.transitionDuration > 0) {
|
||||
var propertyStyle = timings.transitionPropertyStyle;
|
||||
if(propertyStyle.indexOf('all') == -1) {
|
||||
applyFallbackStyle = true;
|
||||
var fallbackProperty = $sniffer.msie ? '-ms-zoom' : 'border-spacing';
|
||||
style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ', ' + fallbackProperty + '; ';
|
||||
style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ', ' + timings.transitionDuration + 's; ';
|
||||
style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ';';
|
||||
style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ';';
|
||||
appliedStyles.push(CSS_PREFIX + 'transition-property');
|
||||
appliedStyles.push(CSS_PREFIX + 'transition-duration');
|
||||
}
|
||||
@@ -1098,10 +1155,6 @@ angular.module('ngAnimate', ['ng'])
|
||||
if(ii > 0) {
|
||||
if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
|
||||
var delayStyle = timings.transitionDelayStyle;
|
||||
if(applyFallbackStyle) {
|
||||
delayStyle += ', ' + timings.transitionDelay + 's';
|
||||
}
|
||||
|
||||
style += CSS_PREFIX + 'transition-delay: ' +
|
||||
prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; ';
|
||||
appliedStyles.push(CSS_PREFIX + 'transition-delay');
|
||||
@@ -1124,11 +1177,16 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
element.on(css3AnimationEvents, onAnimationProgress);
|
||||
element.addClass(activeClassName);
|
||||
elementData.closeAnimationFn = function() {
|
||||
onEnd();
|
||||
activeAnimationComplete();
|
||||
};
|
||||
return onEnd;
|
||||
|
||||
// This will automatically be called by $animate so
|
||||
// there is no need to attach this internally to the
|
||||
// timeout done method.
|
||||
return function onEnd(cancelled) {
|
||||
function onEnd(cancelled) {
|
||||
element.off(css3AnimationEvents, onAnimationProgress);
|
||||
element.removeClass(activeClassName);
|
||||
animateClose(element, className);
|
||||
@@ -1136,7 +1194,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
for (var i in appliedStyles) {
|
||||
node.style.removeProperty(appliedStyles[i]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function onAnimationProgress(event) {
|
||||
event.stopPropagation();
|
||||
@@ -1202,7 +1260,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
//data from the element which will not make the 2nd animation
|
||||
//happen in the first place
|
||||
var cancel = preReflowCancellation;
|
||||
afterReflow(function() {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
unblockKeyframeAnimations(element);
|
||||
//once the reflow is complete then we point cancel to
|
||||
@@ -1218,7 +1276,6 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
function animateClose(element, className) {
|
||||
element.removeClass(className);
|
||||
element.removeClass(NG_ANIMATE_FALLBACK_CLASS_NAME);
|
||||
element.removeData(NG_ANIMATE_CSS_DATA_KEY);
|
||||
}
|
||||
|
||||
@@ -1268,7 +1325,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
beforeAddClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'));
|
||||
if(cancellationMethod) {
|
||||
afterReflow(function() {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
unblockKeyframeAnimations(element);
|
||||
animationCompleted();
|
||||
@@ -1285,7 +1342,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
beforeRemoveClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'));
|
||||
if(cancellationMethod) {
|
||||
afterReflow(function() {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
unblockKeyframeAnimations(element);
|
||||
animationCompleted();
|
||||
|
||||
Vendored
+4
@@ -1572,6 +1572,10 @@ function MockHttpExpectation(method, url, data, headers) {
|
||||
};
|
||||
}
|
||||
|
||||
function createMockXhr() {
|
||||
return new MockXhr();
|
||||
}
|
||||
|
||||
function MockXhr() {
|
||||
|
||||
// hack for testing $http, $httpBackend
|
||||
|
||||
@@ -35,7 +35,7 @@ function shallowClearAndCopy(src, dst) {
|
||||
});
|
||||
|
||||
for (var key in src) {
|
||||
if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') {
|
||||
if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ function shallowClearAndCopy(src, dst) {
|
||||
* when a param value needs to be obtained for a request (unless the param was overridden).
|
||||
*
|
||||
* Each key value in the parameter object is first bound to url template if present and then any
|
||||
* excess keys are appended to the url search query after the `?`.
|
||||
* excess keys are appended to the url seapph query after the `?`.
|
||||
*
|
||||
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
||||
* URL `/path/greet?salutation=Hello`.
|
||||
@@ -237,7 +237,7 @@ function shallowClearAndCopy(src, dst) {
|
||||
newCard.name = "Mike Smith";
|
||||
newCard.$save();
|
||||
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
|
||||
// server returns: {id:789, number:'01234', name: 'Mike Smith'};
|
||||
// server returns: {id:789, number:'0123', name: 'Mike Smith'};
|
||||
expect(newCard.id).toEqual(789);
|
||||
* </pre>
|
||||
*
|
||||
@@ -272,6 +272,35 @@ function shallowClearAndCopy(src, dst) {
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
* # Creating a custom 'PUT' request
|
||||
* In this example we create a custom method on our resource to make a PUT request
|
||||
* <pre>
|
||||
* var app = angular.module('app', ['ngResource', 'ngRoute']);
|
||||
*
|
||||
* // Some APIs expect a PUT request in the format URL/object/ID
|
||||
* // Here we are creating an 'update' method
|
||||
* app.factory('Notes', ['$resource', function($resource) {
|
||||
* return $resource('/notes/:id', null,
|
||||
* {
|
||||
* 'update': { method:'PUT' }
|
||||
* });
|
||||
* }]);
|
||||
*
|
||||
* // In our controller we get the ID from the URL using ngRoute and $routeParams
|
||||
* // We pass in $routeParams and our Notes factory along with $scope
|
||||
* app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
|
||||
function($scope, $routeParams, Notes) {
|
||||
* // First get a note object from the factory
|
||||
* var note = Notes.get({ id:$routeParams.id });
|
||||
* $id = note.id;
|
||||
*
|
||||
* // Now call update passing in the ID first then the object you are updating
|
||||
* Notes.update({ id:$id }, note);
|
||||
*
|
||||
* // This will PUT /notes/ID with the note object in the request payload
|
||||
* }]);
|
||||
* </pre>
|
||||
*/
|
||||
angular.module('ngResource', ['ng']).
|
||||
factory('$resource', ['$http', '$q', function($http, $q) {
|
||||
@@ -372,7 +401,7 @@ angular.module('ngResource', ['ng']).
|
||||
});
|
||||
|
||||
// strip trailing slashes and set the url
|
||||
url = url.replace(/\/+$/, '');
|
||||
url = url.replace(/\/+$/, '') || '/';
|
||||
// then replace collapse `/.` if found in the last URL path segment before the query
|
||||
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
|
||||
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
|
||||
|
||||
@@ -199,7 +199,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
|
||||
var locals = $route.current && $route.current.locals,
|
||||
template = locals && locals.$template;
|
||||
|
||||
if (template) {
|
||||
if (angular.isDefined(template)) {
|
||||
var newScope = scope.$new();
|
||||
var current = $route.current;
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ var validAttrs = angular.extend({}, uriAttrs, makeMap(
|
||||
'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+
|
||||
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+
|
||||
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+
|
||||
'scope,scrolling,shape,span,start,summary,target,title,type,'+
|
||||
'scope,scrolling,shape,size,span,start,summary,target,title,type,'+
|
||||
'valign,value,vspace,width'));
|
||||
|
||||
function makeMap(str) {
|
||||
|
||||
@@ -239,7 +239,8 @@ function callerFile(offset) {
|
||||
* To work around this we instead use our own handler that fires a real event.
|
||||
*/
|
||||
(function(fn){
|
||||
var parentTrigger = fn.trigger;
|
||||
// We need a handle to the original trigger function for input tests.
|
||||
var parentTrigger = fn._originalTrigger = fn.trigger;
|
||||
fn.trigger = function(type) {
|
||||
if (/(click|change|keydown|blur|input|mousedown|mouseup)/.test(type)) {
|
||||
var processDefaults = [];
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
./version.js --minor-bump
|
||||
VERSION=`./version.js --curent`
|
||||
git commit -a -m "chore(relase): start v$VERSION iteration"
|
||||
@@ -504,6 +504,21 @@ describe('angular', function() {
|
||||
expect(log).toEqual(['0:a', '1:c']);
|
||||
});
|
||||
|
||||
if (document.querySelectorAll) {
|
||||
it('should handle the result of querySelectorAll in IE8 as it has no hasOwnProperty function', function() {
|
||||
document.body.innerHTML = "<p>" +
|
||||
"<a name='x'>a</a>" +
|
||||
"<a name='y'>b</a>" +
|
||||
"<a name='x'>c</a>" +
|
||||
"</p>";
|
||||
|
||||
var htmlCollection = document.querySelectorAll('[name="x"]'),
|
||||
log = [];
|
||||
|
||||
forEach(htmlCollection, function(value, key) { log.push(key + ':' + value.innerHTML)});
|
||||
expect(log).toEqual(['0:a', '1:c']);
|
||||
});
|
||||
}
|
||||
|
||||
it('should handle arguments objects like arrays', function() {
|
||||
var args,
|
||||
|
||||
@@ -74,6 +74,17 @@ describe('injector', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should not corrupt the cache when an object fails to get instantiated', function() {
|
||||
expect(function() {
|
||||
injector.get('idontexist');
|
||||
}).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist");
|
||||
|
||||
expect(function() {
|
||||
injector.get('idontexist');
|
||||
}).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist");
|
||||
});
|
||||
|
||||
|
||||
it('should provide path to the missing provider', function() {
|
||||
providers('a', function(idontexist) {return 1;});
|
||||
providers('b', function(a) {return 2;});
|
||||
|
||||
@@ -1120,6 +1120,26 @@ describe('jqLite', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should deregister specific listener within the listener and call subsequent listeners', function() {
|
||||
var aElem = jqLite(a),
|
||||
clickSpy = jasmine.createSpy('click'),
|
||||
clickOnceSpy = jasmine.createSpy('clickOnce').andCallFake(function() {
|
||||
aElem.off('click', clickOnceSpy);
|
||||
});
|
||||
|
||||
aElem.on('click', clickOnceSpy);
|
||||
aElem.on('click', clickSpy);
|
||||
|
||||
browserTrigger(a, 'click');
|
||||
expect(clickOnceSpy).toHaveBeenCalledOnce();
|
||||
expect(clickSpy).toHaveBeenCalledOnce();
|
||||
|
||||
browserTrigger(a, 'click');
|
||||
expect(clickOnceSpy).toHaveBeenCalledOnce();
|
||||
expect(clickSpy.callCount).toBe(2);
|
||||
});
|
||||
|
||||
|
||||
it('should deregister specific listener for multiple types separated by spaces', function() {
|
||||
var aElem = jqLite(a),
|
||||
masterSpy = jasmine.createSpy('master'),
|
||||
@@ -1157,6 +1177,63 @@ describe('jqLite', function() {
|
||||
}
|
||||
});
|
||||
|
||||
describe('one', function() {
|
||||
|
||||
it('should only fire the callback once', function() {
|
||||
var element = jqLite(a);
|
||||
var spy = jasmine.createSpy('click');
|
||||
|
||||
element.one('click', spy);
|
||||
|
||||
browserTrigger(element, 'click');
|
||||
expect(spy).toHaveBeenCalledOnce();
|
||||
|
||||
browserTrigger(element, 'click');
|
||||
expect(spy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should deregister when off is called', function() {
|
||||
var element = jqLite(a);
|
||||
var spy = jasmine.createSpy('click');
|
||||
|
||||
element.one('click', spy);
|
||||
element.off('click', spy);
|
||||
|
||||
browserTrigger(element, 'click');
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return the same event object just as on() does', function() {
|
||||
var element = jqLite(a);
|
||||
var eventA, eventB;
|
||||
element.on('click', function(event) {
|
||||
eventA = event;
|
||||
});
|
||||
element.one('click', function(event) {
|
||||
eventB = event;
|
||||
});
|
||||
|
||||
browserTrigger(element, 'click');
|
||||
expect(eventA).toEqual(eventB);
|
||||
});
|
||||
|
||||
it('should not remove other event handlers of the same type after execution', function() {
|
||||
var element = jqLite(a);
|
||||
var calls = [];
|
||||
element.one('click', function(event) {
|
||||
calls.push('one');
|
||||
});
|
||||
element.on('click', function(event) {
|
||||
calls.push('on');
|
||||
});
|
||||
|
||||
browserTrigger(element, 'click');
|
||||
browserTrigger(element, 'click');
|
||||
|
||||
expect(calls).toEqual(['one','on','on']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('replaceWith', function() {
|
||||
it('should replaceWith', function() {
|
||||
|
||||
@@ -609,5 +609,10 @@ describe('browser', function() {
|
||||
fakeDocument.basePath = 'http://host.com/base/path/index.html';
|
||||
expect(browser.baseHref()).toEqual('/base/path/index.html');
|
||||
});
|
||||
|
||||
it('should remove domain from <base href> beginning with \'//\'', function() {
|
||||
fakeDocument.basePath = '//google.com/base/path/';
|
||||
expect(browser.baseHref()).toEqual('/base/path/');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+13
-2
@@ -202,9 +202,14 @@ describe('$compile', function() {
|
||||
if (msie < 9) return;
|
||||
|
||||
element = jqLite('<div>{{1+2}}</div>');
|
||||
element[0].childNodes[1] = {nodeType: 3, nodeName: 'OBJECT', textContent: 'fake node'};
|
||||
|
||||
if (!element[0].childNodes[1]) return; //browser doesn't support this kind of mocking
|
||||
try {
|
||||
element[0].childNodes[1] = {nodeType: 3, nodeName: 'OBJECT', textContent: 'fake node'};
|
||||
} catch(e) {
|
||||
} finally {
|
||||
if (!element[0].childNodes[1]) return; //browser doesn't support this kind of mocking
|
||||
}
|
||||
|
||||
expect(element[0].childNodes[1].textContent).toBe('fake node');
|
||||
|
||||
$compile(element)($rootScope);
|
||||
@@ -4244,6 +4249,12 @@ describe('$compile', function() {
|
||||
expect(element.attr('test3')).toBe('Misko');
|
||||
}));
|
||||
|
||||
it('should work with the "href" attribute', inject(function($compile, $rootScope) {
|
||||
$rootScope.value = 'test';
|
||||
element = $compile('<a ng-attr-href="test/{{value}}"></a>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('href')).toBe('test/test');
|
||||
}));
|
||||
|
||||
it('should work if they are prefixed with x- or data-', inject(function($compile, $rootScope) {
|
||||
$rootScope.name = "Misko";
|
||||
|
||||
@@ -477,19 +477,37 @@ describe('input', function() {
|
||||
expect(scope.name).toEqual('adam');
|
||||
});
|
||||
|
||||
it('should not update the model between "compositionstart" and "compositionend"', function() {
|
||||
compileInput('<input type="text" ng-model="name" name="alias"" />');
|
||||
changeInputValueTo('a');
|
||||
expect(scope.name).toEqual('a');
|
||||
if (!(msie < 9)) {
|
||||
browserTrigger(inputElm, 'compositionstart');
|
||||
changeInputValueTo('adam');
|
||||
expect(scope.name).toEqual('a');
|
||||
browserTrigger(inputElm, 'compositionend');
|
||||
}
|
||||
changeInputValueTo('adam');
|
||||
expect(scope.name).toEqual('adam');
|
||||
});
|
||||
if (!(msie < 9)) {
|
||||
describe('compositionevents', function() {
|
||||
it('should not update the model between "compositionstart" and "compositionend" on non android', inject(function($sniffer) {
|
||||
$sniffer.android = false;
|
||||
|
||||
compileInput('<input type="text" ng-model="name" name="alias"" />');
|
||||
changeInputValueTo('a');
|
||||
expect(scope.name).toEqual('a');
|
||||
browserTrigger(inputElm, 'compositionstart');
|
||||
changeInputValueTo('adam');
|
||||
expect(scope.name).toEqual('a');
|
||||
browserTrigger(inputElm, 'compositionend');
|
||||
changeInputValueTo('adam');
|
||||
expect(scope.name).toEqual('adam');
|
||||
}));
|
||||
|
||||
it('should update the model between "compositionstart" and "compositionend" on android', inject(function($sniffer) {
|
||||
$sniffer.android = true;
|
||||
|
||||
compileInput('<input type="text" ng-model="name" name="alias"" />');
|
||||
changeInputValueTo('a');
|
||||
expect(scope.name).toEqual('a');
|
||||
browserTrigger(inputElm, 'compositionstart');
|
||||
changeInputValueTo('adam');
|
||||
expect(scope.name).toEqual('adam');
|
||||
browserTrigger(inputElm, 'compositionend');
|
||||
changeInputValueTo('adam2');
|
||||
expect(scope.name).toEqual('adam2');
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
describe('"change" event', function() {
|
||||
function assertBrowserSupportsChangeEvent(inputEventSupported) {
|
||||
@@ -515,6 +533,23 @@ describe('input', function() {
|
||||
'event so that form auto complete works',function() {
|
||||
assertBrowserSupportsChangeEvent(true);
|
||||
});
|
||||
|
||||
if (!_jqLiteMode) {
|
||||
it('should not cause the double $digest when triggering an event using jQuery', function() {
|
||||
$sniffer.hasEvent = function(eventName) {
|
||||
return eventName !== 'input';
|
||||
};
|
||||
|
||||
compileInput('<input type="text" ng-model="name" name="alias" ng-change="change()" />');
|
||||
|
||||
scope.field = 'fake field';
|
||||
scope.$watch('field', function() {
|
||||
// We need to use _originalTrigger since trigger is modified by Angular Scenario.
|
||||
inputElm._originalTrigger('change');
|
||||
});
|
||||
scope.$apply();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('"paste" and "cut" events', function() {
|
||||
|
||||
@@ -338,6 +338,38 @@ describe('ngRepeat', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should allow expressions over multiple lines', function() {
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng-repeat="item in items\n' +
|
||||
'| filter:isTrue">{{item.name}}/</li>' +
|
||||
'</ul>')(scope);
|
||||
|
||||
scope.isTrue = function() {return true;};
|
||||
scope.items = [{name: 'igor'}, {name: 'misko'}];
|
||||
|
||||
scope.$digest();
|
||||
|
||||
expect(element.text()).toEqual('igor/misko/');
|
||||
});
|
||||
|
||||
|
||||
it('should strip white space characters correctly', function() {
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng-repeat="item \t\n \t in \n \t\n\n \nitems \t\t\n | filter:\n\n{' +
|
||||
'\n\t name:\n\n \'ko\'\n\n}\n\n | orderBy: \t \n \'name\' \n\n' +
|
||||
'track \t\n by \n\n\t $index \t\n ">{{item.name}}/</li>' +
|
||||
'</ul>')(scope);
|
||||
|
||||
scope.items = [{name: 'igor'}, {name: 'misko'}];
|
||||
|
||||
scope.$digest();
|
||||
|
||||
expect(element.text()).toEqual('misko/');
|
||||
});
|
||||
|
||||
|
||||
it('should not ngRepeat over parent properties', function() {
|
||||
var Class = function() {};
|
||||
Class.prototype.abc = function() {};
|
||||
|
||||
@@ -20,6 +20,16 @@ describe('ngShow / ngHide', function() {
|
||||
}));
|
||||
|
||||
|
||||
// https://github.com/angular/angular.js/issues/5414
|
||||
it('should show if the expression is a function with a no arguments', inject(function($rootScope, $compile) {
|
||||
element = jqLite('<div ng-show="exp"></div>');
|
||||
element = $compile(element)($rootScope);
|
||||
$rootScope.exp = function(){};
|
||||
$rootScope.$digest();
|
||||
expect(element).toBeShown();
|
||||
}));
|
||||
|
||||
|
||||
it('should make hidden element visible', inject(function($rootScope, $compile) {
|
||||
element = jqLite('<div class="ng-hide" ng-show="exp"></div>');
|
||||
element = $compile(element)($rootScope);
|
||||
|
||||
@@ -1215,6 +1215,30 @@ describe('select', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should treat an empty array as invalid when `multiple` attribute used', function() {
|
||||
createSelect({
|
||||
'ng-model': 'value',
|
||||
'ng-options': 'item.name for item in values',
|
||||
'ng-required': 'required',
|
||||
'multiple': ''
|
||||
}, true);
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.value = [];
|
||||
scope.values = [{name: 'A', id: 1}, {name: 'B', id: 2}];
|
||||
scope.required = true;
|
||||
});
|
||||
expect(element).toBeInvalid();
|
||||
|
||||
scope.$apply(function() {
|
||||
// ngModelWatch does not set objectEquality flag
|
||||
// array must be replaced in order to trigger $formatters
|
||||
scope.value = [scope.values[0]];
|
||||
});
|
||||
expect(element).toBeValid();
|
||||
});
|
||||
|
||||
|
||||
it('should allow falsy values as values', function() {
|
||||
createSelect({
|
||||
'ng-model': 'value',
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('$httpBackend', function() {
|
||||
})
|
||||
}
|
||||
};
|
||||
$backend = createHttpBackend($browser, MockXhr, fakeTimeout, callbacks, fakeDocument);
|
||||
$backend = createHttpBackend($browser, createMockXhr, fakeTimeout, callbacks, fakeDocument);
|
||||
callback = jasmine.createSpy('done');
|
||||
}));
|
||||
|
||||
@@ -90,6 +90,20 @@ describe('$httpBackend', function() {
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
// onreadystatechange might by called multiple times
|
||||
// with readyState === 4 on mobile webkit caused by
|
||||
// xhrs that are resolved while the app is in the background (see #5426).
|
||||
it('should not process onreadystatechange callback with readyState == 4 more than once', function() {
|
||||
$backend('GET', 'URL', null, callback);
|
||||
xhr = MockXhr.$$lastInstance;
|
||||
|
||||
xhr.status = 200;
|
||||
xhr.readyState = 4;
|
||||
xhr.onreadystatechange();
|
||||
xhr.onreadystatechange();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should set only the requested headers', function() {
|
||||
$backend('POST', 'URL', null, noop, {'X-header1': 'value1', 'X-header2': 'value2'});
|
||||
@@ -238,7 +252,7 @@ describe('$httpBackend', function() {
|
||||
expect(response).toBe('response');
|
||||
});
|
||||
|
||||
$backend = createHttpBackend($browser, SyncXhr);
|
||||
$backend = createHttpBackend($browser, function() { return new SyncXhr() });
|
||||
$backend('GET', '/url', null, callback);
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
@@ -414,7 +428,7 @@ describe('$httpBackend', function() {
|
||||
|
||||
|
||||
it('should convert 0 to 200 if content', function() {
|
||||
$backend = createHttpBackend($browser, MockXhr);
|
||||
$backend = createHttpBackend($browser, createMockXhr);
|
||||
|
||||
$backend('GET', 'file:///whatever/index.html', null, callback);
|
||||
respond(0, 'SOME CONTENT');
|
||||
@@ -425,7 +439,7 @@ describe('$httpBackend', function() {
|
||||
|
||||
|
||||
it('should convert 0 to 404 if no content', function() {
|
||||
$backend = createHttpBackend($browser, MockXhr);
|
||||
$backend = createHttpBackend($browser, createMockXhr);
|
||||
|
||||
$backend('GET', 'file:///whatever/index.html', null, callback);
|
||||
respond(0, '');
|
||||
@@ -453,7 +467,7 @@ describe('$httpBackend', function() {
|
||||
|
||||
try {
|
||||
|
||||
$backend = createHttpBackend($browser, MockXhr);
|
||||
$backend = createHttpBackend($browser, createMockXhr);
|
||||
|
||||
$backend('GET', '/whatever/index.html', null, callback);
|
||||
respond(0, '');
|
||||
@@ -468,7 +482,7 @@ describe('$httpBackend', function() {
|
||||
|
||||
|
||||
it('should return original backend status code if different from 0', function () {
|
||||
$backend = createHttpBackend($browser, MockXhr);
|
||||
$backend = createHttpBackend($browser, createMockXhr);
|
||||
|
||||
// request to http://
|
||||
$backend('POST', 'http://rest_api/create_whatever', null, callback);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
describe('$location', function() {
|
||||
var url;
|
||||
|
||||
beforeEach(module(provideLog));
|
||||
|
||||
afterEach(function() {
|
||||
// link rewriting used in html5 mode on legacy browsers binds to document.onClick, so we need
|
||||
// to clean this up after each test.
|
||||
@@ -1229,6 +1231,31 @@ describe('$location', function() {
|
||||
});
|
||||
browserTrigger(button, 'click');
|
||||
}));
|
||||
|
||||
|
||||
it('should not throw when clicking an SVGAElement link', function() {
|
||||
var base;
|
||||
module(function($locationProvider) {
|
||||
return function($browser) {
|
||||
window.location.hash = '!someHash';
|
||||
$browser.url(base = window.location.href);
|
||||
base = base.split('#')[0];
|
||||
$locationProvider.hashPrefix('!');
|
||||
}
|
||||
});
|
||||
inject(function($rootScope, $compile, $browser, $rootElement, $document, $location) {
|
||||
// we need to do this otherwise we can't simulate events
|
||||
$document.find('body').append($rootElement);
|
||||
var template = '<svg><g><a xlink:href="#!/view1"><circle r="50"></circle></a></g></svg>';
|
||||
var element = $compile(template)($rootScope);
|
||||
|
||||
$rootElement.append(element);
|
||||
var av1 = $rootElement.find('a').eq(0);
|
||||
expect(function() {
|
||||
browserTrigger(av1, 'click');
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1376,6 +1403,32 @@ describe('$location', function() {
|
||||
dealoc($rootElement);
|
||||
});
|
||||
});
|
||||
|
||||
it('should always return the new url value via path() when $locationChangeStart event occurs regardless of cause',
|
||||
inject(function($location, $rootScope, $browser, log) {
|
||||
var base = $browser.url();
|
||||
|
||||
$rootScope.$on('$locationChangeStart', function() {
|
||||
log($location.path());
|
||||
});
|
||||
|
||||
// change through $location service
|
||||
$rootScope.$apply(function() {
|
||||
$location.path('/myNewPath');
|
||||
});
|
||||
|
||||
// reset location
|
||||
$rootScope.$apply(function() {
|
||||
$location.path('');
|
||||
});
|
||||
|
||||
// change through $browser
|
||||
$browser.url(base + '#/myNewPath');
|
||||
$browser.poll();
|
||||
|
||||
expect(log).toEqual(['/myNewPath', '/', '/myNewPath']);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('LocationHtml5Url', function() {
|
||||
|
||||
@@ -906,6 +906,7 @@ describe('parser', function() {
|
||||
expect($parse('a.b')({a: {b: 0}}, {a: {b:1}})).toEqual(1);
|
||||
expect($parse('a.b')({a: null}, {a: {b:1}})).toEqual(1);
|
||||
expect($parse('a.b')({a: {b: 0}}, {a: null})).toEqual(undefined);
|
||||
expect($parse('a.b.c')({a: null}, {a: {b: {c: 1}}})).toEqual(1);
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -976,6 +977,55 @@ describe('parser', function() {
|
||||
expect($parse('"name" + id').constant).toBe(false);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('nulls in expressions', function() {
|
||||
// simpleGetterFn1
|
||||
it('should return null for `a` where `a` is null', inject(function($rootScope) {
|
||||
$rootScope.a = null;
|
||||
expect($rootScope.$eval('a')).toBe(null);
|
||||
}));
|
||||
|
||||
it('should return undefined for `a` where `a` is undefined', inject(function($rootScope) {
|
||||
expect($rootScope.$eval('a')).toBeUndefined();
|
||||
}));
|
||||
|
||||
// simpleGetterFn2
|
||||
it('should return undefined for properties of `null` constant', inject(function($rootScope) {
|
||||
expect($rootScope.$eval('null.a')).toBeUndefined();
|
||||
}));
|
||||
|
||||
it('should return undefined for properties of `null` values', inject(function($rootScope) {
|
||||
$rootScope.a = null;
|
||||
expect($rootScope.$eval('a.b')).toBeUndefined();
|
||||
}));
|
||||
|
||||
it('should return null for `a.b` where `b` is null', inject(function($rootScope) {
|
||||
$rootScope.a = { b: null };
|
||||
expect($rootScope.$eval('a.b')).toBe(null);
|
||||
}));
|
||||
|
||||
// cspSafeGetter && pathKeys.length < 6 || pathKeys.length > 2
|
||||
it('should return null for `a.b.c.d.e` where `e` is null', inject(function($rootScope) {
|
||||
$rootScope.a = { b: { c: { d: { e: null } } } };
|
||||
expect($rootScope.$eval('a.b.c.d.e')).toBe(null);
|
||||
}));
|
||||
|
||||
it('should return undefined for `a.b.c.d.e` where `d` is null', inject(function($rootScope) {
|
||||
$rootScope.a = { b: { c: { d: null } } };
|
||||
expect($rootScope.$eval('a.b.c.d.e')).toBeUndefined();
|
||||
}));
|
||||
|
||||
// cspSafeGetter || pathKeys.length > 6
|
||||
it('should return null for `a.b.c.d.e.f.g` where `g` is null', inject(function($rootScope) {
|
||||
$rootScope.a = { b: { c: { d: { e: { f: { g: null } } } } } };
|
||||
expect($rootScope.$eval('a.b.c.d.e.f.g')).toBe(null);
|
||||
}));
|
||||
|
||||
it('should return undefined for `a.b.c.d.e.f.g` where `f` is null', inject(function($rootScope) {
|
||||
$rootScope.a = { b: { c: { d: { e: { f: null } } } } };
|
||||
expect($rootScope.$eval('a.b.c.d.e.f.g')).toBeUndefined();
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+195
-54
@@ -323,41 +323,6 @@ describe('Scope', function() {
|
||||
}));
|
||||
|
||||
|
||||
it('should return a function that allows listeners to be unregistered', inject(
|
||||
function($rootScope) {
|
||||
var listener = jasmine.createSpy('watch listener'),
|
||||
listenerRemove;
|
||||
|
||||
listenerRemove = $rootScope.$watch('foo', listener);
|
||||
$rootScope.$digest(); //init
|
||||
expect(listener).toHaveBeenCalled();
|
||||
expect(listenerRemove).toBeDefined();
|
||||
|
||||
listener.reset();
|
||||
$rootScope.foo = 'bar';
|
||||
$rootScope.$digest(); //triger
|
||||
expect(listener).toHaveBeenCalledOnce();
|
||||
|
||||
listener.reset();
|
||||
$rootScope.foo = 'baz';
|
||||
listenerRemove();
|
||||
$rootScope.$digest(); //trigger
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should allow a watch to be unregistered while in a digest', inject(function($rootScope) {
|
||||
var remove1, remove2;
|
||||
$rootScope.$watch('remove', function() {
|
||||
remove1();
|
||||
remove2();
|
||||
});
|
||||
remove1 = $rootScope.$watch('thing', function() {});
|
||||
remove2 = $rootScope.$watch('thing', function() {});
|
||||
expect(function() {
|
||||
$rootScope.$apply('remove = true');
|
||||
}).not.toThrow();
|
||||
}));
|
||||
|
||||
it('should allow a watch to be added while in a digest', inject(function($rootScope) {
|
||||
var watch1 = jasmine.createSpy('watch1'),
|
||||
watch2 = jasmine.createSpy('watch2');
|
||||
@@ -402,6 +367,94 @@ describe('Scope', function() {
|
||||
expect(log).toEqual([]);
|
||||
}));
|
||||
|
||||
|
||||
describe('$watch deregistration', function() {
|
||||
|
||||
it('should return a function that allows listeners to be deregistered', inject(
|
||||
function($rootScope) {
|
||||
var listener = jasmine.createSpy('watch listener'),
|
||||
listenerRemove;
|
||||
|
||||
listenerRemove = $rootScope.$watch('foo', listener);
|
||||
$rootScope.$digest(); //init
|
||||
expect(listener).toHaveBeenCalled();
|
||||
expect(listenerRemove).toBeDefined();
|
||||
|
||||
listener.reset();
|
||||
$rootScope.foo = 'bar';
|
||||
$rootScope.$digest(); //triger
|
||||
expect(listener).toHaveBeenCalledOnce();
|
||||
|
||||
listener.reset();
|
||||
$rootScope.foo = 'baz';
|
||||
listenerRemove();
|
||||
$rootScope.$digest(); //trigger
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
|
||||
it('should allow a watch to be deregistered while in a digest', inject(function($rootScope) {
|
||||
var remove1, remove2;
|
||||
$rootScope.$watch('remove', function() {
|
||||
remove1();
|
||||
remove2();
|
||||
});
|
||||
remove1 = $rootScope.$watch('thing', function() {});
|
||||
remove2 = $rootScope.$watch('thing', function() {});
|
||||
expect(function() {
|
||||
$rootScope.$apply('remove = true');
|
||||
}).not.toThrow();
|
||||
}));
|
||||
|
||||
|
||||
it('should not mess up the digest loop if deregistration happens during digest', inject(
|
||||
function($rootScope, log) {
|
||||
|
||||
// we are testing this due to regression #5525 which is related to how the digest loops lastDirtyWatch
|
||||
// short-circuiting optimization works
|
||||
|
||||
// scenario: watch1 deregistering watch1
|
||||
var scope = $rootScope.$new();
|
||||
var deregWatch1 = scope.$watch(log.fn('watch1'), function() { deregWatch1(); log('watchAction1'); });
|
||||
scope.$watch(log.fn('watch2'), log.fn('watchAction2'));
|
||||
scope.$watch(log.fn('watch3'), log.fn('watchAction3'));
|
||||
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(log).toEqual(['watch1', 'watchAction1', 'watch2', 'watchAction2', 'watch3', 'watchAction3',
|
||||
'watch2', 'watch3']);
|
||||
scope.$destroy();
|
||||
log.reset();
|
||||
|
||||
|
||||
// scenario: watch1 deregistering watch2
|
||||
scope = $rootScope.$new();
|
||||
scope.$watch(log.fn('watch1'), function() { deregWatch2(); log('watchAction1'); });
|
||||
var deregWatch2 = scope.$watch(log.fn('watch2'), log.fn('watchAction2'));
|
||||
scope.$watch(log.fn('watch3'), log.fn('watchAction3'));
|
||||
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(log).toEqual(['watch1', 'watchAction1', 'watch1', 'watch3', 'watchAction3',
|
||||
'watch1', 'watch3']);
|
||||
scope.$destroy();
|
||||
log.reset();
|
||||
|
||||
|
||||
// scenario: watch2 deregistering watch1
|
||||
scope = $rootScope.$new();
|
||||
deregWatch1 = scope.$watch(log.fn('watch1'), log.fn('watchAction1'));
|
||||
scope.$watch(log.fn('watch2'), function() { deregWatch1(); log('watchAction2'); });
|
||||
scope.$watch(log.fn('watch3'), log.fn('watchAction3'));
|
||||
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(log).toEqual(['watch1', 'watchAction1', 'watch2', 'watchAction2', 'watch3', 'watchAction3',
|
||||
'watch2', 'watch3']);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe('$watchCollection', function() {
|
||||
var log, $rootScope, deregister;
|
||||
|
||||
@@ -730,6 +783,28 @@ describe('Scope', function() {
|
||||
first.$apply();
|
||||
expect(log).toBe('1232323');
|
||||
}));
|
||||
|
||||
|
||||
it('should decrement anscestor $$listenerCount entries', inject(function($rootScope) {
|
||||
var EVENT = 'fooEvent',
|
||||
spy = jasmine.createSpy('listener'),
|
||||
firstSecond = first.$new();
|
||||
|
||||
firstSecond.$on(EVENT, spy);
|
||||
firstSecond.$on(EVENT, spy);
|
||||
middle.$on(EVENT, spy);
|
||||
|
||||
expect($rootScope.$$listenerCount[EVENT]).toBe(3);
|
||||
expect(first.$$listenerCount[EVENT]).toBe(2);
|
||||
|
||||
firstSecond.$destroy();
|
||||
|
||||
expect($rootScope.$$listenerCount[EVENT]).toBe(1);
|
||||
expect(first.$$listenerCount[EVENT]).toBeUndefined();
|
||||
|
||||
$rootScope.$broadcast(EVENT);
|
||||
expect(spy.callCount).toBe(1);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -1091,29 +1166,78 @@ describe('Scope', function() {
|
||||
}));
|
||||
|
||||
|
||||
it('should return a function that deregisters the listener', inject(function($rootScope) {
|
||||
var log = '',
|
||||
child = $rootScope.$new(),
|
||||
listenerRemove;
|
||||
it('should increment ancestor $$listenerCount entries', inject(function($rootScope) {
|
||||
var child1 = $rootScope.$new(),
|
||||
child2 = child1.$new(),
|
||||
spy = jasmine.createSpy();
|
||||
|
||||
function eventFn() {
|
||||
log += 'X';
|
||||
}
|
||||
$rootScope.$on('event1', spy);
|
||||
expect($rootScope.$$listenerCount).toEqual({event1: 1});
|
||||
|
||||
listenerRemove = child.$on('abc', eventFn);
|
||||
expect(log).toEqual('');
|
||||
expect(listenerRemove).toBeDefined();
|
||||
child1.$on('event1', spy);
|
||||
expect($rootScope.$$listenerCount).toEqual({event1: 2});
|
||||
expect(child1.$$listenerCount).toEqual({event1: 1});
|
||||
|
||||
child.$emit('abc');
|
||||
child.$broadcast('abc');
|
||||
expect(log).toEqual('XX');
|
||||
|
||||
log = '';
|
||||
listenerRemove();
|
||||
child.$emit('abc');
|
||||
child.$broadcast('abc');
|
||||
expect(log).toEqual('');
|
||||
child2.$on('event2', spy);
|
||||
expect($rootScope.$$listenerCount).toEqual({event1: 2, event2: 1});
|
||||
expect(child1.$$listenerCount).toEqual({event1: 1, event2: 1});
|
||||
expect(child2.$$listenerCount).toEqual({event2: 1});
|
||||
}));
|
||||
|
||||
|
||||
describe('deregistration', function() {
|
||||
|
||||
it('should return a function that deregisters the listener', inject(function($rootScope) {
|
||||
var log = '',
|
||||
child = $rootScope.$new(),
|
||||
listenerRemove;
|
||||
|
||||
function eventFn() {
|
||||
log += 'X';
|
||||
}
|
||||
|
||||
listenerRemove = child.$on('abc', eventFn);
|
||||
expect(log).toEqual('');
|
||||
expect(listenerRemove).toBeDefined();
|
||||
|
||||
child.$emit('abc');
|
||||
child.$broadcast('abc');
|
||||
expect(log).toEqual('XX');
|
||||
expect($rootScope.$$listenerCount['abc']).toBe(1);
|
||||
|
||||
log = '';
|
||||
listenerRemove();
|
||||
child.$emit('abc');
|
||||
child.$broadcast('abc');
|
||||
expect(log).toEqual('');
|
||||
expect($rootScope.$$listenerCount['abc']).toBeUndefined();
|
||||
}));
|
||||
|
||||
|
||||
it('should decrement ancestor $$listenerCount entries', inject(function($rootScope) {
|
||||
var child1 = $rootScope.$new(),
|
||||
child2 = child1.$new(),
|
||||
spy = jasmine.createSpy();
|
||||
|
||||
$rootScope.$on('event1', spy);
|
||||
expect($rootScope.$$listenerCount).toEqual({event1: 1});
|
||||
|
||||
child1.$on('event1', spy);
|
||||
expect($rootScope.$$listenerCount).toEqual({event1: 2});
|
||||
expect(child1.$$listenerCount).toEqual({event1: 1});
|
||||
|
||||
var deregisterEvent2Listener = child2.$on('event2', spy);
|
||||
expect($rootScope.$$listenerCount).toEqual({event1: 2, event2: 1});
|
||||
expect(child1.$$listenerCount).toEqual({event1: 1, event2: 1});
|
||||
expect(child2.$$listenerCount).toEqual({event2: 1});
|
||||
|
||||
deregisterEvent2Listener();
|
||||
|
||||
expect($rootScope.$$listenerCount).toEqual({event1: 2});
|
||||
expect(child1.$$listenerCount).toEqual({event1: 1});
|
||||
expect(child2.$$listenerCount).toEqual({});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1360,6 +1484,23 @@ describe('Scope', function() {
|
||||
}));
|
||||
|
||||
|
||||
it('should not descend past scopes with a $$listerCount of 0 or undefined',
|
||||
inject(function($rootScope) {
|
||||
var EVENT = 'fooEvent',
|
||||
spy = jasmine.createSpy('listener');
|
||||
|
||||
// Precondition: There should be no listeners for fooEvent.
|
||||
expect($rootScope.$$listenerCount[EVENT]).toBeUndefined();
|
||||
|
||||
// Add a spy listener to a child scope.
|
||||
$rootScope.$$childHead.$$listeners[EVENT] = [spy];
|
||||
|
||||
// $rootScope's count for 'fooEvent' is undefined, so spy should not be called.
|
||||
$rootScope.$broadcast(EVENT);
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
|
||||
it('should return event object', function() {
|
||||
var result = child1.$broadcast('some');
|
||||
|
||||
|
||||
@@ -334,6 +334,21 @@ describe('$sniffer', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should provide the android version', function() {
|
||||
module(function($provide) {
|
||||
var win = {
|
||||
navigator: {
|
||||
userAgent: 'android 2'
|
||||
}
|
||||
};
|
||||
$provide.value('$document', jqLite({}));
|
||||
$provide.value('$window', win);
|
||||
});
|
||||
inject(function($sniffer) {
|
||||
expect($sniffer.android).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the internal msie flag', inject(function($sniffer) {
|
||||
expect(isNaN($sniffer.msie)).toBe(isNaN(msie));
|
||||
if (msie) {
|
||||
|
||||
+217
-124
@@ -4,6 +4,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
beforeEach(module('ngAnimate'));
|
||||
|
||||
|
||||
it("should disable animations on bootstrap for structural animations even after the first digest has passed", function() {
|
||||
var hasBeenAnimated = false;
|
||||
module(function($animateProvider) {
|
||||
@@ -37,10 +38,12 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
//we use another describe block because the before/after operations below
|
||||
//are used across all animations tests and we don't want that same behavior
|
||||
//to be used on the root describe block at the start of the animateSpec.js file
|
||||
describe('', function() {
|
||||
|
||||
var ss, body;
|
||||
beforeEach(module(function() {
|
||||
body = jqLite(document.body);
|
||||
@@ -61,6 +64,7 @@ describe("ngAnimate", function() {
|
||||
dealoc(body);
|
||||
});
|
||||
|
||||
|
||||
describe("$animate", function() {
|
||||
|
||||
var element, $rootElement;
|
||||
@@ -85,6 +89,7 @@ describe("ngAnimate", function() {
|
||||
expect($animate.enabled()).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it('should place a hard disable on all child animations', function() {
|
||||
var count = 0;
|
||||
module(function($animateProvider) {
|
||||
@@ -132,6 +137,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should skip animations if the element is attached to the $rootElement', function() {
|
||||
var count = 0;
|
||||
module(function($animateProvider) {
|
||||
@@ -154,6 +160,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should check enable/disable animations up until the $rootElement element', function() {
|
||||
var rootElm = jqLite('<div></div>');
|
||||
|
||||
@@ -195,6 +202,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("with polyfill", function() {
|
||||
|
||||
var child, after;
|
||||
@@ -262,6 +270,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
it("should animate the enter animation event",
|
||||
inject(function($animate, $rootScope, $sniffer, $timeout) {
|
||||
element[0].removeChild(child[0]);
|
||||
@@ -280,6 +289,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.contents().length).toBe(1);
|
||||
}));
|
||||
|
||||
|
||||
it("should animate the leave animation event",
|
||||
inject(function($animate, $rootScope, $sniffer, $timeout) {
|
||||
|
||||
@@ -297,6 +307,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.contents().length).toBe(0);
|
||||
}));
|
||||
|
||||
|
||||
it("should animate the move animation event",
|
||||
inject(function($animate, $compile, $rootScope, $timeout, $sniffer) {
|
||||
|
||||
@@ -316,6 +327,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.text()).toBe('21');
|
||||
}));
|
||||
|
||||
|
||||
it("should animate the show animation event",
|
||||
inject(function($animate, $rootScope, $sniffer, $timeout) {
|
||||
|
||||
@@ -334,6 +346,7 @@ describe("ngAnimate", function() {
|
||||
expect(child).toBeShown();
|
||||
}));
|
||||
|
||||
|
||||
it("should animate the hide animation event",
|
||||
inject(function($animate, $rootScope, $sniffer, $timeout) {
|
||||
|
||||
@@ -349,6 +362,7 @@ describe("ngAnimate", function() {
|
||||
expect(child).toBeHidden();
|
||||
}));
|
||||
|
||||
|
||||
it("should assign the ng-event className to all animation events when transitions/keyframes are used",
|
||||
inject(function($animate, $sniffer, $rootScope, $timeout) {
|
||||
|
||||
@@ -401,6 +415,7 @@ describe("ngAnimate", function() {
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}));
|
||||
|
||||
|
||||
it("should not run if animations are disabled",
|
||||
inject(function($animate, $rootScope, $timeout, $sniffer) {
|
||||
|
||||
@@ -425,6 +440,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.text()).toBe('memento');
|
||||
}));
|
||||
|
||||
|
||||
it("should only call done() once and right away if another animation takes place in between",
|
||||
inject(function($animate, $rootScope, $sniffer, $timeout) {
|
||||
|
||||
@@ -457,6 +473,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.children().length).toBe(0);
|
||||
}));
|
||||
|
||||
|
||||
it("should retain existing styles of the animated element",
|
||||
inject(function($animate, $rootScope, $sniffer, $timeout) {
|
||||
|
||||
@@ -478,6 +495,7 @@ describe("ngAnimate", function() {
|
||||
expect(child.attr('style')).toMatch(/width: 20px/i);
|
||||
}));
|
||||
|
||||
|
||||
it("should call the cancel callback when another animation is called on the same element",
|
||||
inject(function($animate, $rootScope, $sniffer, $timeout) {
|
||||
|
||||
@@ -495,6 +513,7 @@ describe("ngAnimate", function() {
|
||||
expect(child.hasClass('animation-cancelled')).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it("should skip a class-based animation if the same element already has an ongoing structural animation",
|
||||
inject(function($animate, $rootScope, $sniffer, $timeout) {
|
||||
|
||||
@@ -519,6 +538,28 @@ describe("ngAnimate", function() {
|
||||
expect(completed).toBe(true);
|
||||
}));
|
||||
|
||||
it("should skip class-based animations if animations are directly disabled on the same element", function() {
|
||||
var capture;
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.capture', function() {
|
||||
return {
|
||||
addClass : function(element, className, done) {
|
||||
capture = true;
|
||||
done();
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($animate, $rootScope, $sniffer, $timeout) {
|
||||
$animate.enabled(true);
|
||||
$animate.enabled(false, element);
|
||||
|
||||
$animate.addClass(element, 'capture');
|
||||
expect(element.hasClass('capture')).toBe(true);
|
||||
expect(capture).not.toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("should fire the cancel/end function with the correct flag in the parameters",
|
||||
inject(function($animate, $rootScope, $sniffer, $timeout) {
|
||||
|
||||
@@ -557,6 +598,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.hasClass('custom-long-delay')).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it("should allow both multiple JS and CSS animations which run in parallel",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout, _$rootElement_) {
|
||||
$rootElement = _$rootElement_;
|
||||
@@ -588,7 +630,9 @@ describe("ngAnimate", function() {
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe("with CSS3", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
module(function() {
|
||||
return function(_$rootElement_) {
|
||||
@@ -597,7 +641,9 @@ describe("ngAnimate", function() {
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
describe("Animations", function() {
|
||||
|
||||
it("should properly detect and make use of CSS Animations",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
@@ -621,6 +667,7 @@ describe("ngAnimate", function() {
|
||||
expect(element).toBeShown();
|
||||
}));
|
||||
|
||||
|
||||
it("should properly detect and make use of CSS Animations with multiple iterations",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
@@ -645,29 +692,6 @@ describe("ngAnimate", function() {
|
||||
expect(element).toBeShown();
|
||||
}));
|
||||
|
||||
it("should fallback to the animation duration if an infinite iteration is provided",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
var style = '-webkit-animation-duration: 2s;' +
|
||||
'-webkit-animation-iteration-count: infinite;' +
|
||||
'animation-duration: 2s;' +
|
||||
'animation-iteration-count: infinite;';
|
||||
|
||||
ss.addRule('.ng-hide-add', style);
|
||||
ss.addRule('.ng-hide-remove', style);
|
||||
|
||||
element = $compile(html('<div>1</div>'))($rootScope);
|
||||
|
||||
element.addClass('ng-hide');
|
||||
expect(element).toBeHidden();
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.animations) {
|
||||
$timeout.flush();
|
||||
browserTrigger(element,'animationend', { timeStamp: Date.now() + 2000, elapsedTime: 2 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
}));
|
||||
|
||||
it("should not consider the animation delay is provided",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
@@ -695,6 +719,7 @@ describe("ngAnimate", function() {
|
||||
expect(element).toBeShown();
|
||||
}));
|
||||
|
||||
|
||||
it("should skip animations if disabled and run when enabled",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
$animate.enabled(false);
|
||||
@@ -711,6 +736,7 @@ describe("ngAnimate", function() {
|
||||
expect(element).toBeShown();
|
||||
}));
|
||||
|
||||
|
||||
it("should finish the previous animation when a new animation is started",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
var style = '-webkit-animation: some_animation 2s linear 0s 1 alternate;' +
|
||||
@@ -745,6 +771,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.hasClass('ng-hide-remove-active')).toBe(false);
|
||||
}));
|
||||
|
||||
|
||||
it("should stagger the items when the correct CSS class is provided",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) {
|
||||
|
||||
@@ -805,6 +832,7 @@ describe("ngAnimate", function() {
|
||||
expect(elements[4].attr('style')).not.toMatch(/animation-delay: 0\.4\d*s/);
|
||||
}));
|
||||
|
||||
|
||||
it("should stagger items when multiple animation durations/delays are defined",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) {
|
||||
|
||||
@@ -838,108 +866,11 @@ describe("ngAnimate", function() {
|
||||
expect(elements[2].attr('style')).toMatch(/animation-delay: 1\.2\d*s,\s*2\.2\d*s/);
|
||||
expect(elements[3].attr('style')).toMatch(/animation-delay: 1\.3\d*s,\s*2\.3\d*s/);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe("Transitions", function() {
|
||||
it("should only apply the fallback transition property unless all properties are being animated",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.animations) return;
|
||||
|
||||
ss.addRule('.all.ng-enter', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all');
|
||||
|
||||
ss.addRule('.one.ng-enter', '-webkit-transition:1s linear color;' +
|
||||
'transition:1s linear color');
|
||||
|
||||
var element = $compile('<div></div>')($rootScope);
|
||||
var child = $compile('<div class="all">...</div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
var body = jqLite($document[0].body);
|
||||
body.append($rootElement);
|
||||
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
expect(child.attr('style') || '').not.toContain('transition-property');
|
||||
expect(child.hasClass('ng-animate-start')).toBe(true);
|
||||
expect(child.hasClass('ng-animate-active')).toBe(true);
|
||||
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1000 });
|
||||
$timeout.flush();
|
||||
|
||||
expect(child.hasClass('ng-animate')).toBe(false);
|
||||
expect(child.hasClass('ng-animate-active')).toBe(false);
|
||||
|
||||
child.remove();
|
||||
|
||||
var child2 = $compile('<div class="one">...</div>')($rootScope);
|
||||
|
||||
$animate.enter(child2, element);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
//IE removes the -ms- prefix when placed on the style
|
||||
var fallbackProperty = $sniffer.msie ? 'zoom' : 'border-spacing';
|
||||
var regExp = new RegExp("transition-property:\\s+color\\s*,\\s*" + fallbackProperty + "\\s*;");
|
||||
expect(child2.attr('style') || '').toMatch(regExp);
|
||||
expect(child2.hasClass('ng-animate')).toBe(true);
|
||||
expect(child2.hasClass('ng-animate-active')).toBe(true);
|
||||
|
||||
browserTrigger(child2,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1000 });
|
||||
$timeout.flush();
|
||||
|
||||
expect(child2.hasClass('ng-animate')).toBe(false);
|
||||
expect(child2.hasClass('ng-animate-active')).toBe(false);
|
||||
}));
|
||||
|
||||
it("should not apply the fallback classes if no animations are going on or if CSS animations are going on",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.animations) return;
|
||||
|
||||
ss.addRule('.transitions', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all');
|
||||
|
||||
ss.addRule('.keyframes', '-webkit-animation:my_animation 1s;' +
|
||||
'animation:my_animation 1s');
|
||||
|
||||
var element = $compile('<div class="transitions">...</div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animate.enabled(false);
|
||||
|
||||
$animate.addClass(element, 'klass');
|
||||
|
||||
expect(element.hasClass('ng-animate-start')).toBe(false);
|
||||
|
||||
element.removeClass('klass');
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
$animate.addClass(element, 'klass');
|
||||
|
||||
$timeout.flush();
|
||||
|
||||
expect(element.hasClass('ng-animate-start')).toBe(true);
|
||||
expect(element.hasClass('ng-animate-active')).toBe(true);
|
||||
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
|
||||
expect(element.hasClass('ng-animate-start')).toBe(false);
|
||||
expect(element.hasClass('ng-animate-active')).toBe(false);
|
||||
|
||||
element.attr('class', 'keyframes');
|
||||
|
||||
$animate.addClass(element, 'klass2');
|
||||
|
||||
$timeout.flush();
|
||||
|
||||
expect(element.hasClass('ng-animate-start')).toBe(false);
|
||||
expect(element.hasClass('ng-animate-active')).toBe(false);
|
||||
}));
|
||||
|
||||
it("should skip transitions if disabled and run when enabled",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
@@ -971,6 +902,7 @@ describe("ngAnimate", function() {
|
||||
expect(element).toBeShown();
|
||||
}));
|
||||
|
||||
|
||||
it("should skip animations if disabled and run when enabled picking the longest specified duration",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
@@ -998,6 +930,7 @@ describe("ngAnimate", function() {
|
||||
expect(element).toBeShown();
|
||||
}));
|
||||
|
||||
|
||||
it("should skip animations if disabled and run when enabled picking the longest specified duration/delay combination",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
$animate.enabled(false);
|
||||
@@ -1033,6 +966,7 @@ describe("ngAnimate", function() {
|
||||
expect(element).toBeShown();
|
||||
}));
|
||||
|
||||
|
||||
it("should NOT overwrite styles with outdated values when animation completes",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
@@ -1062,6 +996,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.css('width')).toBe("200px");
|
||||
}));
|
||||
|
||||
|
||||
it("should animate for the highest duration",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
var style = '-webkit-transition:1s linear all 2s;' +
|
||||
@@ -1083,12 +1018,13 @@ describe("ngAnimate", function() {
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
if ($sniffer.transitions) {
|
||||
expect(element.hasClass('ng-animate-active')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-remove-active')).toBe(true);
|
||||
browserTrigger(element,'animationend', { timeStamp: Date.now() + 11000, elapsedTime: 11 });
|
||||
expect(element.hasClass('ng-animate-active')).toBe(false);
|
||||
expect(element.hasClass('ng-hide-remove-active')).toBe(false);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
it("should finish the previous transition when a new animation is started",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
var style = '-webkit-transition: 1s linear all;' +
|
||||
@@ -1121,6 +1057,7 @@ describe("ngAnimate", function() {
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
it("should stagger the items when the correct CSS class is provided",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) {
|
||||
|
||||
@@ -1181,6 +1118,7 @@ describe("ngAnimate", function() {
|
||||
expect(elements[4].attr('style')).not.toMatch(/transition-delay: 0\.4\d*s/);
|
||||
}));
|
||||
|
||||
|
||||
it("should stagger items when multiple transition durations/delays are defined",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) {
|
||||
|
||||
@@ -1209,13 +1147,66 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
expect(elements[0].attr('style')).toMatch(/transition-duration: 1\d*s,\s*3\d*s;/);
|
||||
expect(elements[0].attr('style')).not.toContain('transition-delay');
|
||||
expect(elements[1].attr('style')).toMatch(/transition-delay: 2\.1\d*s,\s*4\.1\d*s/);
|
||||
expect(elements[2].attr('style')).toMatch(/transition-delay: 2\.2\d*s,\s*4\.2\d*s/);
|
||||
expect(elements[3].attr('style')).toMatch(/transition-delay: 2\.3\d*s,\s*4\.3\d*s/);
|
||||
}));
|
||||
|
||||
|
||||
it("apply a closing timeout to close all pending transitions",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.animated-element', '-webkit-transition:5s linear all;' +
|
||||
'transition:5s linear all;');
|
||||
|
||||
element = $compile(html('<div class="animated-element">foo</div>'))($rootScope);
|
||||
|
||||
$animate.addClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //reflow
|
||||
expect(element.hasClass('some-class-add-active')).toBe(true);
|
||||
|
||||
$timeout.flush(7500); //closing timeout
|
||||
expect(element.hasClass('some-class-add-active')).toBe(false);
|
||||
}));
|
||||
|
||||
|
||||
it("should not allow the closing animation to close off a successive animation midway",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.some-class-add', '-webkit-transition:5s linear all;' +
|
||||
'transition:5s linear all;');
|
||||
ss.addRule('.some-class-remove', '-webkit-transition:10s linear all;' +
|
||||
'transition:10s linear all;');
|
||||
|
||||
element = $compile(html('<div>foo</div>'))($rootScope);
|
||||
|
||||
$animate.addClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //reflow
|
||||
expect(element.hasClass('some-class-add-active')).toBe(true);
|
||||
|
||||
$animate.removeClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //second reflow
|
||||
|
||||
$timeout.flush(7500); //closing timeout for the first animation
|
||||
expect(element.hasClass('some-class-remove-active')).toBe(true);
|
||||
|
||||
$timeout.flush(15000); //closing timeout for the second animation
|
||||
expect(element.hasClass('some-class-remove-active')).toBe(false);
|
||||
|
||||
$timeout.verifyNoPendingTasks();
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
it("should apply staggering to both transitions and keyframe animations when used within the same animation",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) {
|
||||
|
||||
@@ -1263,7 +1254,9 @@ describe("ngAnimate", function() {
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe('animation evaluation', function () {
|
||||
|
||||
it('should re-evaluate the CSS classes for an animation each time',
|
||||
inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout, $compile) {
|
||||
|
||||
@@ -1306,6 +1299,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.hasClass('xyz')).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it('should only append active to the newly append CSS className values',
|
||||
inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1340,7 +1334,9 @@ describe("ngAnimate", function() {
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe("Callbacks", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.custom', function($timeout) {
|
||||
@@ -1360,6 +1356,7 @@ describe("ngAnimate", function() {
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
it("should fire the enter callback",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1379,6 +1376,7 @@ describe("ngAnimate", function() {
|
||||
expect(flag).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it("should fire the leave callback",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1398,6 +1396,7 @@ describe("ngAnimate", function() {
|
||||
expect(flag).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it("should fire the move callback",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1419,6 +1418,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.parent().id).toBe(parent2.id);
|
||||
}));
|
||||
|
||||
|
||||
it("should fire the addClass/removeClass callbacks",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1441,6 +1441,7 @@ describe("ngAnimate", function() {
|
||||
expect(signature).toBe('AB');
|
||||
}));
|
||||
|
||||
|
||||
it("should fire a done callback when provided with no animation",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1458,6 +1459,7 @@ describe("ngAnimate", function() {
|
||||
expect(flag).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it("should fire a done callback when provided with a css animation/transition",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1483,6 +1485,7 @@ describe("ngAnimate", function() {
|
||||
expect(flag).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it("should fire a done callback when provided with a JS animation",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1501,6 +1504,7 @@ describe("ngAnimate", function() {
|
||||
expect(flag).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it("should fire the callback right away if another animation is called right after",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1528,7 +1532,9 @@ describe("ngAnimate", function() {
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe("addClass / removeClass", function() {
|
||||
|
||||
var captured;
|
||||
beforeEach(function() {
|
||||
module(function($animateProvider, $provide) {
|
||||
@@ -1547,6 +1553,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should not perform an animation, and the followup DOM operation, if the class is " +
|
||||
"already present during addClass or not present during removeClass on the element",
|
||||
inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) {
|
||||
@@ -1582,6 +1589,7 @@ describe("ngAnimate", function() {
|
||||
expect(captured).toBe('addClass-some-class');
|
||||
}));
|
||||
|
||||
|
||||
it("should add and remove CSS classes after an animation even if no animation is present",
|
||||
inject(function($animate, $rootScope, $sniffer, $rootElement) {
|
||||
|
||||
@@ -1601,6 +1609,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.hasClass('klass-remove-active')).toBe(false);
|
||||
}));
|
||||
|
||||
|
||||
it("should add and remove CSS classes with a callback",
|
||||
inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1626,6 +1635,7 @@ describe("ngAnimate", function() {
|
||||
expect(signature).toBe('AB');
|
||||
}));
|
||||
|
||||
|
||||
it("should end the current addClass animation, add the CSS class and then run the removeClass animation",
|
||||
inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1675,6 +1685,7 @@ describe("ngAnimate", function() {
|
||||
expect(signature).toBe('12');
|
||||
}));
|
||||
|
||||
|
||||
it("should properly execute JS animations and use callbacks when using addClass / removeClass",
|
||||
inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1704,6 +1715,7 @@ describe("ngAnimate", function() {
|
||||
expect(signature).toBe('XY');
|
||||
}));
|
||||
|
||||
|
||||
it("should properly execute CSS animations/transitions and use callbacks when using addClass / removeClass",
|
||||
inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1754,6 +1766,7 @@ describe("ngAnimate", function() {
|
||||
expect(signature).toBe('db');
|
||||
}));
|
||||
|
||||
|
||||
it("should allow for multiple css classes to be animated plus a callback when added",
|
||||
inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1795,6 +1808,7 @@ describe("ngAnimate", function() {
|
||||
expect(flag).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it("should allow for multiple css classes to be animated plus a callback when removed",
|
||||
inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
@@ -1858,6 +1872,7 @@ describe("ngAnimate", function() {
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
it("should properly animate and parse CSS3 transitions",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
|
||||
@@ -1881,6 +1896,7 @@ describe("ngAnimate", function() {
|
||||
expect(child.hasClass('ng-enter-active')).toBe(false);
|
||||
}));
|
||||
|
||||
|
||||
it("should properly animate and parse CSS3 animations",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
|
||||
@@ -1903,6 +1919,7 @@ describe("ngAnimate", function() {
|
||||
expect(child.hasClass('ng-enter-active')).toBe(false);
|
||||
}));
|
||||
|
||||
|
||||
it("should not set the transition property flag if only CSS animations are used",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
|
||||
@@ -1937,6 +1954,7 @@ describe("ngAnimate", function() {
|
||||
expect(child.css(propertyKey)).not.toBe('background-color');
|
||||
}));
|
||||
|
||||
|
||||
it("should skip animations if the browser does not support CSS3 transitions and CSS3 animations",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer) {
|
||||
|
||||
@@ -1955,6 +1973,7 @@ describe("ngAnimate", function() {
|
||||
expect(child.hasClass('ng-enter')).toBe(false);
|
||||
}));
|
||||
|
||||
|
||||
it("should run other defined animations inline with CSS3 animations", function() {
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.custom', function($timeout) {
|
||||
@@ -1990,6 +2009,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should properly cancel CSS transitions or animations if another animation is fired", function() {
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.usurper', function($timeout) {
|
||||
@@ -2036,6 +2056,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should not perform the active class animation if the animation has been cancelled before the reflow occurs", function() {
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
if(!$sniffer.transitions) return;
|
||||
@@ -2059,6 +2080,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
// it("should add and remove CSS classes and perform CSS animations during the process",
|
||||
// inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
//
|
||||
@@ -2098,6 +2120,7 @@ describe("ngAnimate", function() {
|
||||
// expect(element.hasClass('on-remove-active')).toBe(false);
|
||||
// }));
|
||||
//
|
||||
//
|
||||
// it("should show and hide elements with CSS & JS animations being performed in the process", function() {
|
||||
// module(function($animateProvider) {
|
||||
// $animateProvider.register('.displayer', function($timeout) {
|
||||
@@ -2158,6 +2181,8 @@ describe("ngAnimate", function() {
|
||||
// expect(element.hasClass('hiding')).toBe(false);
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
it("should remove all the previous classes when the next animation is applied before a reflow", function() {
|
||||
var fn, interceptedClass;
|
||||
module(function($animateProvider) {
|
||||
@@ -2195,6 +2220,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should provide the correct CSS class to the addClass and removeClass callbacks within a JS animation", function() {
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.classify', function() {
|
||||
@@ -2224,6 +2250,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should not skip ngAnimate animations when any pre-existing CSS transitions are present on the element", function() {
|
||||
inject(function($compile, $rootScope, $animate, $timeout, $sniffer) {
|
||||
if(!$sniffer.transitions) return;
|
||||
@@ -2252,6 +2279,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should wait until both the duration and delay are complete to close off the animation",
|
||||
inject(function($compile, $rootScope, $animate, $timeout, $sniffer) {
|
||||
|
||||
@@ -2286,6 +2314,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.contents().length).toBe(1);
|
||||
}));
|
||||
|
||||
|
||||
it("should cancel all child animations when a leave or move animation is triggered on a parent element", function() {
|
||||
|
||||
var step, animationState;
|
||||
@@ -2361,6 +2390,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should wait until a queue of animations are complete before performing a reflow",
|
||||
inject(function($rootScope, $compile, $timeout,$sniffer) {
|
||||
|
||||
@@ -2495,6 +2525,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should not disable any child animations when any parent class-based animations are run", function() {
|
||||
var intercepted;
|
||||
module(function($animateProvider) {
|
||||
@@ -2521,6 +2552,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should cache the response from getComputedStyle if each successive element has the same className value and parent until the first reflow hits", function() {
|
||||
var count = 0;
|
||||
module(function($provide) {
|
||||
@@ -2567,6 +2599,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should cancel an ongoing class-based animation only if the new class contains transition/animation CSS code",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
|
||||
@@ -2603,6 +2636,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.hasClass('yellow-add')).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it("should cancel and perform the dom operation only after the reflow has run",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
|
||||
@@ -2633,6 +2667,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.hasClass('red')).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it('should enable and disable animations properly on the root element', function() {
|
||||
var count = 0;
|
||||
module(function($animateProvider) {
|
||||
@@ -2656,6 +2691,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should perform pre and post animations', function() {
|
||||
var steps = [];
|
||||
module(function($animateProvider) {
|
||||
@@ -2684,6 +2720,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should treat the leave event always as a before event and discard the beforeLeave function', function() {
|
||||
var parentID, steps = [];
|
||||
module(function($animateProvider) {
|
||||
@@ -2717,6 +2754,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should only perform the DOM operation once',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
@@ -2751,6 +2789,7 @@ describe("ngAnimate", function() {
|
||||
expect(element.hasClass('base-class')).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it('should block and unblock transitions before the dom operation occurs',
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $animate, $sniffer, $timeout) {
|
||||
|
||||
@@ -2784,6 +2823,7 @@ describe("ngAnimate", function() {
|
||||
expect(capturedProperty).not.toBe('none');
|
||||
}));
|
||||
|
||||
|
||||
it('should block and unblock keyframe animations around the reflow operation',
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $animate, $sniffer, $timeout) {
|
||||
|
||||
@@ -2810,6 +2850,7 @@ describe("ngAnimate", function() {
|
||||
expect(node.style[animationKey]).not.toContain('none');
|
||||
}));
|
||||
|
||||
|
||||
it('should block and unblock keyframe animations before the followup JS animation occurs', function() {
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.special', function($sniffer, $window) {
|
||||
@@ -2853,6 +2894,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should round up long elapsedTime values to close off a CSS3 animation',
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $animate, $sniffer, $timeout, $window) {
|
||||
if (!$sniffer.animations) return;
|
||||
@@ -2874,6 +2916,7 @@ describe("ngAnimate", function() {
|
||||
expect($rootElement.children().length).toBe(0);
|
||||
}));
|
||||
|
||||
|
||||
it('should properly animate elements with compound directives', function() {
|
||||
var capturedAnimation;
|
||||
module(function($animateProvider) {
|
||||
@@ -2926,5 +2969,55 @@ describe("ngAnimate", function() {
|
||||
expect(capturedAnimation).toBe('leave');
|
||||
});
|
||||
});
|
||||
|
||||
it('should animate only the specified CSS className', function() {
|
||||
var captures = {};
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.classNameFilter(/prefixed-animation/);
|
||||
$animateProvider.register('.capture', function() {
|
||||
return {
|
||||
enter : buildFn('enter'),
|
||||
leave : buildFn('leave')
|
||||
};
|
||||
|
||||
function buildFn(key) {
|
||||
return function(element, className, done) {
|
||||
captures[key] = true;
|
||||
(done || className)();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $timeout, $templateCache, $sniffer, $animate) {
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
var element = $compile('<div class="capture"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var enterDone = false;
|
||||
$animate.enter(element, $rootElement, null, function() {
|
||||
enterDone = true;
|
||||
});
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
expect(captures['enter']).toBeUndefined();
|
||||
expect(enterDone).toBe(true);
|
||||
|
||||
element.addClass('prefixed-animation');
|
||||
|
||||
var leaveDone = false;
|
||||
$animate.leave(element, function() {
|
||||
leaveDone = true;
|
||||
});
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
expect(captures['leave']).toBe(true);
|
||||
expect(leaveDone).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -150,6 +150,13 @@ describe("resource", function() {
|
||||
R.get({a:6, b:7, c:8});
|
||||
});
|
||||
|
||||
it('should not collapsed the url into an empty string', function() {
|
||||
var R = $resource('/:foo/:bar/');
|
||||
|
||||
$httpBackend.when('GET', '/').respond('{}');
|
||||
|
||||
R.get({});
|
||||
});
|
||||
|
||||
it('should support escaping colons in url template', function() {
|
||||
var R = $resource('http://localhost\\:8080/Path/:a/\\:stillPath/:b');
|
||||
|
||||
@@ -56,6 +56,29 @@ describe('ngView', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should instantiate controller for empty template', function() {
|
||||
var log = [], controllerScope,
|
||||
Ctrl = function($scope) {
|
||||
controllerScope = $scope;
|
||||
log.push('ctrl-init');
|
||||
};
|
||||
|
||||
module(function($routeProvider) {
|
||||
$routeProvider.when('/some', {templateUrl: '/tpl.html', controller: Ctrl});
|
||||
});
|
||||
|
||||
inject(function($route, $rootScope, $templateCache, $location) {
|
||||
$templateCache.put('/tpl.html', [200, '', {}]);
|
||||
$location.path('/some');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(controllerScope.$parent).toBe($rootScope);
|
||||
expect(controllerScope).toBe($route.current.scope);
|
||||
expect(log).toEqual(['ctrl-init']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should instantiate controller with an alias', function() {
|
||||
var log = [], controllerScope,
|
||||
Ctrl = function($scope) {
|
||||
|
||||
Reference in New Issue
Block a user