Compare commits

...

26 Commits

Author SHA1 Message Date
Matias Niemelä 1d18e60ef7 docs(CHANGELOG): add changes for 1.4.5 2015-08-28 12:06:35 -07:00
Matias Niemelä ea8016c4c8 fix(ngAnimate): use requestAnimationFrame to space out child animations
This reverts the previous behaviour of using foreced reflows to deal
with preparation classes in favour of a system that uses
requestAnimationFrame (RAF).

Closes #12669
Closes #12594
Closes #12655
Closes #12631
Closes #12612
Closes #12187
2015-08-27 16:56:18 -07:00
Matias Niemelä c3d5e33e18 fix($animate): $animate.enabled(false) should disable animations on $animateCss as well
Closes #12696
Closes #12685
2015-08-27 16:29:33 -07:00
Matias Niemelä 2f6b6fb7a1 fix($animateCss): do not throw errors when a closing timeout is fired on a removed element
Closes #12650
2015-08-27 14:07:59 -07:00
Lucas Galfaso ea2518fcea test($parse): fix csp setup
Run the parse interpreter for csp enabled tests
2015-08-26 23:24:02 +02:00
grsmvg 7e67e525a5 docs(jqLite): document unsupported event object as parameter in off()/unbind()
Closes #12291
2015-08-25 15:34:19 +03:00
Martin Staffa 0e001084ff fix(ngModel): validate pattern against the viewValue
Since the HTML5 pattern validation constraint validates the input value,
we should also validate against the viewValue. While this worked in
core up to Angular 1.2, in 1.3, we changed not only validation,
but the way `input[date]` and `input[number]` are handled - they parse
their input values into `Date` and `Number` respectively, which cannot
be validated by a regex.

Fixes #12344

BREAKING CHANGE:

The `ngPattern` and `pattern` directives will validate the regex
against the `viewValue` of `ngModel`, i.e. the value of the model
before the $parsers are applied. Previously, the modelValue
(the result of the $parsers) was validated.

This fixes issues where `input[date]` and `input[number]` cannot
be validated because the viewValue string is parsed into
`Date` and `Number` respectively (starting with Angular 1.3).
It also brings the directives in line with HTML5 constraint
validation, which validates against the input value.

This change is unlikely to cause applications to fail, because even
in Angular 1.2, the value that was validated by pattern could have
been manipulated by the $parsers, as all validation was done
inside this pipeline.

If you rely on the pattern being validated against the modelValue,
you must create your own validator directive that overwrites
the built-in pattern validator:

```
.directive('patternModelOverwrite', function patternModelOverwriteDirective() {
  return {
    restrict: 'A',
    require: '?ngModel',
    priority: 1,
    compile: function() {
      var regexp, patternExp;

      return {
        pre: function(scope, elm, attr, ctrl) {
          if (!ctrl) return;

          attr.$observe('pattern', function(regex) {
            /**
             * The built-in directive will call our overwritten validator
             * (see below). We just need to update the regex.
             * The preLink fn guaranetees our observer is called first.
             */
            if (isString(regex) && regex.length > 0) {
              regex = new RegExp('^' + regex + '$');
            }

            if (regex && !regex.test) {
              //The built-in validator will throw at this point
              return;
            }

            regexp = regex || undefined;
          });

        },
        post: function(scope, elm, attr, ctrl) {
          if (!ctrl) return;

          regexp, patternExp = attr.ngPattern || attr.pattern;

          //The postLink fn guarantees we overwrite the built-in pattern validator
          ctrl.$validators.pattern = function(value) {
            return ctrl.$isEmpty(value) ||
              isUndefined(regexp) ||
              regexp.test(value);
          };
        }
      };
    }
  };
});
```
2015-08-24 17:01:20 +02:00
Peter Bacon Darwin 85e3203918 chore(npm-shrinkwrap): update to dgeni-packages 0.10.19
See https://github.com/angular/dgeni-packages/commit/313a7c3832
2015-08-23 21:40:11 -04:00
Martin Staffa f95bc42cee docs(ngAnimate): fix typo
Closes #12521
2015-08-21 12:30:02 +02:00
Martin Staffa 9080d2c53c doc(ngMock.$controller): correct controller name in bindToController example
Closes #12550
2015-08-21 11:35:10 +02:00
Matias Niemelä 728f7e2a85 docs(ngAnimate): staggering example should contain duration:0s property
As of 1.4.4 this property needs to always be in the CSS code

Related #12594
Closes #12637
2015-08-20 20:54:31 +02:00
Matias Niemelä 5f704065a7 docs(ngShow): simplify the CSS transition code
The animation example contains unnecessarily complex CSS animation
code in it and the conventions are off.

Related #12631
2015-08-20 20:47:56 +02:00
Matias Niemelä 64631bf2e6 docs(ngAnimate): remove -webkit-transition properties
This property is no longer mandatory by browsers.
2015-08-20 20:47:55 +02:00
Karl Svartholm aa35b243f8 docs(ngAnimate): remove extraneous "then"
To improve readability

Closes #12634
2015-08-20 20:44:42 +02:00
Gabriel Monteagudo 1cc9c9ca9d fix($animateCss): fix parse errors on older Android WebViews
Errors are caused by reserved keywords 'finally' and 'catch'

Closes #12610
2015-08-20 20:39:41 +02:00
Matias Niemelä dc48aadd26 fix(ngAnimate): only buffer rAF requests within the animation runners
Closes #12280
2015-08-19 10:40:37 -07:00
Matias Niemelä ebce2f7253 revert: fix(core): ensure that multiple requests to requestAnimationFrame are buffered 2015-08-19 10:39:47 -07:00
Matias Niemelä d0e50fdcd0 docs(CHANGELOG): add changes for 1.3.18 2015-08-19 01:10:06 -07:00
Matias Niemelä d88167318d fix($animateCss): properly handle cancellation timeouts for follow-up animations
Prior to this fix if `$animateCss` was called multiple on the same
element with new animation data then the preceeding fallback timout
would cause the animation to cancel midway. This fix ensures that
`$animateCss` can be triggered multiple times and only when the final
timeout has passed then all animations will be closed.

Closes #12490
Closes #12359
2015-08-17 21:02:39 -07:00
Sreenivasan K 0a75a3db6e fix($animateCss): ensure failed animations clear the internal cache
Closes #12214
Closes #12518
Closes #12381
2015-08-17 16:06:25 -07:00
Lucas Galfaso b643f0d322 fix(ngResources): support IPv6 URLs
Do not confuse IPv6 URLs domains and resource parameters.

Closes #12512
Closes #12532
2015-08-16 12:36:56 +02:00
Martin Staffa 01dd588a28 docs(select): tweak description and add examples
- Change some wordings to make them more understandable
- Reorder the paragraphs so they can be read more easily as a coherent text
- Add examples for static single / multiple selects, and non-selected option
- Add example for select with repeated options
- Remove form-related info from ngOptions select (doesn't apply)
2015-08-15 18:32:50 +02:00
Meli 3d6dc3fe31 docs(select): explain how to set default value
Setting the default value in a select is a real trap for beginners, questions about how to do this on StackExchange have been view more than 40000 times in the last year.  This changes updates the documentation to make it clearer.

Closes #12546
2015-08-15 18:32:49 +02:00
Matias Niemelä 0c81e9fd25 fix($animateCss): the transitions options delay value should be applied before class application
When `options.delay` is passed into `$animateCss`the delay style would be
applied after the add/remove CSS classes are evaluated (for transitions).
At this point it is too late for the delay to be picked up (this
functionality however does work with keyfarme animations).

This patch ensures that the provided `options.delay` value is
applied before the CSS classes are applied to the element.

Closes #12584
2015-08-14 13:58:38 -07:00
Elvio Cavalcante 5df80e1854 docs(tutorial): fix test issue
Unnecessary split. The url returns a string without the hash,
resulting in an undefined value and making the test fails.

Matches the phonecat app more closely, too.

Closes #12590
2015-08-14 15:12:39 -04:00
David Czech ba9fb82f18 docs($animate): remove redundant 'animate' in link
Closes #12568
2015-08-14 12:08:32 +02:00
46 changed files with 1272 additions and 655 deletions
+135
View File
@@ -1,3 +1,138 @@
<a name="1.4.5"></a>
# 1.4.5 permanent-internship (2015-08-28)
## Bug Fixes
- **$animate:** `$animate.enabled(false)` should disable animations on $animateCss as well
([c3d5e33e](https://github.com/angular/angular.js/commit/c3d5e33e18bd9e423e2d0678e85564fad1dba99f),
[#12696](https://github.com/angular/angular.js/issues/12696), [#12685](https://github.com/angular/angular.js/issues/12685))
- **$animateCss:**
- do not throw errors when a closing timeout is fired on a removed element
([2f6b6fb7](https://github.com/angular/angular.js/commit/2f6b6fb7a1dee0ff97c5d2959b927347eeda6e8b),
[#12650](https://github.com/angular/angular.js/issues/12650))
- fix parse errors on older Android WebViews
([1cc9c9ca](https://github.com/angular/angular.js/commit/1cc9c9ca9d9698356ea541517b3d06ce6556c01d),
[#12610](https://github.com/angular/angular.js/issues/12610))
- properly handle cancellation timeouts for follow-up animations
([d8816731](https://github.com/angular/angular.js/commit/d88167318d1c69f0dbd2101c05955eb450c34fd5),
[#12490](https://github.com/angular/angular.js/issues/12490), [#12359](https://github.com/angular/angular.js/issues/12359))
- ensure failed animations clear the internal cache
([0a75a3db](https://github.com/angular/angular.js/commit/0a75a3db6ef265389c8c955981c2fe67bb4f7769),
[#12214](https://github.com/angular/angular.js/issues/12214), [#12518](https://github.com/angular/angular.js/issues/12518), [#12381](https://github.com/angular/angular.js/issues/12381))
- the transitions options delay value should be applied before class application
([0c81e9fd](https://github.com/angular/angular.js/commit/0c81e9fd25285dd757db98d458919776a1fb62fc),
[#12584](https://github.com/angular/angular.js/issues/12584))
- **ngAnimate:**
- use requestAnimationFrame to space out child animations
([ea8016c4](https://github.com/angular/angular.js/commit/ea8016c4c8f55bc021549f342618ed869998e335),
[#12669](https://github.com/angular/angular.js/issues/12669), [#12594](https://github.com/angular/angular.js/issues/12594), [#12655](https://github.com/angular/angular.js/issues/12655), [#12631](https://github.com/angular/angular.js/issues/12631), [#12612](https://github.com/angular/angular.js/issues/12612), [#12187](https://github.com/angular/angular.js/issues/12187))
- only buffer rAF requests within the animation runners
([dc48aadd](https://github.com/angular/angular.js/commit/dc48aadd26bbf1797c1c408f63ffde99d67414a9),
[#12280](https://github.com/angular/angular.js/issues/12280))
- **ngModel:** validate pattern against the viewValue
([0e001084](https://github.com/angular/angular.js/commit/0e001084ffff8674efad289d37cb16cc4e46b50a),
[#12344](https://github.com/angular/angular.js/issues/12344))
- **ngResources:** support IPv6 URLs
([b643f0d3](https://github.com/angular/angular.js/commit/b643f0d3223a627ef813f0777524e25d2dd95371),
[#12512](https://github.com/angular/angular.js/issues/12512), [#12532](https://github.com/angular/angular.js/issues/12532))
## Breaking Changes
- **ngModel:** due to [0e001084](https://github.com/angular/angular.js/commit/0e001084ffff8674efad289d37cb16cc4e46b50a),
The `ngPattern` and `pattern` directives will validate the regex
against the `viewValue` of `ngModel`, i.e. the value of the model
before the $parsers are applied. Previously, the modelValue
(the result of the $parsers) was validated.
This fixes issues where `input[date]` and `input[number]` cannot
be validated because the viewValue string is parsed into
`Date` and `Number` respectively (starting with Angular 1.3).
It also brings the directives in line with HTML5 constraint
validation, which validates against the input value.
This change is unlikely to cause applications to fail, because even
in Angular 1.2, the value that was validated by pattern could have
been manipulated by the $parsers, as all validation was done
inside this pipeline.
If you rely on the pattern being validated against the modelValue,
you must create your own validator directive that overwrites
the built-in pattern validator:
```js
.directive('patternModelOverwrite', function patternModelOverwriteDirective() {
return {
restrict: 'A',
require: '?ngModel',
priority: 1,
compile: function() {
var regexp, patternExp;
return {
pre: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
attr.$observe('pattern', function(regex) {
/**
* The built-in directive will call our overwritten validator
* (see below). We just need to update the regex.
* The preLink fn guaranetees our observer is called first.
*/
if (isString(regex) && regex.length > 0) {
regex = new RegExp('^' + regex + '$');
}
if (regex && !regex.test) {
//The built-in validator will throw at this point
return;
}
regexp = regex || undefined;
});
},
post: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
regexp, patternExp = attr.ngPattern || attr.pattern;
//The postLink fn guarantees we overwrite the built-in pattern validator
ctrl.$validators.pattern = function(value) {
return ctrl.$isEmpty(value) ||
isUndefined(regexp) ||
regexp.test(value);
};
}
};
}
};
});
```
<a name="1.3.18"></a>
# 1.3.18 collective-penmanship (2015-08-18)
## Bug Fixes
- **$animate:**
- clear class animations cache if animation is not started
([2c03a357](https://github.com/angular/angular.js/commit/2c03a3574336ed814d020cf7ba36cee5b87e65b5),
[#12604](https://github.com/angular/angular.js/issues/12604), [#12603](https://github.com/angular/angular.js/issues/12603))
- do not throw errors if element is removed before animation starts
([6b72598b](https://github.com/angular/angular.js/commit/6b72598b87022e1dd96bddc4451e007ef0601579),
[#10205](https://github.com/angular/angular.js/issues/10205))
- **ngModel:** correct minErr usage for correct doc creation
([64a142b5](https://github.com/angular/angular.js/commit/64a142b58ed0a0e3896d82f3f9ce35373548d0ff),
[#12386](https://github.com/angular/angular.js/issues/12386), [#12416](https://github.com/angular/angular.js/issues/12416))
<a name="1.4.4"></a>
# 1.4.4 pylon-requirement (2015-08-13)
+1
View File
@@ -93,6 +93,7 @@ var angularFiles = {
'ngAnimate': [
'src/ngAnimate/shared.js',
'src/ngAnimate/body.js',
'src/ngAnimate/rafScheduler.js',
'src/ngAnimate/animateChildrenDirective.js',
'src/ngAnimate/animateCss.js',
'src/ngAnimate/animateCssDriver.js',
+1 -1
View File
@@ -75,7 +75,7 @@ __`test/e2e/scenarios.js`__:
query.sendKeys('nexus');
element.all(by.css('.phones li a')).first().click();
browser.getLocationAbsUrl().then(function(url) {
expect(url.split('#')[1]).toBe('/phones/nexus-s');
expect(url).toBe('/phones/nexus-s');
});
});
...
+7 -7
View File
@@ -2284,7 +2284,7 @@
"version": "0.6.1",
"dependencies": {
"wordwrap": {
"version": "0.0.2"
"version": "0.0.3"
},
"minimist": {
"version": "0.0.10"
@@ -2385,7 +2385,7 @@
}
},
"dgeni-packages": {
"version": "0.10.17",
"version": "0.10.19",
"dependencies": {
"catharsis": {
"version": "0.8.7",
@@ -2518,7 +2518,7 @@
"version": "0.3.0",
"dependencies": {
"lru-cache": {
"version": "2.6.4"
"version": "2.6.5"
},
"sigmund": {
"version": "1.0.1"
@@ -2552,7 +2552,7 @@
"version": "0.2.14",
"dependencies": {
"lru-cache": {
"version": "2.6.4"
"version": "2.6.5"
},
"sigmund": {
"version": "1.0.1"
@@ -2582,10 +2582,10 @@
"version": "0.1.6"
},
"fsevents": {
"version": "0.3.6",
"version": "0.3.8",
"dependencies": {
"nan": {
"version": "1.8.4"
"version": "2.0.5"
}
}
}
@@ -2625,7 +2625,7 @@
"version": "4.3.6"
},
"shelljs": {
"version": "0.5.1"
"version": "0.5.3"
},
"winston": {
"version": "0.7.3",
+48 -48
View File
@@ -3487,96 +3487,96 @@
"dependencies": {
"dependency-graph": {
"version": "0.1.0",
"from": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.1.0.tgz",
"from": "dependency-graph@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.1.0.tgz",
"dependencies": {
"underscore": {
"version": "1.4.4",
"from": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
"from": "underscore@1.4.4",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
}
}
},
"di": {
"version": "0.0.1",
"from": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
"from": "di@0.0.1",
"resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz"
},
"optimist": {
"version": "0.6.1",
"from": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"from": "optimist@>=0.6.0 <0.7.0",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"dependencies": {
"wordwrap": {
"version": "0.0.2",
"from": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz"
"version": "0.0.3",
"from": "wordwrap@>=0.0.2 <0.1.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz"
},
"minimist": {
"version": "0.0.10",
"from": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"from": "minimist@>=0.0.1 <0.1.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz"
}
}
},
"q": {
"version": "0.9.7",
"from": "https://registry.npmjs.org/q/-/q-0.9.7.tgz",
"from": "q@>=0.9.7 <0.10.0",
"resolved": "https://registry.npmjs.org/q/-/q-0.9.7.tgz"
},
"validate.js": {
"version": "0.2.0",
"from": "https://registry.npmjs.org/validate.js/-/validate.js-0.2.0.tgz",
"from": "validate.js@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/validate.js/-/validate.js-0.2.0.tgz"
},
"winston": {
"version": "0.7.3",
"from": "https://registry.npmjs.org/winston/-/winston-0.7.3.tgz",
"from": "winston@>=0.7.2 <0.8.0",
"resolved": "https://registry.npmjs.org/winston/-/winston-0.7.3.tgz",
"dependencies": {
"async": {
"version": "0.2.10",
"from": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
"from": "async@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz"
},
"colors": {
"version": "0.6.2",
"from": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
"from": "colors@>=0.6.0 <0.7.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz"
},
"cycle": {
"version": "1.0.3",
"from": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
"from": "cycle@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz"
},
"eyes": {
"version": "0.1.8",
"from": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
"from": "eyes@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz"
},
"pkginfo": {
"version": "0.3.0",
"from": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.0.tgz",
"from": "pkginfo@>=0.3.0 <0.4.0",
"resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.0.tgz"
},
"request": {
"version": "2.16.6",
"from": "https://registry.npmjs.org/request/-/request-2.16.6.tgz",
"from": "request@>=2.16.0 <2.17.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.16.6.tgz",
"dependencies": {
"form-data": {
"version": "0.0.10",
"from": "https://registry.npmjs.org/form-data/-/form-data-0.0.10.tgz",
"from": "form-data@>=0.0.3 <0.1.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-0.0.10.tgz",
"dependencies": {
"combined-stream": {
"version": "0.0.7",
"from": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
"from": "combined-stream@>=0.0.4 <0.1.0",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
"dependencies": {
"delayed-stream": {
"version": "0.0.5",
"from": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz",
"from": "delayed-stream@0.0.5",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
}
}
@@ -3585,81 +3585,81 @@
},
"mime": {
"version": "1.2.11",
"from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz",
"from": "mime@>=1.2.7 <1.3.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
},
"hawk": {
"version": "0.10.2",
"from": "https://registry.npmjs.org/hawk/-/hawk-0.10.2.tgz",
"from": "hawk@>=0.10.2 <0.11.0",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-0.10.2.tgz",
"dependencies": {
"hoek": {
"version": "0.7.6",
"from": "https://registry.npmjs.org/hoek/-/hoek-0.7.6.tgz",
"from": "hoek@>=0.7.0 <0.8.0",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-0.7.6.tgz"
},
"boom": {
"version": "0.3.8",
"from": "https://registry.npmjs.org/boom/-/boom-0.3.8.tgz",
"from": "boom@>=0.3.0 <0.4.0",
"resolved": "https://registry.npmjs.org/boom/-/boom-0.3.8.tgz"
},
"cryptiles": {
"version": "0.1.3",
"from": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.1.3.tgz",
"from": "cryptiles@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.1.3.tgz"
},
"sntp": {
"version": "0.1.4",
"from": "https://registry.npmjs.org/sntp/-/sntp-0.1.4.tgz",
"from": "sntp@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/sntp/-/sntp-0.1.4.tgz"
}
}
},
"node-uuid": {
"version": "1.4.3",
"from": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.3.tgz",
"from": "node-uuid@>=1.4.0 <1.5.0",
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.3.tgz"
},
"cookie-jar": {
"version": "0.2.0",
"from": "https://registry.npmjs.org/cookie-jar/-/cookie-jar-0.2.0.tgz",
"from": "cookie-jar@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/cookie-jar/-/cookie-jar-0.2.0.tgz"
},
"aws-sign": {
"version": "0.2.0",
"from": "https://registry.npmjs.org/aws-sign/-/aws-sign-0.2.0.tgz",
"from": "aws-sign@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/aws-sign/-/aws-sign-0.2.0.tgz"
},
"oauth-sign": {
"version": "0.2.0",
"from": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.2.0.tgz",
"from": "oauth-sign@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.2.0.tgz"
},
"forever-agent": {
"version": "0.2.0",
"from": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.2.0.tgz",
"from": "forever-agent@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.2.0.tgz"
},
"tunnel-agent": {
"version": "0.2.0",
"from": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.2.0.tgz",
"from": "tunnel-agent@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.2.0.tgz"
},
"json-stringify-safe": {
"version": "3.0.0",
"from": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-3.0.0.tgz",
"from": "json-stringify-safe@>=3.0.0 <3.1.0",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-3.0.0.tgz"
},
"qs": {
"version": "0.5.6",
"from": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz",
"from": "qs@>=0.5.4 <0.6.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz"
}
}
},
"stack-trace": {
"version": "0.0.9",
"from": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz",
"from": "stack-trace@>=0.0.0 <0.1.0",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz"
}
}
@@ -3667,8 +3667,8 @@
}
},
"dgeni-packages": {
"version": "0.10.17",
"from": "dgeni-packages@0.10.17",
"version": "0.10.19",
"from": "dgeni-packages@0.10.19",
"dependencies": {
"catharsis": {
"version": "0.8.7",
@@ -3877,9 +3877,9 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
"dependencies": {
"lru-cache": {
"version": "2.6.4",
"version": "2.6.5",
"from": "lru-cache@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz"
},
"sigmund": {
"version": "1.0.1",
@@ -3931,9 +3931,9 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
"dependencies": {
"lru-cache": {
"version": "2.6.4",
"version": "2.6.5",
"from": "lru-cache@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz"
},
"sigmund": {
"version": "1.0.1",
@@ -3977,14 +3977,14 @@
"resolved": "https://registry.npmjs.org/async-each/-/async-each-0.1.6.tgz"
},
"fsevents": {
"version": "0.3.6",
"version": "0.3.8",
"from": "fsevents@>=0.3.1 <0.4.0",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-0.3.6.tgz",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-0.3.8.tgz",
"dependencies": {
"nan": {
"version": "1.8.4",
"from": "nan@>=1.8.0 <2.0.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz"
"version": "2.0.5",
"from": "nan@>=2.0.2 <3.0.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.0.5.tgz"
}
}
}
@@ -4042,9 +4042,9 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz"
},
"shelljs": {
"version": "0.5.1",
"version": "0.5.3",
"from": "shelljs@>=0.5.0 <0.6.0",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.1.tgz"
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz"
},
"winston": {
"version": "0.7.3",
+2 -2
View File
@@ -65,7 +65,7 @@
* - [`html()`](http://api.jquery.com/html/)
* - [`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
* - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
* - [`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/)
@@ -79,7 +79,7 @@
* - [`text()`](http://api.jquery.com/text/)
* - [`toggleClass()`](http://api.jquery.com/toggleClass/)
* - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
* - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
* - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
* - [`val()`](http://api.jquery.com/val/)
* - [`wrap()`](http://api.jquery.com/wrap/)
*
+2 -2
View File
@@ -35,10 +35,10 @@ var $CoreAnimateCssProvider = function() {
return this.getPromise().then(f1,f2);
},
'catch': function(f1) {
return this.getPromise().catch(f1);
return this.getPromise()['catch'](f1);
},
'finally': function(f1) {
return this.getPromise().finally(f1);
return this.getPromise()['finally'](f1);
}
};
-1
View File
@@ -404,7 +404,6 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
</script>
<style>
.my-form {
-webkit-transition:all linear 0.5s;
transition:all linear 0.5s;
background: transparent;
}
-1
View File
@@ -262,7 +262,6 @@ function classDirective(name, selector) {
</file>
<file name="style.css">
.base-class {
-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;
}
-1
View File
@@ -61,7 +61,6 @@
}
.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;
}
-1
View File
@@ -85,7 +85,6 @@
}
.slide-animate.ng-enter, .slide-animate.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;
position:absolute;
-1
View File
@@ -935,7 +935,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
</script>
<style>
.my-input {
-webkit-transition:all linear 0.5s;
transition:all linear 0.5s;
background: transparent;
}
-1
View File
@@ -256,7 +256,6 @@
.animate-repeat.ng-move,
.animate-repeat.ng-enter,
.animate-repeat.ng-leave {
-webkit-transition:all linear 0.5s;
transition:all linear 0.5s;
}
+1 -4
View File
@@ -125,9 +125,7 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
background: white;
}
.animate-show.ng-hide-add.ng-hide-add-active,
.animate-show.ng-hide-remove.ng-hide-remove-active {
-webkit-transition: all linear 0.5s;
.animate-show.ng-hide-add, .animate-show.ng-hide-remove {
transition: all linear 0.5s;
}
@@ -284,7 +282,6 @@ var ngShowDirective = ['$animate', function($animate) {
</file>
<file name="animations.css">
.animate-hide {
-webkit-transition: all linear 0.5s;
transition: all linear 0.5s;
line-height: 20px;
opacity: 1;
-1
View File
@@ -91,7 +91,6 @@
}
.animate-switch.ng-animate {
-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;
position:absolute;
+146 -15
View File
@@ -108,31 +108,162 @@ var SelectController =
* @description
* HTML `SELECT` element with angular data-binding.
*
* In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
* ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits such as reducing
* memory and increasing speed by not creating a new scope for each repeated instance, as well as providing
* more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
* comprehension expression.
* The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
* between the scope and the `<select>` control (including setting default values).
* Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
* {@link ngOptions `ngOptions`} directives.
*
* When an item in the `<select>` menu is selected, the array element or object property
* represented by the selected option will be bound to the model identified by the `ngModel`
* directive.
* When an item in the `<select>` menu is selected, the value of the selected option will be bound
* to the model identified by the `ngModel` directive. With static or repeated options, this is
* the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
* If you want dynamic value attributes, you can use interpolation inside the value attribute.
*
* If the viewValue contains a value that doesn't match any of the options then the control
* will automatically add an "unknown" option, which it then removes when this is resolved.
* <div class="alert alert-warning">
* Note that the value of a `select` directive used without `ngOptions` is always a string.
* When the model needs to be bound to a non-string value, you must either explictly convert it
* using a directive (see example below) or use `ngOptions` to specify the set of options.
* This is because an option element can only be bound to string values at present.
* </div>
*
* If the viewValue of `ngModel` does not match any of the options, then the control
* will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
*
* Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
* be nested into the `<select>` element. This element will then represent the `null` or "not selected"
* option. See example below for demonstration.
*
* <div class="alert alert-info">
* The value of a `select` directive used without `ngOptions` is always a string.
* When the model needs to be bound to a non-string value, you must either explictly convert it
* using a directive (see example below) or use `ngOptions` to specify the set of options.
* This is because an option element can only be bound to string values at present.
* In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
* ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
* more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
* comprehension expression, and additionally in reducing memory and increasing speed by not creating
* a new scope for each repeated instance.
* </div>
*
* ### Example (binding `select` to a non-string value)
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds required attribute and required validation constraint to
* the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
* when you want to data-bind to the required attribute.
* @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
* interaction with the select element.
* @param {string=} ngOptions sets the options that the select is populated with and defines what is
* set on the model on selection. See {@link ngOptions `ngOptions`}.
*
* @example
* ### Simple `select` elements with static options
*
* <example name="static-select" module="staticSelect">
* <file name="index.html">
* <div ng-controller="ExampleController">
* <form name="myForm">
* <label for="singleSelect"> Single select: </label><br>
* <select name="singleSelect" ng-model="data.singleSelect">
* <option value="option-1">Option 1</option>
* <option value="option-2">Option 2</option>
* </select><br>
*
* <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
* <select name="singleSelect" ng-model="data.singleSelect">
* <option value="">---Please select---</option> <!-- not selected / blank option -->
* <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
* <option value="option-2">Option 2</option>
* </select><br>
* <button ng-click="forceUnknownOption()">Force unknown option</button><br>
* <tt>singleSelect = {{data.singleSelect}}</tt>
*
* <hr>
* <label for="multipleSelect"> Multiple select: </label><br>
* <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
* <option value="option-1">Option 1</option>
* <option value="option-2">Option 2</option>
* <option value="option-3">Option 3</option>
* </select><br>
* <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
* </form>
* </div>
* </file>
* <file name="app.js">
* angular.module('staticSelect', [])
* .controller('ExampleController', ['$scope', function($scope) {
* $scope.data = {
* singleSelect: null,
* multipleSelect: [],
* option1: 'option-1',
* };
*
* $scope.forceUnknownOption = function() {
* $scope.data.singleSelect = 'nonsense';
* };
* }]);
* </file>
*</example>
*
* ### Using `ngRepeat` to generate `select` options
* <example name="ngrepeat-select" module="ngrepeatSelect">
* <file name="index.html">
* <div ng-controller="ExampleController">
* <form name="myForm">
* <label for="repeatSelect"> Repeat select: </label>
* <select name="repeatSelect" ng-model="data.repeatSelect">
* <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
* </select>
* </form>
* <hr>
* <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
* </div>
* </file>
* <file name="app.js">
* angular.module('ngrepeatSelect', [])
* .controller('ExampleController', ['$scope', function($scope) {
* $scope.data = {
* singleSelect: null,
* availableOptions: [
* {id: '1', name: 'Option A'},
* {id: '2', name: 'Option B'},
* {id: '3', name: 'Option C'}
* ],
* };
* }]);
* </file>
*</example>
*
*
* ### Using `select` with `ngOptions` and setting a default value
* See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
*
* <example name="select-with-default-values" module="defaultValueSelect">
* <file name="index.html">
* <div ng-controller="ExampleController">
* <form name="myForm">
* <label for="mySelect">Make a choice:</label>
* <select name="mySelect" id="mySelect"
* ng-options="option.name for option in data.availableOptions track by option.id"
* ng-model="data.selectedOption"></select>
* </form>
* <hr>
* <tt>option = {{data.selectedOption}}</tt><br/>
* </div>
* </file>
* <file name="app.js">
* angular.module('defaultValueSelect', [])
* .controller('ExampleController', ['$scope', function($scope) {
* $scope.data = {
* availableOptions: [
* {id: '1', name: 'Option A'},
* {id: '2', name: 'Option B'},
* {id: '3', name: 'Option C'}
* ],
* selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
* };
* }]);
* </file>
*</example>
*
*
* ### Binding `select` to a non-string value via `ngModel` parsing / formatting
*
* <example name="select-with-non-string-options" module="nonStringSelect">
* <file name="index.html">
+3 -2
View File
@@ -43,8 +43,9 @@ var patternDirective = function() {
ctrl.$validate();
});
ctrl.$validators.pattern = function(value) {
return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
ctrl.$validators.pattern = function(modelValue, viewValue) {
// HTML5 pattern constraint validates the input value, so we validate the viewValue
return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
};
}
};
+3 -41
View File
@@ -10,7 +10,7 @@ function $$RAFProvider() { //rAF
$window.webkitCancelRequestAnimationFrame;
var rafSupported = !!requestAnimationFrame;
var rafFn = rafSupported
var raf = rafSupported
? function(fn) {
var id = requestAnimationFrame(fn);
return function() {
@@ -24,46 +24,8 @@ function $$RAFProvider() { //rAF
};
};
queueFn.supported = rafSupported;
raf.supported = rafSupported;
var cancelLastRAF;
var taskCount = 0;
var taskQueue = [];
return queueFn;
function flush() {
for (var i = 0; i < taskQueue.length; i++) {
var task = taskQueue[i];
if (task) {
taskQueue[i] = null;
task();
}
}
taskCount = taskQueue.length = 0;
}
function queueFn(asyncFn) {
var index = taskQueue.length;
taskCount++;
taskQueue.push(asyncFn);
if (index === 0) {
cancelLastRAF = rafFn(flush);
}
return function cancelQueueFn() {
if (index >= 0) {
taskQueue[index] = null;
index = null;
if (--taskCount === 0 && cancelLastRAF) {
cancelLastRAF();
cancelLastRAF = null;
taskQueue.length = 0;
}
}
};
}
return raf;
}];
}
+67 -35
View File
@@ -1,5 +1,7 @@
'use strict';
var ANIMATE_TIMER_KEY = '$$animateCss';
/**
* @ngdoc service
* @name $animateCss
@@ -326,8 +328,10 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
var gcsLookup = createLocalCacheLookup();
var gcsStaggerLookup = createLocalCacheLookup();
this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout', '$$forceReflow', '$sniffer', '$$rAF',
function($window, $$jqLite, $$AnimateRunner, $timeout, $$forceReflow, $sniffer, $$rAF) {
this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout',
'$$forceReflow', '$sniffer', '$$rAFScheduler', '$animate',
function($window, $$jqLite, $$AnimateRunner, $timeout,
$$forceReflow, $sniffer, $$rAFScheduler, $animate) {
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
@@ -387,12 +391,8 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
var cancelLastRAFRequest;
var rafWaitQueue = [];
function waitUntilQuiet(callback) {
if (cancelLastRAFRequest) {
cancelLastRAFRequest(); //cancels the request
}
rafWaitQueue.push(callback);
cancelLastRAFRequest = $$rAF(function() {
cancelLastRAFRequest = null;
$$rAFScheduler.waitUntilQuiet(function() {
gcsLookup.flush();
gcsStaggerLookup.flush();
@@ -409,8 +409,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
});
}
return init;
function computeTimings(node, className, cacheKey) {
var timings = computeCachedCssStyles(node, className, cacheKey, DETECT_CSS_PROPERTIES);
var aD = timings.animationDelay;
@@ -425,9 +423,11 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return timings;
}
function init(element, options) {
return function init(element, options) {
var node = getDomNode(element);
if (!node || !node.parentNode) {
if (!node
|| !node.parentNode
|| !$animate.enabled()) {
return closeAndReturnNoopAnimator();
}
@@ -483,7 +483,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
// there actually is a detected transition or keyframe animation
if (options.applyClassesEarly && addRemoveClassName.length) {
applyAnimationClasses(element, options);
addRemoveClassName = '';
}
var preparationClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
@@ -598,6 +597,18 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return closeAndReturnNoopAnimator();
}
if (options.delay != null) {
var delayStyle = parseFloat(options.delay);
if (flags.applyTransitionDelay) {
temporaryStyles.push(getCssDelayStyle(delayStyle));
}
if (flags.applyAnimationDelay) {
temporaryStyles.push(getCssDelayStyle(delayStyle, true));
}
}
// we need to recalculate the delay value since we used a pre-emptive negative
// delay value and the delay value is required for the final event checking. This
// property will ensure that this will happen after the RAF phase has passed.
@@ -712,6 +723,8 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
cancel: cancelFn
});
// should flush the cache animation
waitUntilQuiet(noop);
close();
return {
@@ -809,27 +822,16 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
flags.hasAnimations = timings.animationDuration > 0;
}
if (flags.applyTransitionDelay || flags.applyAnimationDelay) {
if (flags.applyAnimationDelay) {
relativeDelay = typeof options.delay !== "boolean" && truthyTimingValue(options.delay)
? parseFloat(options.delay)
: relativeDelay;
maxDelay = Math.max(relativeDelay, 0);
var delayStyle;
if (flags.applyTransitionDelay) {
timings.transitionDelay = relativeDelay;
delayStyle = getCssDelayStyle(relativeDelay);
temporaryStyles.push(delayStyle);
node.style[delayStyle[0]] = delayStyle[1];
}
if (flags.applyAnimationDelay) {
timings.animationDelay = relativeDelay;
delayStyle = getCssDelayStyle(relativeDelay, true);
temporaryStyles.push(delayStyle);
node.style[delayStyle[0]] = delayStyle[1];
}
timings.animationDelay = relativeDelay;
delayStyle = getCssDelayStyle(relativeDelay, true);
temporaryStyles.push(delayStyle);
node.style[delayStyle[0]] = delayStyle[1];
}
maxDelayTime = maxDelay * ONE_SECOND;
@@ -858,17 +860,47 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
}
startTime = Date.now();
element.on(events.join(' '), onAnimationProgress);
$timeout(onAnimationExpired, maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime, false);
var timerTime = maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime;
var endTime = startTime + timerTime;
var animationsData = element.data(ANIMATE_TIMER_KEY) || [];
var setupFallbackTimer = true;
if (animationsData.length) {
var currentTimerData = animationsData[0];
setupFallbackTimer = endTime > currentTimerData.expectedEndTime;
if (setupFallbackTimer) {
$timeout.cancel(currentTimerData.timer);
} else {
animationsData.push(close);
}
}
if (setupFallbackTimer) {
var timer = $timeout(onAnimationExpired, timerTime, false);
animationsData[0] = {
timer: timer,
expectedEndTime: endTime
};
animationsData.push(close);
element.data(ANIMATE_TIMER_KEY, animationsData);
}
element.on(events.join(' '), onAnimationProgress);
applyAnimationToStyles(element, options);
}
function onAnimationExpired() {
// although an expired animation is a failed animation, getting to
// this outcome is very easy if the CSS code screws up. Therefore we
// should still continue normally as if the animation completed correctly.
close();
var animationsData = element.data(ANIMATE_TIMER_KEY);
// this will be false in the event that the element was
// removed from the DOM (via a leave animation or something
// similar)
if (animationsData) {
for (var i = 1; i < animationsData.length; i++) {
animationsData[i]();
}
element.removeData(ANIMATE_TIMER_KEY);
}
}
function onAnimationProgress(event) {
@@ -895,6 +927,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
}
}
}
}
};
}];
}];
+5 -17
View File
@@ -22,13 +22,13 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
return function initDriverFn(animationDetails, onBeforeClassesAppliedCb) {
return function initDriverFn(animationDetails) {
return animationDetails.from && animationDetails.to
? prepareFromToAnchorAnimation(animationDetails.from,
animationDetails.to,
animationDetails.classes,
animationDetails.anchors)
: prepareRegularAnimation(animationDetails, onBeforeClassesAppliedCb);
: prepareRegularAnimation(animationDetails);
};
function filterCssClasses(classes) {
@@ -224,21 +224,14 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
};
}
function prepareRegularAnimation(animationDetails, onBeforeClassesAppliedCb) {
function prepareRegularAnimation(animationDetails) {
var element = animationDetails.element;
var options = animationDetails.options || {};
// since the ng-EVENT, class-ADD and class-REMOVE classes are applied inside
// of the animateQueue pre and postDigest stages then there is no need to add
// then them here as well.
options.$$skipPreparationClasses = true;
// during the pre/post digest stages inside of animateQueue we also performed
// the blocking (transition:-9999s) so there is no point in doing that again.
options.skipBlocking = true;
if (animationDetails.structural) {
options.event = animationDetails.event;
options.structural = true;
options.applyClassesEarly = true;
// we special case the leave animation since we want to ensure that
// the element is removed as soon as the animation is over. Otherwise
@@ -248,11 +241,6 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
}
}
// we apply the classes right away since the pre-digest took care of the
// preparation classes.
onBeforeClassesAppliedCb(element);
applyAnimationClasses(element, options);
// We assign the preparationClasses as the actual animation event since
// the internals of $animateCss will just suffix the event token values
// with `-active` to trigger the animation.
+2 -2
View File
@@ -5,8 +5,8 @@
// by the time...
var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
this.$get = ['$injector', '$$AnimateRunner', '$$rAFMutex', '$$jqLite',
function($injector, $$AnimateRunner, $$rAFMutex, $$jqLite) {
this.$get = ['$injector', '$$AnimateRunner', '$$jqLite',
function($injector, $$AnimateRunner, $$jqLite) {
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
// $animateJs(element, 'enter');
+1 -7
View File
@@ -381,9 +381,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return runner;
}
applyGeneratedPreparationClasses(element, isStructural ? event : null, options);
blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
// the counter keeps track of cancelled animations
var counter = (existingAnimation.counter || 0) + 1;
newAnimation.counter = counter;
@@ -442,10 +439,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
: animationDetails.event;
markElementAnimationState(element, RUNNING_STATE);
var realRunner = $$animation(element, event, animationDetails.options, function(e) {
$$forceReflow();
blockTransitions(getDomNode(e), false);
});
var realRunner = $$animation(element, event, animationDetails.options);
realRunner.done(function(status) {
close(!status);
+21 -6
View File
@@ -1,18 +1,33 @@
'use strict';
var $$rAFMutexFactory = ['$$rAF', function($$rAF) {
var $$AnimateAsyncRunFactory = ['$$rAF', function($$rAF) {
var waitQueue = [];
function waitForTick(fn) {
waitQueue.push(fn);
if (waitQueue.length > 1) return;
$$rAF(function() {
for (var i = 0; i < waitQueue.length; i++) {
waitQueue[i]();
}
waitQueue = [];
});
}
return function() {
var passed = false;
$$rAF(function() {
waitForTick(function() {
passed = true;
});
return function(fn) {
passed ? fn() : $$rAF(fn);
return function(callback) {
passed ? callback() : waitForTick(callback);
};
};
}];
var $$AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) {
var $$AnimateRunnerFactory = ['$q', '$sniffer', '$$animateAsyncRun',
function($q, $sniffer, $$animateAsyncRun) {
var INITIAL_STATE = 0;
var DONE_PENDING_STATE = 1;
var DONE_COMPLETE_STATE = 2;
@@ -57,7 +72,7 @@ var $$AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) {
this.setHost(host);
this._doneCallbacks = [];
this._runInAnimationFrame = $$rAFMutex();
this._runInAnimationFrame = $$animateAsyncRun();
this._state = 0;
}
+15 -17
View File
@@ -19,8 +19,8 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
return element.data(RUNNER_STORAGE_KEY);
}
this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap',
function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$HashMap) {
this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler',
function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$HashMap, $$rAFScheduler) {
var animationQueue = [];
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
@@ -88,11 +88,11 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
if (remainingLevelEntries <= 0) {
remainingLevelEntries = nextLevelEntries;
nextLevelEntries = 0;
result = result.concat(row);
result.push(row);
row = [];
}
row.push(entry.fn);
forEach(entry.children, function(childEntry) {
entry.children.forEach(function(childEntry) {
nextLevelEntries++;
queue.push(childEntry);
});
@@ -100,14 +100,15 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
}
if (row.length) {
result = result.concat(row);
result.push(row);
}
return result;
}
}
// TODO(matsko): document the signature in a better way
return function(element, event, options, onBeforeClassesAppliedCb) {
return function(element, event, options) {
options = prepareAnimationOptions(options);
var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;
@@ -159,8 +160,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
// the element was destroyed early on which removed the runner
// form its storage. This means we can't animate this element
// at all and it already has been closed due to destruction.
var elm = entry.element;
if (getRunner(elm) && getDomNode(elm).parentNode) {
if (getRunner(entry.element)) {
animations.push(entry);
} else {
entry.close();
@@ -191,7 +191,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
: animationEntry.element;
if (getRunner(targetElement)) {
var operation = invokeFirstDriver(animationEntry, onBeforeClassesAppliedCb);
var operation = invokeFirstDriver(animationEntry);
if (operation) {
startAnimationFn = operation.start;
}
@@ -211,11 +211,9 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
});
// we need to sort each of the animations in order of parent to child
// relationships. This ensures that the parent to child classes are
// applied at the right time.
forEach(sortAnimations(toBeSortedAnimations), function(triggerAnimation) {
triggerAnimation();
});
// relationships. This ensures that the child classes are applied at the
// right time.
$$rAFScheduler(sortAnimations(toBeSortedAnimations));
});
return runner;
@@ -285,7 +283,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
var lookupKey = from.animationID.toString();
if (!anchorGroups[lookupKey]) {
var group = anchorGroups[lookupKey] = {
// TODO(matsko): double-check this code
structural: true,
beforeStart: function() {
fromAnimation.beforeStart();
toAnimation.beforeStart();
@@ -339,7 +337,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
return matches.join(' ');
}
function invokeFirstDriver(animationDetails, onBeforeClassesAppliedCb) {
function invokeFirstDriver(animationDetails) {
// we loop in reverse order since the more general drivers (like CSS and JS)
// may attempt more elements, but custom drivers are more particular
for (var i = drivers.length - 1; i >= 0; i--) {
@@ -347,7 +345,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
if (!$injector.has(driverName)) continue; // TODO(matsko): remove this check
var factory = $injector.get(driverName);
var driver = factory(animationDetails, onBeforeClassesAppliedCb);
var driver = factory(animationDetails);
if (driver) {
return driver;
}
+9 -8
View File
@@ -3,7 +3,8 @@
/* global angularAnimateModule: true,
$$BodyProvider,
$$rAFMutexFactory,
$$AnimateAsyncRunFactory,
$$rAFSchedulerFactory,
$$AnimateChildrenDirective,
$$AnimateRunnerFactory,
$$AnimateQueueProvider,
@@ -20,7 +21,7 @@
* @description
*
* The `ngAnimate` module provides support for CSS-based animations (keyframes and transitions) as well as JavaScript-based animations via
* callback hooks. Animations are not enabled by default, however, by including `ngAnimate` then the animation hooks are enabled for an Angular app.
* callback hooks. Animations are not enabled by default, however, by including `ngAnimate` the animation hooks are enabled for an Angular app.
*
* <div doc-module-components="ngAnimate"></div>
*
@@ -53,7 +54,7 @@
* CSS-based animations with ngAnimate are unique since they require no JavaScript code at all. By using a CSS class that we reference between our HTML
* and CSS code we can create an animation that will be picked up by Angular when an the underlying directive performs an operation.
*
* The example below shows how an `enter` animation can be made possible on a element using `ng-if`:
* The example below shows how an `enter` animation can be made possible on an element using `ng-if`:
*
* ```html
* <div ng-if="bool" class="fade">
@@ -188,8 +189,8 @@
* /&#42; this will have a 100ms delay between each successive leave animation &#42;/
* transition-delay: 0.1s;
*
* /&#42; in case the stagger doesn't work then the duration value
* must be set to 0 to avoid an accidental CSS inheritance &#42;/
* /&#42; As of 1.4.4, this must always be set: it signals ngAnimate
* to not accidentally inherit a delay property from another CSS class &#42;/
* transition-duration: 0s;
* }
* .my-animation.ng-enter.ng-enter-active {
@@ -738,16 +739,16 @@
* @description
* The ngAnimate `$animate` service documentation is the same for the core `$animate` service.
*
* Click here {@link ng.$animate $animate to learn more about animations with `$animate`}.
* Click here {@link ng.$animate to learn more about animations with `$animate`}.
*/
angular.module('ngAnimate', [])
.provider('$$body', $$BodyProvider)
.directive('ngAnimateChildren', $$AnimateChildrenDirective)
.factory('$$rAFMutex', $$rAFMutexFactory)
.factory('$$rAFScheduler', $$rAFSchedulerFactory)
.factory('$$AnimateRunner', $$AnimateRunnerFactory)
.factory('$$animateAsyncRun', $$AnimateAsyncRunFactory)
.provider('$$animateQueue', $$AnimateQueueProvider)
.provider('$$animation', $$AnimationProvider)
+50
View File
@@ -0,0 +1,50 @@
'use strict';
var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
var queue, cancelFn;
function scheduler(tasks) {
// we make a copy since RAFScheduler mutates the state
// of the passed in array variable and this would be difficult
// to track down on the outside code
queue = queue.concat(tasks);
nextTick();
}
queue = scheduler.queue = [];
/* waitUntilQuiet does two things:
* 1. It will run the FINAL `fn` value only when an uncancelled RAF has passed through
* 2. It will delay the next wave of tasks from running until the quiet `fn` has run.
*
* The motivation here is that animation code can request more time from the scheduler
* before the next wave runs. This allows for certain DOM properties such as classes to
* be resolved in time for the next animation to run.
*/
scheduler.waitUntilQuiet = function(fn) {
if (cancelFn) cancelFn();
cancelFn = $$rAF(function() {
cancelFn = null;
fn();
nextTick();
});
};
return scheduler;
function nextTick() {
if (!queue.length) return;
var items = queue.shift();
for (var i = 0; i < items.length; i++) {
items[i]();
}
if (!cancelFn) {
$$rAF(function() {
if (!cancelFn) nextTick();
});
}
}
}];
+45 -20
View File
@@ -763,25 +763,50 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
return reflowFn;
});
$provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$$forceReflow',
function($delegate, $timeout, $browser, $$rAF, $$forceReflow) {
$provide.factory('$$animateAsyncRun', function() {
var queue = [];
var queueFn = function() {
return function(fn) {
queue.push(fn);
};
};
queueFn.flush = function() {
if (queue.length === 0) return false;
for (var i = 0; i < queue.length; i++) {
queue[i]();
}
queue = [];
return true;
};
return queueFn;
});
$provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$$forceReflow', '$$animateAsyncRun',
function($delegate, $timeout, $browser, $$rAF, $$forceReflow, $$animateAsyncRun) {
var animate = {
queue: [],
cancel: $delegate.cancel,
on: $delegate.on,
off: $delegate.off,
pin: $delegate.pin,
get reflows() {
return $$forceReflow.totalReflows;
},
enabled: $delegate.enabled,
triggerCallbackEvents: function() {
$$rAF.flush();
},
triggerCallbackPromise: function() {
$timeout.flush(0);
},
triggerCallbacks: function() {
this.triggerCallbackEvents();
this.triggerCallbackPromise();
flush: function() {
var rafsFlushed = false;
if ($$rAF.queue.length) {
$$rAF.flush();
rafsFlushed = true;
}
var animatorsFlushed = $$animateAsyncRun.flush();
if (!rafsFlushed && !animatorsFlushed) {
throw new Error('No pending animations ready to be closed or flushed');
}
}
};
@@ -1733,28 +1758,28 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
}];
angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
var queue = [];
var rafFn = function(fn) {
var index = queue.length;
queue.push(fn);
var index = rafFn.queue.length;
rafFn.queue.push(fn);
return function() {
queue.splice(index, 1);
rafFn.queue.splice(index, 1);
};
};
rafFn.queue = [];
rafFn.supported = $delegate.supported;
rafFn.flush = function() {
if (queue.length === 0) {
if (rafFn.queue.length === 0) {
throw new Error('No rAF callbacks present');
}
var length = queue.length;
var length = rafFn.queue.length;
for (var i = 0; i < length; i++) {
queue[i]();
rafFn.queue[i]();
}
queue = queue.slice(i);
rafFn.queue = rafFn.queue.slice(i);
};
return rafFn;
@@ -1802,7 +1827,7 @@ angular.mock.$RootElementProvider = function() {
*
* describe('myDirectiveController', function() {
* it('should write the bound name to the log', inject(function($controller, $log) {
* var ctrl = $controller('MyDirective', { /* no locals &#42;/ }, { name: 'Clark Kent' });
* var ctrl = $controller('MyDirectiveController', { /* no locals &#42;/ }, { name: 'Clark Kent' });
* expect(ctrl.name).toEqual('Clark Kent');
* expect($log.info.logs).toEqual(['Clark Kent']);
* });
+8 -2
View File
@@ -348,6 +348,7 @@ function shallowClearAndCopy(src, dst) {
*/
angular.module('ngResource', ['ng']).
provider('$resource', function() {
var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
var provider = this;
this.defaults = {
@@ -422,7 +423,8 @@ angular.module('ngResource', ['ng']).
var self = this,
url = actionUrl || self.template,
val,
encodedVal;
encodedVal,
protocolAndDomain = '';
var urlParams = self.urlParams = {};
forEach(url.split(/\W/), function(param) {
@@ -435,6 +437,10 @@ angular.module('ngResource', ['ng']).
}
});
url = url.replace(/\\:/g, ':');
url = url.replace(PROTOCOL_AND_DOMAIN_REGEX, function(match) {
protocolAndDomain = match;
return '';
});
params = params || {};
forEach(self.urlParams, function(_, urlParam) {
@@ -465,7 +471,7 @@ angular.module('ngResource', ['ng']).
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
// replace escaped `/\.` with `/.`
config.url = url.replace(/\/\\\./, '/.');
config.url = protocolAndDomain + url.replace(/\/\\\./, '/.');
// set params - delegate param encoding to $http
-1
View File
@@ -90,7 +90,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
}
.view-animate.ng-enter, .view-animate.ng-leave {
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
display:block;
+2 -2
View File
@@ -468,7 +468,7 @@ describe('ngClass animations', function() {
};
});
});
inject(function($compile, $rootScope, $browser, $rootElement, $animate, $timeout, $$body, $$rAF) {
inject(function($compile, $rootScope, $browser, $rootElement, $animate, $timeout, $$body) {
$animate.enabled(true);
$rootScope.val = 'crazy';
@@ -488,7 +488,7 @@ describe('ngClass animations', function() {
expect(enterComplete).toBe(false);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
$rootScope.$digest();
expect(element.hasClass('crazy')).toBe(true);
+10 -8
View File
@@ -428,9 +428,11 @@ describe('ngInclude', function() {
});
expect(autoScrollSpy).not.toHaveBeenCalled();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
expect(autoScrollSpy).toHaveBeenCalledOnce();
}));
@@ -446,7 +448,6 @@ describe('ngInclude', function() {
});
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$rootScope.$apply(function() {
$rootScope.tpl = 'another.html';
@@ -455,7 +456,6 @@ describe('ngInclude', function() {
expect($animate.queue.shift().event).toBe('leave');
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$rootScope.$apply(function() {
$rootScope.tpl = 'template.html';
@@ -464,7 +464,9 @@ describe('ngInclude', function() {
expect($animate.queue.shift().event).toBe('leave');
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$digest();
expect(autoScrollSpy).toHaveBeenCalled();
expect(autoScrollSpy.callCount).toBe(3);
@@ -480,7 +482,6 @@ describe('ngInclude', function() {
});
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
expect(autoScrollSpy).not.toHaveBeenCalled();
}));
@@ -496,7 +497,6 @@ describe('ngInclude', function() {
});
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$rootScope.$apply(function() {
$rootScope.tpl = 'template.html';
@@ -518,7 +518,9 @@ describe('ngInclude', function() {
$rootScope.$apply("tpl = 'template.html'");
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$digest();
expect(autoScrollSpy).toHaveBeenCalledOnce();
}
+2 -2
View File
@@ -1462,7 +1462,7 @@ describe('ngRepeat animations', function() {
}));
it('should not change the position of the block that is being animated away via a leave animation',
inject(function($compile, $rootScope, $animate, $document, $window, $sniffer, $timeout, $$rAF) {
inject(function($compile, $rootScope, $animate, $document, $window, $sniffer, $timeout) {
if (!$sniffer.transitions) return;
var item;
@@ -1487,7 +1487,7 @@ describe('ngRepeat animations', function() {
$rootScope.$digest();
expect(element.text()).toBe('123'); // the original order should be preserved
$$rAF.flush();
$animate.flush();
$timeout.flush(1500); // 1s * 1.5 closing buffer
expect(element.text()).toBe('13');
} finally {
+15
View File
@@ -204,6 +204,21 @@ describe('validators', function() {
expect($rootScope.form.test.$error.pattern).toBe(true);
expect(inputElm).not.toBeValid();
});
it('should validate the viewValue and not the modelValue', function() {
var inputElm = helper.compileInput('<input type="text" name="test" ng-model="value" pattern="\\d{4}">');
var ctrl = inputElm.controller('ngModel');
ctrl.$parsers.push(function(value) {
return (value * 10) + '';
});
helper.changeInputValueTo('1234');
expect($rootScope.form.test.$error.pattern).not.toBe(true);
expect($rootScope.form.test.$modelValue).toBe('12340');
expect(inputElm).toBeValid();
});
});
+5 -5
View File
@@ -1681,8 +1681,9 @@ describe('parser', function() {
beforeEach(module(function($provide) {
$provide.decorator('$sniffer', function($delegate) {
$delegate.csp = cspEnabled;
return $delegate;
expect($delegate.csp.noUnsafeEval === true ||
$delegate.csp.noUnsafeEval === false).toEqual(true);
$delegate.csp.noUnsafeEval = cspEnabled;
});
}, provideLog));
@@ -2120,9 +2121,8 @@ describe('parser', function() {
expect(scope.$eval('items[1] = "abc"')).toEqual("abc");
expect(scope.$eval('items[1]')).toEqual("abc");
// Dont know how to make this work....
// expect(scope.$eval('books[1] = "moby"')).toEqual("moby");
// expect(scope.$eval('books[1]')).toEqual("moby");
expect(scope.$eval('books[1] = "moby"')).toEqual("moby");
expect(scope.$eval('books[1]')).toEqual("moby");
});
it('should evaluate grouped filters', function() {
-40
View File
@@ -31,46 +31,6 @@ describe('$$rAF', function() {
expect(present).toBe(true);
}));
it('should only consume only one RAF if multiple async functions are registered before the first frame kicks in', inject(function($$rAF) {
if (!$$rAF.supported) return;
//we need to create our own injector to work around the ngMock overrides
var rafLog = [];
var injector = createInjector(['ng', function($provide) {
$provide.value('$window', {
location: window.location,
history: window.history,
webkitRequestAnimationFrame: function(fn) {
rafLog.push(fn);
}
});
}]);
$$rAF = injector.get('$$rAF');
var log = [];
function logFn() {
log.push(log.length);
}
$$rAF(logFn);
$$rAF(logFn);
$$rAF(logFn);
expect(log).toEqual([]);
expect(rafLog.length).toBe(1);
rafLog[0]();
expect(log).toEqual([0,1,2]);
expect(rafLog.length).toBe(1);
$$rAF(logFn);
expect(log).toEqual([0,1,2]);
expect(rafLog.length).toBe(2);
}));
describe('$timeout fallback', function() {
it("it should use a $timeout incase native rAF isn't suppored", function() {
var timeoutSpy = jasmine.createSpy('callback');
+20 -28
View File
@@ -3,6 +3,7 @@
describe("ngAnimate $$animateCssDriver", function() {
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));
function int(x) {
return parseInt(x, 10);
@@ -104,7 +105,7 @@ describe("ngAnimate $$animateCssDriver", function() {
expect(capturedAnimation[1].applyClassesEarly).toBeFalsy();
driver({ element: element, structural: true });
expect(capturedAnimation[1].applyClassesEarly).toBeFalsy();
expect(capturedAnimation[1].applyClassesEarly).toBeTruthy();
}));
it("should only set the event value if the animation is structural", inject(function() {
@@ -414,7 +415,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should then do an addClass('ng-anchor-in') animation on the cloned anchor and remove the old class",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
var fromAnchor = jqLite('<div></div>');
from.append(fromAnchor);
@@ -434,7 +435,6 @@ describe("ngAnimate $$animateCssDriver", function() {
}).start();
captureLog.pop().runner.end();
$$rAF.flush();
var anchorDetails = captureLog.pop().args[1];
expect(anchorDetails.removeClass.trim()).toBe('ng-anchor-out');
@@ -464,7 +464,7 @@ describe("ngAnimate $$animateCssDriver", function() {
});
});
inject(function($rootElement, $$rAF) {
inject(function($rootElement, $animate) {
var fromAnchor = jqLite('<div></div>');
from.append(fromAnchor);
var toAnchor = jqLite('<div></div>');
@@ -488,7 +488,7 @@ describe("ngAnimate $$animateCssDriver", function() {
expect(animationStarted).toBe(expectedClass);
runner.end();
$$rAF.flush();
$animate.flush();
expect(complete).toBe(true);
});
});
@@ -552,7 +552,7 @@ describe("ngAnimate $$animateCssDriver", function() {
expect(int(fromStyles.left)).toBeGreaterThan(149);
}));
it("should append a `px` value for all seeded animation styles", inject(function($rootElement, $$rAF) {
it("should append a `px` value for all seeded animation styles", inject(function($rootElement) {
ss.addRule('.starting-element', 'width:10px; height:20px; display:inline-block;');
var fromAnchor = jqLite('<div class="starting-element"' +
@@ -582,7 +582,6 @@ describe("ngAnimate $$animateCssDriver", function() {
// the out animation goes first
anchorAnimation.runner.end();
$$rAF.flush();
anchorAnimation = captureLog.pop();
anchorDetails = anchorAnimation.args[1];
@@ -593,7 +592,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should then do an removeClass('out') + addClass('in') animation on the cloned anchor",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
var fromAnchor = jqLite('<div></div>');
from.append(fromAnchor);
@@ -614,7 +613,6 @@ describe("ngAnimate $$animateCssDriver", function() {
// the out animation goes first
captureLog.pop().runner.end();
$$rAF.flush();
var anchorDetails = captureLog.pop().args[1];
expect(anchorDetails.removeClass).toMatch(/\bout\b/);
@@ -623,7 +621,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should add the `ng-anchor` class to the cloned anchor element",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
var fromAnchor = jqLite('<div></div>');
from.append(fromAnchor);
@@ -647,7 +645,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should add and remove the `ng-animate-shim` class on the in anchor element during the animation",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
var fromAnchor = jqLite('<div></div>');
from.append(fromAnchor);
@@ -670,14 +668,13 @@ describe("ngAnimate $$animateCssDriver", function() {
// the out animation goes first
captureLog.pop().runner.end();
$$rAF.flush();
captureLog.pop().runner.end();
expect(fromAnchor).not.toHaveClass('ng-animate-shim');
}));
it("should add and remove the `ng-animate-shim` class on the out anchor element during the animation",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
var fromAnchor = jqLite('<div></div>');
from.append(fromAnchor);
@@ -700,7 +697,6 @@ describe("ngAnimate $$animateCssDriver", function() {
// the out animation goes first
captureLog.pop().runner.end();
$$rAF.flush();
expect(toAnchor).toHaveClass('ng-animate-shim');
captureLog.pop().runner.end();
@@ -709,7 +705,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should create the cloned anchor with all of the classes from the from anchor element",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
var fromAnchor = jqLite('<div class="yes no maybe"></div>');
from.append(fromAnchor);
@@ -733,7 +729,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should remove the classes of the starting anchor from the cloned anchor node during the in animation and also add the classes of the destination anchor within the same animation",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
var fromAnchor = jqLite('<div class="yes no maybe"></div>');
from.append(fromAnchor);
@@ -754,7 +750,6 @@ describe("ngAnimate $$animateCssDriver", function() {
// the out animation goes first
captureLog.pop().runner.end();
$$rAF.flush();
var anchorDetails = captureLog.pop().args[1];
var removedClasses = anchorDetails.removeClass.split(' ');
@@ -765,7 +760,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should not attempt to add/remove any classes that contain a `ng-` prefix",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
var fromAnchor = jqLite('<div class="ng-yes ng-no sure"></div>');
from.append(fromAnchor);
@@ -786,7 +781,6 @@ describe("ngAnimate $$animateCssDriver", function() {
// the out animation goes first
captureLog.pop().runner.end();
$$rAF.flush();
var inAnimation = captureLog.pop();
var details = inAnimation.args[1];
@@ -802,7 +796,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should not remove any shared CSS classes between the starting and destination anchor element during the in animation",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
var fromAnchor = jqLite('<div class="blue green red"></div>');
from.append(fromAnchor);
@@ -823,7 +817,6 @@ describe("ngAnimate $$animateCssDriver", function() {
// the out animation goes first
captureLog.pop().runner.end();
$$rAF.flush();
var inAnimation = captureLog.pop();
var clonedAnchor = inAnimation.element;
@@ -851,7 +844,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should continue the anchor animation by seeding the to styles based on where the final anchor element will be positioned",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
ss.addRule('.ending-element', 'width:9999px; height:6666px; display:inline-block;');
var fromAnchor = jqLite('<div></div>');
@@ -874,7 +867,6 @@ describe("ngAnimate $$animateCssDriver", function() {
}).start();
captureLog.pop().runner.end();
$$rAF.flush();
var anchorAnimation = captureLog.pop();
var anchorElement = anchorAnimation.element;
@@ -889,7 +881,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should remove the cloned anchor node from the DOM once the 'in' animation is complete",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
var fromAnchor = jqLite('<div class="blue green red"></div>');
from.append(fromAnchor);
@@ -913,7 +905,6 @@ describe("ngAnimate $$animateCssDriver", function() {
var clonedAnchor = inAnimation.element;
expect(clonedAnchor.parent().length).toBe(1);
inAnimation.runner.end();
$$rAF.flush();
// now the in animation completes
expect(clonedAnchor.parent().length).toBe(1);
@@ -923,7 +914,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should pass the provided domOperation into $animateCss to be run right after the element is animated if a leave animation is present",
inject(function($rootElement, $$rAF) {
inject(function($rootElement) {
toAnimation.structural = true;
toAnimation.event = 'enter';
@@ -949,7 +940,7 @@ describe("ngAnimate $$animateCssDriver", function() {
}));
it("should fire the returned runner promise when the from, to and anchor animations are all complete",
inject(function($rootElement, $rootScope, $$rAF) {
inject(function($rootElement, $rootScope, $animate) {
ss.addRule('.ending-element', 'width:9999px; height:6666px; display:inline-block;');
@@ -978,7 +969,8 @@ describe("ngAnimate $$animateCssDriver", function() {
captureLog.pop().runner.end(); //to
captureLog.pop().runner.end(); //anchor(out)
captureLog.pop().runner.end(); //anchor(in)
$$rAF.flush();
$animate.flush();
$rootScope.$digest();
expect(completed).toBe(true);
+194 -41
View File
@@ -3,6 +3,7 @@
describe("ngAnimate $animateCss", function() {
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));
function assertAnimationRunning(element, not) {
var className = element.attr('class');
@@ -17,9 +18,10 @@ describe("ngAnimate $animateCss", function() {
var ss, prefix, triggerAnimationStartFrame;
beforeEach(module(function() {
return function($document, $window, $sniffer, $$rAF) {
return function($document, $window, $sniffer, $$rAF, $animate) {
prefix = '-' + $sniffer.vendorPrefix.toLowerCase() + '-';
ss = createMockStyleSheet($document, $window);
$animate.enabled(true);
triggerAnimationStartFrame = function() {
$$rAF.flush();
};
@@ -51,8 +53,25 @@ describe("ngAnimate $animateCss", function() {
describe('when active', function() {
if (!browserSupportsCssAnimations()) return;
it("should not attempt an animation if animations are globally disabled",
inject(function($animateCss, $animate, $rootElement, $$body) {
$animate.enabled(false);
var animator, element = jqLite('<div></div>');
$rootElement.append(element);
$$body.append($rootElement);
animator = $animateCss(element, {
duration: 10,
to: { 'height': '100px' }
});
expect(animator.$$willAnimate).toBeFalsy();
}));
it("should silently quit the animation and not throw when an element has no parent during preparation",
inject(function($animateCss, $$rAF, $rootScope, $document, $rootElement) {
inject(function($animateCss, $rootScope, $document, $rootElement) {
var element = jqLite('<div></div>');
expect(function() {
@@ -385,7 +404,7 @@ describe("ngAnimate $animateCss", function() {
they("should close the animation, but still accept $prop callbacks if no animation is detected",
['done', 'then'], function(method) {
inject(function($animateCss, $$rAF, $rootScope) {
inject(function($animateCss, $animate, $rootScope) {
ss.addRule('.the-third-fake-animation', 'background:green;');
element.addClass('another-fake-animation');
@@ -400,7 +419,8 @@ describe("ngAnimate $animateCss", function() {
});
expect(done).toBe(false);
$$rAF.flush();
$animate.flush();
if (method === 'then') {
$rootScope.$digest();
}
@@ -411,7 +431,7 @@ describe("ngAnimate $animateCss", function() {
they("should close the animation, but still accept recognize runner.$prop if no animation is detected",
['done(cancel)', 'catch'], function(method) {
inject(function($animateCss, $$rAF, $rootScope) {
inject(function($animateCss, $rootScope) {
ss.addRule('.the-third-fake-animation', 'background:green;');
element.addClass('another-fake-animation');
@@ -1078,7 +1098,7 @@ describe("ngAnimate $animateCss", function() {
}));
it("should still resolve the animation once expired",
inject(function($animateCss, $$body, $rootElement, $timeout) {
inject(function($animateCss, $$body, $rootElement, $timeout, $animate, $rootScope) {
ss.addRule('.ng-enter', 'transition:10s linear all;');
@@ -1097,11 +1117,13 @@ describe("ngAnimate $animateCss", function() {
triggerAnimationStartFrame();
$timeout.flush(15000);
$animate.flush();
$rootScope.$digest();
expect(passed).toBe(true);
}));
it("should not resolve/reject after passing if the animation completed successfully",
inject(function($animateCss, $$body, $rootElement, $timeout, $rootScope) {
inject(function($animateCss, $$body, $rootElement, $timeout, $rootScope, $animate) {
ss.addRule('.ng-enter', 'transition:10s linear all;');
@@ -1125,6 +1147,7 @@ describe("ngAnimate $animateCss", function() {
browserTrigger(element, 'transitionend',
{ timeStamp: Date.now() + 1000, elapsedTime: 10 });
$animate.flush();
$rootScope.$digest();
expect(passed).toBe(true);
@@ -1135,6 +1158,82 @@ describe("ngAnimate $animateCss", function() {
expect(passed).toBe(true);
expect(failed).not.toBe(true);
}));
it("should close all stacked animations after the last timeout runs on the same element",
inject(function($animateCss, $$body, $rootElement, $timeout, $animate) {
var now = 0;
spyOn(Date, 'now').andCallFake(function() {
return now;
});
var cancelSpy = spyOn($timeout, 'cancel').andCallThrough();
var doneSpy = jasmine.createSpy();
ss.addRule('.elm', 'transition:1s linear all;');
ss.addRule('.elm.red', 'background:red;');
ss.addRule('.elm.blue', 'transition:2s linear all; background:blue;');
ss.addRule('.elm.green', 'background:green;');
var element = jqLite('<div class="elm"></div>');
$rootElement.append(element);
$$body.append($rootElement);
// timeout will be at 1500s
animate(element, 'red', doneSpy);
expect(doneSpy).not.toHaveBeenCalled();
fastForwardClock(500); //1000s left to go
// timeout will not be at 500 + 3000s = 3500s
animate(element, 'blue', doneSpy);
expect(doneSpy).not.toHaveBeenCalled();
expect(cancelSpy).toHaveBeenCalled();
cancelSpy.reset();
// timeout will not be set again since the former animation is longer
animate(element, 'green', doneSpy);
expect(doneSpy).not.toHaveBeenCalled();
expect(cancelSpy).not.toHaveBeenCalled();
// this will close the animations fully
fastForwardClock(3500);
$animate.flush();
expect(doneSpy).toHaveBeenCalled();
expect(doneSpy.callCount).toBe(3);
function fastForwardClock(time) {
now += time;
$timeout.flush(time);
}
function animate(element, klass, onDone) {
var animator = $animateCss(element, { addClass: klass }).start();
animator.done(onDone);
triggerAnimationStartFrame();
return animator;
}
}));
it("should not throw an error any pending timeout requests resolve after the element has already been removed",
inject(function($animateCss, $$body, $rootElement, $timeout, $animate) {
var element = jqLite('<div></div>');
$rootElement.append(element);
$$body.append($rootElement);
ss.addRule('.red', 'transition:1s linear all;');
$animateCss(element, { addClass: 'red' }).start();
triggerAnimationStartFrame();
element.remove();
expect(function() {
$timeout.flush();
}).not.toThrow();
}));
});
describe("getComputedStyle", function() {
@@ -1162,7 +1261,7 @@ describe("ngAnimate $animateCss", function() {
}));
it("should cache frequent calls to getComputedStyle before the next animation frame kicks in",
inject(function($animateCss, $document, $rootElement, $$rAF) {
inject(function($animateCss, $document, $rootElement) {
var i, elm, animator;
for (i = 0; i < 5; i++) {
@@ -1277,6 +1376,25 @@ describe("ngAnimate $animateCss", function() {
expect(element.attr('style')).not.toContain('transition');
}));
it("should clear cache if no animation so follow-up animation on the same element will not be from cache",
inject(function($animateCss, $rootElement, $$body, $$rAF) {
var element = jqLite('<div class="rclass"></div>');
var options = {
event: 'enter',
structural: true
};
$rootElement.append(element);
$$body.append($rootElement);
var animator = $animateCss(element, options);
expect(animator.$$willAnimate).toBeFalsy();
$$rAF.flush();
ss.addRule('.ng-enter', '-webkit-animation:3.5s keyframe_animation;' +
'animation:3.5s keyframe_animation;');
animator = $animateCss(element, options);
expect(animator.$$willAnimate).toBeTruthy();
}));
it('should apply a custom temporary class when a non-structural animation is used',
inject(function($animateCss, $rootElement, $$body) {
@@ -1671,11 +1789,13 @@ describe("ngAnimate $animateCss", function() {
describe("options", function() {
var element;
beforeEach(inject(function($rootElement, $$body) {
$$body.append($rootElement);
beforeEach(module(function() {
return function($rootElement, $$body) {
$$body.append($rootElement);
element = jqLite('<div></div>');
$rootElement.append(element);
element = jqLite('<div></div>');
$rootElement.append(element);
};
}));
describe("[$$skipPreparationClasses]", function() {
@@ -1862,7 +1982,6 @@ describe("ngAnimate $animateCss", function() {
animator.start();
triggerAnimationStartFrame();
var prop = element.css('transition-delay');
expect(prop).toEqual('500s');
}));
@@ -1978,6 +2097,53 @@ describe("ngAnimate $animateCss", function() {
expect(element.css('transition-delay')).toEqual('10s');
}));
it("should apply the keyframe and transition duration value before the CSS classes are applied", function() {
var classSpy = jasmine.createSpy();
module(function($provide) {
$provide.value('$$jqLite', {
addClass: function() {
classSpy();
},
removeClass: function() {
classSpy();
}
});
});
inject(function($animateCss, $rootElement) {
element.addClass('element');
ss.addRule('.element', '-webkit-animation:3s keyframe_animation;' +
'animation:3s keyframe_animation;' +
'transition:5s linear all;');
var options = {
delay: 2,
duration: 2,
addClass: 'superman',
$$skipPreparationClasses: true,
structural: true
};
var animator = $animateCss(element, options);
expect(element.attr('style') || '').not.toContain('animation-delay');
expect(element.attr('style') || '').not.toContain('transition-delay');
expect(classSpy).not.toHaveBeenCalled();
//redefine the classSpy to assert that the delay values have been
//applied before the classes are added
var assertionsRun = false;
classSpy = function() {
assertionsRun = true;
expect(element.css(prefix + 'animation-delay')).toEqual('2s');
expect(element.css('transition-delay')).toEqual('2s');
expect(element).not.toHaveClass('superman');
};
animator.start();
triggerAnimationStartFrame();
expect(assertionsRun).toBe(true);
});
});
it("should apply blocking before the animation starts, but then apply the detected delay when options.delay is true",
inject(function($animateCss, $rootElement) {
@@ -1995,41 +2161,28 @@ describe("ngAnimate $animateCss", function() {
animator.start();
triggerAnimationStartFrame();
expect(element.css('transition-delay')).toEqual('1s');
expect(element.attr('style') || '').not.toContain('transition-delay');
}));
they("should consider a negative value when delay:true is used with a $prop animation", {
'transition': function() {
return {
prop: 'transition-delay',
css: 'transition:2s linear all; transition-delay: -1s'
};
},
'keyframe': function(prefix) {
return {
prop: prefix + 'animation-delay',
css: prefix + 'animation:2s keyframe_animation; ' + prefix + 'animation-delay: -1s;'
};
}
}, function(testDetailsFactory) {
it("should consider a negative value when delay:true is used with a keyframe animation",
inject(function($animateCss, $rootElement) {
var testDetails = testDetailsFactory(prefix);
ss.addRule('.ng-enter', testDetails.css);
var options = {
delay: true,
event: 'enter',
structural: true
};
ss.addRule('.ng-enter', prefix + 'animation:2s keyframe_animation; ' +
prefix + 'animation-delay: -1s;');
var animator = $animateCss(element, options);
var options = {
delay: true,
event: 'enter',
structural: true
};
animator.start();
triggerAnimationStartFrame();
var animator = $animateCss(element, options);
expect(element.css(testDetails.prop)).toContain('-1s');
});
});
animator.start();
triggerAnimationStartFrame();
expect(element.css(prefix + 'animation-delay')).toContain('-1s');
}));
they("should consider a negative value when a negative option delay is provided for a $prop animation", {
'transition': function() {
+6 -5
View File
@@ -3,6 +3,7 @@
describe("ngAnimate $$animateJsDriver", function() {
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));
it('should register the $$animateJsDriver into the list of drivers found in $animateProvider',
module(function($animateProvider) {
@@ -96,7 +97,7 @@ describe("ngAnimate $$animateJsDriver", function() {
}));
they('should $prop both animations when $prop() is called on the runner', ['end', 'cancel'], function(method) {
inject(function($rootScope, $$rAF) {
inject(function($rootScope, $animate) {
var child1 = jqLite('<div></div>');
element.append(child1);
var child2 = jqLite('<div></div>');
@@ -127,7 +128,7 @@ describe("ngAnimate $$animateJsDriver", function() {
$rootScope.$digest();
runner[method]();
$$rAF.flush();
$animate.flush();
expect(animationsClosed).toBe(true);
expect(status).toBe(method === 'end' ? true : false);
@@ -135,7 +136,7 @@ describe("ngAnimate $$animateJsDriver", function() {
});
they('should fully $prop when all inner animations are complete', ['end', 'cancel'], function(method) {
inject(function($rootScope, $$rAF) {
inject(function($rootScope, $animate) {
var child1 = jqLite('<div></div>');
element.append(child1);
var child2 = jqLite('<div></div>');
@@ -163,12 +164,12 @@ describe("ngAnimate $$animateJsDriver", function() {
status = s;
});
$$rAF.flush();
captureLog[0].runner[method]();
expect(animationsClosed).toBe(false);
captureLog[1].runner[method]();
$animate.flush();
expect(animationsClosed).toBe(true);
expect(status).toBe(method === 'end' ? true : false);
+35 -34
View File
@@ -3,6 +3,7 @@
describe("ngAnimate $$animateJs", function() {
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));
function getDoneFunction(args) {
for (var i = 1; i < args.length; i++) {
@@ -86,7 +87,7 @@ describe("ngAnimate $$animateJs", function() {
$animateProvider.register('.two', makeAnimation('enter'));
$animateProvider.register('.three', makeAnimation('enter'));
});
inject(function($$animateJs, $$rAF) {
inject(function($$animateJs, $animate) {
var element = jqLite('<div class="one two three"></div>');
var animator = $$animateJs(element, 'enter');
var complete = false;
@@ -97,7 +98,7 @@ describe("ngAnimate $$animateJs", function() {
forEach(doneCallbacks, function(cb) {
cb();
});
$$rAF.flush();
$animate.flush();
expect(complete).toBe(true);
});
});
@@ -206,7 +207,7 @@ describe("ngAnimate $$animateJs", function() {
};
});
});
inject(function($$animateJs, $$rAF) {
inject(function($$animateJs, $animate) {
var element = jqLite('<div class="the-end"></div>');
var animator = $$animateJs(element, 'addClass', {
addClass: 'red'
@@ -221,12 +222,12 @@ describe("ngAnimate $$animateJs", function() {
before();
expect(after).toBeUndefined();
$$rAF.flush();
$animate.flush();
expect(after).toBeDefined();
after();
expect(endCalled).toBeUndefined();
$$rAF.flush();
$animate.flush();
expect(endCalled).toBe(true);
});
});
@@ -251,7 +252,7 @@ describe("ngAnimate $$animateJs", function() {
};
});
});
inject(function($$animateJs, $$rAF) {
inject(function($$animateJs, $animate) {
var element = jqLite('<div class="the-end"></div>');
var animator = $$animateJs(element, 'addClass', {
domOperation: function() {
@@ -264,7 +265,7 @@ describe("ngAnimate $$animateJs", function() {
});
runner[method]();
$$rAF.flush();
$animate.flush();
expect(log).toEqual(
['before addClass ' + method,
'dom addClass',
@@ -278,7 +279,7 @@ describe("ngAnimate $$animateJs", function() {
return { beforeAddClass: noop };
});
});
inject(function($$animateJs, $$rAF, $rootScope) {
inject(function($$animateJs, $animate, $rootScope) {
var element = jqLite('<div class="the-end"></div>');
var animator = $$animateJs(element, 'addClass');
var runner = animator.start();
@@ -291,7 +292,7 @@ describe("ngAnimate $$animateJs", function() {
});
runner.end();
$$rAF.flush();
$animate.flush();
$rootScope.$digest();
expect(done).toBe(true);
expect(cancelled).toBe(false);
@@ -304,7 +305,7 @@ describe("ngAnimate $$animateJs", function() {
return { beforeAddClass: noop };
});
});
inject(function($$animateJs, $$rAF, $rootScope) {
inject(function($$animateJs, $animate, $rootScope) {
var element = jqLite('<div class="the-end"></div>');
var animator = $$animateJs(element, 'addClass');
var runner = animator.start();
@@ -317,7 +318,7 @@ describe("ngAnimate $$animateJs", function() {
});
runner.cancel();
$$rAF.flush();
$animate.flush();
$rootScope.$digest();
expect(done).toBe(false);
expect(cancelled).toBe(true);
@@ -504,7 +505,7 @@ describe("ngAnimate $$animateJs", function() {
var allEvents = ['leave'].concat(otherEvents).concat(enterMoveEvents);
they("$prop should asynchronously render the before$prop animation", otherEvents, function(event) {
inject(function($$rAF) {
inject(function($animate) {
var beforeMethod = 'before' + event.charAt(0).toUpperCase() + event.substr(1);
animations[beforeMethod] = function(element, a, b, c) {
log.push('before ' + event);
@@ -514,14 +515,14 @@ describe("ngAnimate $$animateJs", function() {
runAnimation(event);
expect(log).toEqual(['before ' + event]);
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['before ' + event, 'dom ' + event]);
});
});
they("$prop should asynchronously render the $prop animation", allEvents, function(event) {
inject(function($$rAF) {
inject(function($animate) {
animations[event] = function(element, a, b, c) {
log.push('after ' + event);
var done = getDoneFunction(arguments);
@@ -534,11 +535,11 @@ describe("ngAnimate $$animateJs", function() {
if (event === 'leave') {
expect(log).toEqual(['after leave']);
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['after leave', 'dom leave', 'complete']);
} else {
expect(log).toEqual(['dom ' + event, 'after ' + event]);
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['dom ' + event, 'after ' + event, 'complete']);
}
});
@@ -547,7 +548,7 @@ describe("ngAnimate $$animateJs", function() {
they("$prop should asynchronously render the $prop animation when a start/end animator object is returned",
allEvents, function(event) {
inject(function($$rAF, $$AnimateRunner) {
inject(function($animate, $$AnimateRunner) {
var runner;
animations[event] = function(element, a, b, c) {
return {
@@ -565,12 +566,12 @@ describe("ngAnimate $$animateJs", function() {
if (event === 'leave') {
expect(log).toEqual(['start leave']);
runner.end();
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['start leave', 'dom leave', 'complete']);
} else {
expect(log).toEqual(['dom ' + event, 'start ' + event]);
runner.end();
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['dom ' + event, 'start ' + event, 'complete']);
}
});
@@ -579,7 +580,7 @@ describe("ngAnimate $$animateJs", function() {
they("$prop should asynchronously render the $prop animation when an instance of $$AnimateRunner is returned",
allEvents, function(event) {
inject(function($$rAF, $$AnimateRunner) {
inject(function($animate, $$AnimateRunner) {
var runner;
animations[event] = function(element, a, b, c) {
log.push('start ' + event);
@@ -593,19 +594,19 @@ describe("ngAnimate $$animateJs", function() {
if (event === 'leave') {
expect(log).toEqual(['start leave']);
runner.end();
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['start leave', 'dom leave', 'complete']);
} else {
expect(log).toEqual(['dom ' + event, 'start ' + event]);
runner.end();
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['dom ' + event, 'start ' + event, 'complete']);
}
});
});
they("$prop should asynchronously reject the before animation if the callback function is called with false", otherEvents, function(event) {
inject(function($$rAF, $rootScope) {
inject(function($animate, $rootScope) {
var beforeMethod = 'before' + event.charAt(0).toUpperCase() + event.substr(1);
animations[beforeMethod] = function(element, a, b, c) {
log.push('before ' + event);
@@ -624,13 +625,13 @@ describe("ngAnimate $$animateJs", function() {
function() { log.push('fail'); });
expect(log).toEqual(['before ' + event]);
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['before ' + event, 'dom ' + event, 'fail']);
});
});
they("$prop should asynchronously reject the after animation if the callback function is called with false", allEvents, function(event) {
inject(function($$rAF, $rootScope) {
inject(function($animate, $rootScope) {
animations[event] = function(element, a, b, c) {
log.push('after ' + event);
var done = getDoneFunction(arguments);
@@ -644,17 +645,17 @@ describe("ngAnimate $$animateJs", function() {
var expectations = [];
if (event === 'leave') {
expect(log).toEqual(['after leave']);
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['after leave', 'dom leave', 'fail']);
} else {
expect(log).toEqual(['dom ' + event, 'after ' + event]);
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['dom ' + event, 'after ' + event, 'fail']);
}
});
});
it('setClass should delegate down to addClass/removeClass if not defined', inject(function($$rAF) {
it('setClass should delegate down to addClass/removeClass if not defined', inject(function($animate) {
animations.addClass = function(element, done) {
log.push('addClass');
};
@@ -671,7 +672,7 @@ describe("ngAnimate $$animateJs", function() {
}));
it('beforeSetClass should delegate down to beforeAddClass/beforeRemoveClass if not defined',
inject(function($$rAF) {
inject(function($animate) {
animations.beforeAddClass = function(element, className, done) {
log.push('beforeAddClass');
@@ -686,13 +687,13 @@ describe("ngAnimate $$animateJs", function() {
expect(animations.setClass).toBeFalsy();
runAnimation('setClass');
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['beforeRemoveClass', 'beforeAddClass', 'dom setClass']);
}));
it('leave should always ignore the `beforeLeave` animation',
inject(function($$rAF) {
inject(function($animate) {
animations.beforeLeave = function(element, done) {
log.push('beforeLeave');
@@ -705,13 +706,13 @@ describe("ngAnimate $$animateJs", function() {
};
runAnimation('leave');
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['leave', 'dom leave']);
}));
it('should allow custom events to be triggered',
inject(function($$rAF) {
inject(function($animate) {
animations.beforeFlex = function(element, done) {
log.push('beforeFlex');
@@ -724,7 +725,7 @@ describe("ngAnimate $$animateJs", function() {
};
runAnimation('flex');
$$rAF.flush();
$animate.flush();
expect(log).toEqual(['beforeFlex', 'dom flex', 'flex']);
}));
+8 -8
View File
@@ -1,12 +1,12 @@
'use strict';
describe('$$rAFMutex', function() {
describe('$$animateAsyncRun', function() {
beforeEach(module('ngAnimate'));
it('should fire the callback only when one or more RAFs have passed',
inject(function($$rAF, $$rAFMutex) {
inject(function($$animateAsyncRun, $$rAF) {
var trigger = $$rAFMutex();
var trigger = $$animateAsyncRun();
var called = false;
trigger(function() {
called = true;
@@ -18,9 +18,9 @@ describe('$$rAFMutex', function() {
}));
it('should immediately fire the callback if a RAF has passed since construction',
inject(function($$rAF, $$rAFMutex) {
inject(function($$animateAsyncRun, $$rAF) {
var trigger = $$rAFMutex();
var trigger = $$animateAsyncRun();
$$rAF.flush();
var called = false;
@@ -89,7 +89,7 @@ describe("$$AnimateRunner", function() {
they("should immediately resolve each combined runner in a bottom-up order when $prop is called",
['end', 'cancel'], function(method) {
inject(function($$AnimateRunner, $$rAF) {
inject(function($$AnimateRunner) {
var runner1 = new $$AnimateRunner();
var runner2 = new $$AnimateRunner();
runner1.setHost(runner2);
@@ -206,7 +206,7 @@ describe("$$AnimateRunner", function() {
they("should immediately resolve if and when all runners have been $prop",
{ ended: 'end', cancelled: 'cancel' }, function(method) {
inject(function($$rAF, $$AnimateRunner) {
inject(function($$AnimateRunner) {
var runner1 = new $$AnimateRunner();
var runner2 = new $$AnimateRunner();
var runner3 = new $$AnimateRunner();
@@ -227,7 +227,7 @@ describe("$$AnimateRunner", function() {
});
it("should return a status of `false` if one or more runners was cancelled",
inject(function($$rAF, $$AnimateRunner) {
inject(function($$AnimateRunner) {
var runner1 = new $$AnimateRunner();
var runner2 = new $$AnimateRunner();
+48 -116
View File
@@ -3,6 +3,7 @@
describe("animations", function() {
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));
var element, applyAnimationClasses;
afterEach(inject(function($$jqLite) {
@@ -410,7 +411,7 @@ describe("animations", function() {
}));
it('should remove all element and comment nodes during leave animation',
inject(function($compile, $rootScope, $$rAF, $$AnimateRunner) {
inject(function($compile, $rootScope, $animate, $$AnimateRunner) {
element = $compile(
'<div>' +
@@ -433,7 +434,7 @@ describe("animations", function() {
$rootScope.items.length = 0;
$rootScope.$digest();
runner.end();
$$rAF.flush();
$animate.flush();
// we're left with a text node and a comment node
expect(element[0].childNodes.length).toBeLessThan(3);
@@ -491,74 +492,6 @@ describe("animations", function() {
expect(element).not.toHaveClass('green');
}));
they('should apply the $prop CSS class to the element before digest for the given event and remove when complete',
{'ng-enter': 'enter', 'ng-leave': 'leave', 'ng-move': 'move'}, function(event) {
inject(function($animate, $rootScope, $document, $rootElement) {
$animate.enabled(true);
var element = jqLite('<div></div>');
var parent = jqLite('<div></div>');
$rootElement.append(parent);
jqLite($document[0].body).append($rootElement);
var runner;
if (event === 'leave') {
parent.append(element);
runner = $animate[event](element);
} else {
runner = $animate[event](element, parent);
}
var expectedClassName = 'ng-' + event;
expect(element).toHaveClass(expectedClassName);
$rootScope.$digest();
expect(element).toHaveClass(expectedClassName);
runner.end();
expect(element).not.toHaveClass(expectedClassName);
dealoc(parent);
});
});
they('should add CSS classes with the $prop suffix when depending on the event and remove when complete',
{'-add': 'add', '-remove': 'remove'}, function(event) {
inject(function($animate, $rootScope, $document, $rootElement) {
$animate.enabled(true);
var element = jqLite('<div></div>');
$rootElement.append(element);
jqLite($document[0].body).append($rootElement);
var classes = 'one two';
var expectedClasses = ['one-',event,' ','two-', event].join('');
var runner;
if (event === 'add') {
runner = $animate.addClass(element, classes);
} else {
element.addClass(classes);
runner = $animate.removeClass(element, classes);
}
expect(element).toHaveClass(expectedClasses);
$rootScope.$digest();
expect(element).toHaveClass(expectedClasses);
runner.end();
expect(element).not.toHaveClass(expectedClasses);
dealoc(element);
});
});
they('$prop() should operate using a native DOM element',
['enter', 'move', 'leave', 'addClass', 'removeClass', 'setClass', 'animate'], function(event) {
@@ -671,7 +604,7 @@ describe("animations", function() {
they('should not cancel a pre-digest parent class-based animation if a child $prop animation is set to run',
['structural', 'class-based'], function(animationType) {
inject(function($rootScope, $animate, $$rAF) {
inject(function($rootScope, $animate) {
parent.append(element);
var child = jqLite('<div></div>');
@@ -692,7 +625,7 @@ describe("animations", function() {
they('should not cancel a post-digest parent class-based animation if a child $prop animation is set to run',
['structural', 'class-based'], function(animationType) {
inject(function($rootScope, $animate, $$rAF) {
inject(function($rootScope, $animate) {
parent.append(element);
var child = jqLite('<div></div>');
@@ -717,7 +650,7 @@ describe("animations", function() {
they('should not cancel a post-digest $prop child animation if a class-based parent animation is set to run',
['structural', 'class-based'], function(animationType) {
inject(function($rootScope, $animate, $$rAF) {
inject(function($rootScope, $animate) {
parent.append(element);
var child = jqLite('<div></div>');
@@ -815,8 +748,8 @@ describe("animations", function() {
expect(capturedAnimation).toBeFalsy();
}));
it("should disable all child animations for atleast one RAF when a structural animation is issued",
inject(function($animate, $rootScope, $compile, $$body, $rootElement, $$rAF, $$AnimateRunner) {
it("should disable all child animations for atleast one turn when a structural animation is issued",
inject(function($animate, $rootScope, $compile, $$body, $rootElement, $$AnimateRunner) {
element = $compile(
'<div><div class="if-animation" ng-if="items.length">' +
@@ -847,7 +780,7 @@ describe("animations", function() {
expect(element[0].querySelectorAll('.repeat-animation').length).toBe(2);
runner.end();
$$rAF.flush();
$animate.flush();
$rootScope.items = [1, 2, 3];
$rootScope.$digest();
@@ -946,7 +879,7 @@ describe("animations", function() {
}));
it('should allow follow-up class-based animations to run in parallel on the same element',
inject(function($rootScope, $animate, $$rAF) {
inject(function($rootScope, $animate) {
parent.append(element);
@@ -957,7 +890,7 @@ describe("animations", function() {
});
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(capturedAnimation).toBeTruthy();
expect(runner1done).toBeFalsy();
@@ -977,7 +910,7 @@ describe("animations", function() {
});
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(capturedAnimation).toBeTruthy();
expect(runner2done).toBeFalsy();
@@ -990,7 +923,7 @@ describe("animations", function() {
}));
it('should remove the animation block on child animations once the parent animation is complete',
inject(function($rootScope, $rootElement, $animate, $$AnimateRunner, $$rAF) {
inject(function($rootScope, $rootElement, $animate, $$AnimateRunner) {
var runner = new $$AnimateRunner();
overriddenAnimationRunner = runner;
@@ -1005,7 +938,7 @@ describe("animations", function() {
expect(capturedAnimationHistory.length).toBe(1);
runner.end();
$$rAF.flush();
$animate.flush();
$animate.addClass(element, 'stark');
$rootScope.$digest();
@@ -1036,19 +969,19 @@ describe("animations", function() {
}));
it('should cancel the previous structural animation if a follow-up structural animation takes over before the postDigest',
inject(function($animate, $$rAF) {
inject(function($animate) {
var enterDone = jasmine.createSpy('enter animation done');
$animate.enter(element, parent).done(enterDone);
expect(enterDone).not.toHaveBeenCalled();
$animate.leave(element);
$$rAF.flush();
$animate.flush();
expect(enterDone).toHaveBeenCalled();
}));
it('should cancel the previously running addClass animation if a follow-up removeClass animation is using the same class value',
inject(function($animate, $rootScope, $$rAF) {
inject(function($animate, $rootScope) {
parent.append(element);
var runner = $animate.addClass(element, 'active-class');
@@ -1057,7 +990,7 @@ describe("animations", function() {
var doneHandler = jasmine.createSpy('addClass done');
runner.done(doneHandler);
$$rAF.flush();
$animate.flush();
expect(doneHandler).not.toHaveBeenCalled();
@@ -1068,7 +1001,7 @@ describe("animations", function() {
}));
it('should cancel the previously running removeClass animation if a follow-up addClass animation is using the same class value',
inject(function($animate, $rootScope, $$rAF) {
inject(function($animate, $rootScope) {
element.addClass('active-class');
parent.append(element);
@@ -1078,7 +1011,7 @@ describe("animations", function() {
var doneHandler = jasmine.createSpy('addClass done');
runner.done(doneHandler);
$$rAF.flush();
$animate.flush();
expect(doneHandler).not.toHaveBeenCalled();
@@ -1294,7 +1227,7 @@ describe("animations", function() {
}));
it('should not skip or miss the animations when animations are executed sequential',
inject(function($animate, $rootScope, $$rAF, $rootElement) {
inject(function($animate, $rootScope, $rootElement) {
element = jqLite('<div></div>');
@@ -1306,8 +1239,6 @@ describe("animations", function() {
$animate.removeClass(element, 'rclass');
$rootScope.$digest();
$$rAF.flush();
expect(element).not.toHaveClass('rclass');
}));
});
@@ -1532,7 +1463,7 @@ describe("animations", function() {
}));
it('should trigger a callback for an enter animation',
inject(function($animate, $rootScope, $$rAF, $rootElement, $$body) {
inject(function($animate, $rootScope, $rootElement, $$body) {
var callbackTriggered = false;
$animate.on('enter', $$body, function() {
@@ -1543,13 +1474,13 @@ describe("animations", function() {
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(callbackTriggered).toBe(true);
}));
it('should fire the callback with the signature of (element, phase, data)',
inject(function($animate, $rootScope, $$rAF, $rootElement, $$body) {
inject(function($animate, $rootScope, $rootElement, $$body) {
var capturedElement;
var capturedPhase;
@@ -1565,7 +1496,7 @@ describe("animations", function() {
element = jqLite('<div></div>');
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(capturedElement).toBe(element);
expect(isString(capturedPhase)).toBe(true);
@@ -1573,7 +1504,7 @@ describe("animations", function() {
}));
it('should not fire a callback if the element is outside of the given container',
inject(function($animate, $rootScope, $$rAF, $rootElement) {
inject(function($animate, $rootScope, $rootElement) {
var callbackTriggered = false;
var innerContainer = jqLite('<div></div>');
@@ -1588,13 +1519,13 @@ describe("animations", function() {
element = jqLite('<div></div>');
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(callbackTriggered).toBe(false);
}));
it('should fire a callback if the element is the given container',
inject(function($animate, $rootScope, $$rAF, $rootElement) {
inject(function($animate, $rootScope, $rootElement) {
element = jqLite('<div></div>');
@@ -1607,13 +1538,13 @@ describe("animations", function() {
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(callbackTriggered).toBe(true);
}));
it('should remove all the event-based event listeners when $animate.off(event) is called',
inject(function($animate, $rootScope, $$rAF, $rootElement, $$body) {
inject(function($animate, $rootScope, $rootElement, $$body) {
element = jqLite('<div></div>');
@@ -1627,7 +1558,7 @@ describe("animations", function() {
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(count).toBe(2);
@@ -1635,13 +1566,13 @@ describe("animations", function() {
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(count).toBe(2);
}));
it('should remove the container-based event listeners when $animate.off(event, container) is called',
inject(function($animate, $rootScope, $$rAF, $rootElement, $$body) {
inject(function($animate, $rootScope, $rootElement, $$body) {
element = jqLite('<div></div>');
@@ -1657,7 +1588,7 @@ describe("animations", function() {
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(count).toBe(2);
@@ -1665,13 +1596,13 @@ describe("animations", function() {
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(count).toBe(3);
}));
it('should remove the callback-based event listener when $animate.off(event, container, callback) is called',
inject(function($animate, $rootScope, $$rAF, $rootElement) {
inject(function($animate, $rootScope, $rootElement) {
element = jqLite('<div></div>');
@@ -1693,7 +1624,7 @@ describe("animations", function() {
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(count).toBe(2);
@@ -1701,13 +1632,13 @@ describe("animations", function() {
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(count).toBe(3);
}));
it('should fire a `start` callback when the animation starts with the matching element',
inject(function($animate, $rootScope, $$rAF, $rootElement, $$body) {
inject(function($animate, $rootScope, $rootElement, $$body) {
element = jqLite('<div></div>');
@@ -1720,14 +1651,14 @@ describe("animations", function() {
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(capturedState).toBe('start');
expect(capturedElement).toBe(element);
}));
it('should fire a `close` callback when the animation ends with the matching element',
inject(function($animate, $rootScope, $$rAF, $rootElement, $$body) {
inject(function($animate, $rootScope, $rootElement, $$body) {
element = jqLite('<div></div>');
@@ -1741,14 +1672,14 @@ describe("animations", function() {
var runner = $animate.enter(element, $rootElement);
$rootScope.$digest();
runner.end();
$$rAF.flush();
$animate.flush();
expect(capturedState).toBe('close');
expect(capturedElement).toBe(element);
}));
it('should remove the event listener if the element is removed',
inject(function($animate, $rootScope, $$rAF, $rootElement) {
inject(function($animate, $rootScope, $rootElement) {
element = jqLite('<div></div>');
@@ -1764,19 +1695,20 @@ describe("animations", function() {
$animate.enter(element, $rootElement);
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(count).toBe(1);
element.remove();
$animate.addClass(element, 'viljami');
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
expect(count).toBe(1);
}));
it('leave: should remove the element even if another animation is called after',
inject(function($animate, $rootScope, $$rAF, $rootElement) {
inject(function($animate, $rootScope, $rootElement) {
var outerContainer = jqLite('<div></div>');
element = jqLite('<div></div>');
@@ -1787,7 +1719,7 @@ describe("animations", function() {
$animate.removeClass(element,'rclass');
$rootScope.$digest();
runner.end();
$$rAF.flush();
$animate.flush();
var isElementRemoved = !outerContainer[0].contains(element[0]);
expect(isElementRemoved).toBe(true);
+110 -22
View File
@@ -3,6 +3,7 @@
describe('$$animation', function() {
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));
var element;
afterEach(function() {
@@ -14,14 +15,14 @@ describe('$$animation', function() {
}));
it("should not run an animation if there are no drivers",
inject(function($$animation, $$rAF, $rootScope) {
inject(function($$animation, $animate, $rootScope) {
element = jqLite('<div></div>');
var done = false;
$$animation(element, 'someEvent').then(function() {
done = true;
});
$$rAF.flush();
$animate.flush();
$rootScope.$digest();
expect(done).toBe(true);
}));
@@ -33,14 +34,14 @@ describe('$$animation', function() {
return false;
});
});
inject(function($$animation, $$rAF, $rootScope) {
inject(function($$animation, $animate, $rootScope) {
element = jqLite('<div></div>');
var done = false;
$$animation(element, 'someEvent').then(function() {
done = true;
});
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
$rootScope.$digest();
expect(done).toBe(true);
});
@@ -195,7 +196,7 @@ describe('$$animation', function() {
});
});
inject(function($$animation, $rootScope, $$rAF) {
inject(function($$animation, $rootScope, $animate) {
var status, element = jqLite('<div></div>');
var runner = $$animation(element, 'enter');
runner.then(function() {
@@ -210,7 +211,7 @@ describe('$$animation', function() {
event === 'resolve' ? runner.end() : runner.cancel();
// the resolve/rejection digest
$$rAF.flush();
$animate.flush();
$rootScope.$digest();
expect(status).toBe(event);
@@ -296,6 +297,90 @@ describe('$$animation', function() {
};
}));
it('should space out multiple ancestorial class-based animations with a RAF in between',
inject(function($rootScope, $$animation, $$rAF) {
var parent = element;
element = jqLite('<div></div>');
parent.append(element);
var child = jqLite('<div></div>');
element.append(child);
$$animation(parent, 'addClass', { addClass: 'blue' });
$$animation(element, 'addClass', { addClass: 'red' });
$$animation(child, 'addClass', { addClass: 'green' });
$rootScope.$digest();
expect(captureLog.length).toBe(1);
expect(capturedAnimation.options.addClass).toBe('blue');
$$rAF.flush();
expect(captureLog.length).toBe(2);
expect(capturedAnimation.options.addClass).toBe('red');
$$rAF.flush();
expect(captureLog.length).toBe(3);
expect(capturedAnimation.options.addClass).toBe('green');
}));
it('should properly cancel out pending animations that are spaced with a RAF request before the digest completes',
inject(function($rootScope, $$animation, $$rAF) {
var parent = element;
element = jqLite('<div></div>');
parent.append(element);
var child = jqLite('<div></div>');
element.append(child);
var r1 = $$animation(parent, 'addClass', { addClass: 'blue' });
var r2 = $$animation(element, 'addClass', { addClass: 'red' });
var r3 = $$animation(child, 'addClass', { addClass: 'green' });
r2.end();
$rootScope.$digest();
expect(captureLog.length).toBe(1);
expect(capturedAnimation.options.addClass).toBe('blue');
$$rAF.flush();
expect(captureLog.length).toBe(2);
expect(capturedAnimation.options.addClass).toBe('green');
}));
it('should properly cancel out pending animations that are spaced with a RAF request after the digest completes',
inject(function($rootScope, $$animation, $$rAF) {
var parent = element;
element = jqLite('<div></div>');
parent.append(element);
var child = jqLite('<div></div>');
element.append(child);
var r1 = $$animation(parent, 'addClass', { addClass: 'blue' });
var r2 = $$animation(element, 'addClass', { addClass: 'red' });
var r3 = $$animation(child, 'addClass', { addClass: 'green' });
$rootScope.$digest();
r2.end();
expect(captureLog.length).toBe(1);
expect(capturedAnimation.options.addClass).toBe('blue');
$$rAF.flush();
expect(captureLog.length).toBe(1);
$$rAF.flush();
expect(captureLog.length).toBe(2);
expect(capturedAnimation.options.addClass).toBe('green');
}));
they('should return a runner that object that contains a $prop() function',
['end', 'cancel', 'then'], function(method) {
inject(function($$animation) {
@@ -306,7 +391,7 @@ describe('$$animation', function() {
they('should close the animation if runner.$prop() is called before the $postDigest phase kicks in',
['end', 'cancel'], function(method) {
inject(function($$animation, $rootScope, $$rAF) {
inject(function($$animation, $rootScope, $animate) {
var status;
var runner = $$animation(element, 'someEvent');
runner.then(function() { status = 'end'; },
@@ -316,7 +401,7 @@ describe('$$animation', function() {
$rootScope.$digest();
expect(runnerLog).toEqual([]);
$$rAF.flush();
$animate.flush();
expect(status).toBe(method);
});
});
@@ -363,11 +448,10 @@ describe('$$animation', function() {
}));
it('should immediately end the animation if the element is removed from the DOM during the animation',
inject(function($$animation, $$rAF, $rootScope) {
inject(function($$animation, $animate, $rootScope) {
var runner = $$animation(element, 'someEvent');
$rootScope.$digest();
$$rAF.flush(); //the animation is "animating"
expect(capturedAnimation).toBeTruthy();
expect(runnerLog).toEqual([]);
@@ -376,14 +460,13 @@ describe('$$animation', function() {
}));
it('should not end the animation when the leave animation removes the element from the DOM',
inject(function($$animation, $$rAF, $rootScope) {
inject(function($$animation, $animate, $rootScope) {
var runner = $$animation(element, 'leave', {}, function() {
element.remove();
});
$rootScope.$digest();
$$rAF.flush(); //the animation is "animating"
expect(runnerLog).toEqual([]);
capturedAnimation.options.domOperation(); //this removes the element
@@ -392,7 +475,7 @@ describe('$$animation', function() {
}));
it('should remove the $destroy event listener when the animation is closed',
inject(function($$animation, $$rAF, $rootScope) {
inject(function($$animation, $rootScope) {
var addListen = spyOn(element, 'on').andCallThrough();
var removeListen = spyOn(element, 'off').andCallThrough();
@@ -408,7 +491,7 @@ describe('$$animation', function() {
}));
it('should always sort parent-element animations to run in order of parent-to-child DOM structure',
inject(function($$animation, $$rAF, $rootScope) {
inject(function($$animation, $rootScope, $animate) {
var child = jqLite('<div></div>');
var grandchild = jqLite('<div></div>');
@@ -424,6 +507,9 @@ describe('$$animation', function() {
$rootScope.$digest();
$animate.flush(); // element -> child
$animate.flush(); // child -> grandchild
expect(captureLog[0].element).toBe(element);
expect(captureLog[1].element).toBe(child);
expect(captureLog[2].element).toBe(grandchild);
@@ -543,7 +629,7 @@ describe('$$animation', function() {
}));
it("should not group animations into an anchored animation if enter/leave events are NOT used",
inject(function($$animation, $rootScope) {
inject(function($$animation, $rootScope, $$rAF) {
fromElement.addClass('shared-class');
fromElement.attr('ng-animate-ref', '1');
@@ -558,6 +644,7 @@ describe('$$animation', function() {
});
$rootScope.$digest();
$$rAF.flush();
expect(captureLog.length).toBe(2);
}));
@@ -649,7 +736,7 @@ describe('$$animation', function() {
});
it("should not end the animation when the `from` animation calls its own leave dom operation",
inject(function($$animation, $rootScope, $$rAF) {
inject(function($$animation, $rootScope) {
fromElement.addClass('group-1');
var elementRemoved = false;
@@ -679,7 +766,7 @@ describe('$$animation', function() {
}));
it("should not end the animation if any of the anchor elements are removed from the DOM during the animation",
inject(function($$animation, $rootScope, $$rAF) {
inject(function($$animation, $rootScope) {
fromElement.addClass('group-1');
var elementRemoved = false;
@@ -702,7 +789,7 @@ describe('$$animation', function() {
}));
it('should prepare a parent-element animation to run first before the anchored animation',
inject(function($$animation, $$rAF, $rootScope, $rootElement) {
inject(function($$animation, $rootScope, $rootElement, $animate) {
fromAnchors[0].attr('ng-animate-ref', 'shared');
toAnchors[0].attr('ng-animate-ref', 'shared');
@@ -725,6 +812,7 @@ describe('$$animation', function() {
expect(captureLog.length).toBe(0);
$rootScope.$digest();
$animate.flush();
expect(captureLog[0].element).toBe(parent);
expect(captureLog[1].from.element).toBe(fromElement);
@@ -869,7 +957,7 @@ describe('$$animation', function() {
};
});
});
inject(function($$animation, $rootScope, $$rAF) {
inject(function($$animation, $rootScope, $animate) {
element.addClass('four');
var completed = false;
@@ -882,7 +970,7 @@ describe('$$animation', function() {
$rootScope.$digest(); //runs the animation
$rootScope.$digest(); //flushes the step code
$$rAF.flush(); //runs the $$animation promise
$animate.flush();
$rootScope.$digest(); //the runner promise
expect(completed).toBe(true);
@@ -921,7 +1009,7 @@ describe('$$animation', function() {
};
});
});
inject(function($$animation, $rootScope, $$rAF) {
inject(function($$animation, $rootScope, $animate) {
element.addClass('four');
var completed = false;
@@ -937,7 +1025,7 @@ describe('$$animation', function() {
$rootScope.$digest(); //flushes the step code
runner.end();
$$rAF.flush(); //runs the $$animation promise
$animate.flush();
$rootScope.$digest(); //the runner promise
expect(completed).toBe(true);
+77 -89
View File
@@ -3,6 +3,7 @@
describe('ngAnimate integration tests', function() {
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));
var element, html, ss;
beforeEach(module(function() {
@@ -30,7 +31,7 @@ describe('ngAnimate integration tests', function() {
they('should render an $prop animation',
['enter', 'leave', 'move', 'addClass', 'removeClass', 'setClass'], function(event) {
inject(function($animate, $compile, $rootScope, $rootElement, $$rAF) {
inject(function($animate, $compile, $rootScope, $rootElement) {
element = jqLite('<div class="animate-me"></div>');
$compile(element)($rootScope);
@@ -97,10 +98,11 @@ describe('ngAnimate integration tests', function() {
});
expect(element).toHaveClass(setupClass);
$$rAF.flush();
$animate.flush();
expect(element).toHaveClass(activeClass);
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2 });
$animate.flush();
expect(element).not.toHaveClass(setupClass);
expect(element).not.toHaveClass(activeClass);
@@ -112,7 +114,7 @@ describe('ngAnimate integration tests', function() {
});
it('should not throw an error if the element is orphaned before the CSS animation starts',
inject(function($rootScope, $rootElement, $animate, $$rAF) {
inject(function($rootScope, $rootElement, $animate) {
ss.addRule('.animate-me', 'transition:2s linear all;');
@@ -127,51 +129,19 @@ describe('ngAnimate integration tests', function() {
$rootScope.$digest();
// this will run the first class-based animation
$$rAF.flush();
$animate.flush();
element.remove();
expect(function() {
$$rAF.flush();
$animate.flush();
}).not.toThrow();
dealoc(element);
}));
it('should always synchronously add css classes in order for child animations to animate properly',
inject(function($animate, $compile, $rootScope, $rootElement, $$rAF, $document) {
ss.addRule('.animations-enabled .animate-me.ng-enter', 'transition:2s linear all;');
element = jqLite('<div ng-class="{\'animations-enabled\':exp}"></div>');
var child = jqLite('<div ng-if="exp" class="animate-me"></div>');
element.append(child);
$rootElement.append(element);
jqLite($document[0].body).append($rootElement);
$compile(element)($rootScope);
$rootScope.exp = true;
$rootScope.$digest();
child = element.find('div');
expect(element).toHaveClass('animations-enabled');
expect(child).toHaveClass('ng-enter');
$$rAF.flush();
expect(child).toHaveClass('ng-enter-active');
browserTrigger(child, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2 });
expect(child).not.toHaveClass('ng-enter-active');
expect(child).not.toHaveClass('ng-enter');
}));
it('should synchronously add/remove ng-class expressions in time for other animations to run on the same element',
inject(function($animate, $compile, $rootScope, $rootElement, $$rAF, $document) {
it('should include the added/removed classes in lieu of the enter animation',
inject(function($animate, $compile, $rootScope, $rootElement, $document) {
ss.addRule('.animate-me.ng-enter.on', 'transition:2s linear all;');
@@ -184,7 +154,7 @@ describe('ngAnimate integration tests', function() {
$rootScope.exp = true;
$rootScope.$digest();
$$rAF.flush();
$animate.flush();
var child = element.find('div');
@@ -203,18 +173,19 @@ describe('ngAnimate integration tests', function() {
expect(child).toHaveClass('on');
expect(child).toHaveClass('ng-enter');
$$rAF.flush();
$animate.flush();
expect(child).toHaveClass('ng-enter-active');
browserTrigger(child, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2 });
$animate.flush();
expect(child).not.toHaveClass('ng-enter-active');
expect(child).not.toHaveClass('ng-enter');
}));
it('should animate ng-class and a structural animation in parallel on the same element',
inject(function($animate, $compile, $rootScope, $rootElement, $$rAF, $document) {
inject(function($animate, $compile, $rootScope, $rootElement, $document) {
ss.addRule('.animate-me.ng-enter', 'transition:2s linear all;');
ss.addRule('.animate-me.expand', 'transition:5s linear all; font-size:200px;');
@@ -236,12 +207,13 @@ describe('ngAnimate integration tests', function() {
expect(child).toHaveClass('expand-add');
expect(child).toHaveClass('expand');
$$rAF.flush();
$animate.flush();
expect(child).toHaveClass('ng-enter-active');
expect(child).toHaveClass('expand-add-active');
browserTrigger(child, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2 });
$animate.flush();
expect(child).not.toHaveClass('ng-enter-active');
expect(child).not.toHaveClass('ng-enter');
@@ -249,9 +221,9 @@ describe('ngAnimate integration tests', function() {
expect(child).not.toHaveClass('expand-add');
}));
it('should issue a reflow for each element animation on all DOM levels', function() {
it('should issue a RAF for each element animation on all DOM levels', function() {
module('ngAnimateMock');
inject(function($animate, $compile, $rootScope, $rootElement, $$rAF, $document) {
inject(function($animate, $compile, $rootScope, $rootElement, $document, $$rAF) {
element = jqLite(
'<div ng-class="{parent:exp}">' +
'<div ng-class="{parent2:exp}">' +
@@ -267,70 +239,84 @@ describe('ngAnimate integration tests', function() {
$compile(element)($rootScope);
$rootScope.$digest();
expect($animate.reflows).toBe(0);
var outer = element;
var inner = element.find('div');
$rootScope.exp = true;
$rootScope.items = [1,2,3,4,5,6,7,8,9,10];
$rootScope.$digest();
expect(outer).not.toHaveClass('parent');
expect(inner).not.toHaveClass('parent2');
// 2 parents + 10 items = 12
expect($animate.reflows).toBe(12);
assertTotalRepeats(0);
$$rAF.flush();
expect(outer).toHaveClass('parent');
assertTotalRepeats(0);
$$rAF.flush();
expect(inner).toHaveClass('parent2');
assertTotalRepeats(10);
function assertTotalRepeats(total) {
expect(inner[0].querySelectorAll('div.ng-enter').length).toBe(total);
}
});
});
it('should issue a reflow for each element and also its children', function() {
it('should pack level elements into their own RAF flush', function() {
module('ngAnimateMock');
inject(function($animate, $compile, $rootScope, $rootElement, $$rAF, $document) {
inject(function($animate, $compile, $rootScope, $rootElement, $document) {
ss.addRule('.inner', 'transition:2s linear all;');
element = jqLite(
'<div ng-class="{one:exp}">' +
'<div ng-if="exp"></div>' +
'</div>' +
'<div ng-class="{two:exp}">' +
'<div ng-if="exp"></div>' +
'</div>' +
'<div ng-class="{three:exp}">' +
'<div ng-if="false"></div>' +
'</div>' +
'<div ng-class="{four:exp}"></div>'
);
$rootElement.append(element);
jqLite($document[0].body).append($rootElement);
$compile(element)($rootScope);
$rootScope.$digest();
expect($animate.reflows).toBe(0);
$rootScope.exp = true;
$rootScope.$digest();
// there is one element's expression in there that is false
expect($animate.reflows).toBe(6);
});
});
it('should always issue atleast one reflow incase there are no parent class-based animations', function() {
module('ngAnimateMock');
inject(function($animate, $compile, $rootScope, $rootElement, $$rAF, $document) {
element = jqLite(
'<div ng-repeat="item in items" ng-class="{someAnimation:exp}">' +
'{{ item }}' +
'<div>' +
'<div class="outer" ng-class="{on:exp}">' +
'<div class="inner" ng-if="exp"></div>' +
'</div>' +
'<div class="outer" ng-class="{on:exp}">' +
'<div class="inner" ng-if="exp"></div>' +
'</div>' +
'<div class="outer" ng-class="{on:exp}">' +
'<div class="inner" ng-if="exp"></div>' +
'</div>' +
'<div class="outer" ng-class="{on:exp}"></div>' +
'</div>'
);
$rootElement.append(element);
jqLite($document[0].body).append($rootElement);
$compile(element)($rootScope);
$rootScope.$digest();
expect($animate.reflows).toBe(0);
assertGroupHasClass(query('outer'), 'on', true);
expect(query('inner').length).toBe(0);
$rootScope.exp = true;
$rootScope.items = [1,2,3,4,5,6,7,8,9,10];
$rootScope.$digest();
expect($animate.reflows).toBe(10);
assertGroupHasClass(query('outer'), 'on', true);
assertGroupHasClass(query('inner'), 'ng-enter', true);
$animate.flush();
assertGroupHasClass(query('outer'), 'on');
assertGroupHasClass(query('inner'), 'ng-enter');
function query(className) {
return element[0].querySelectorAll('.' + className);
}
function assertGroupHasClass(elms, className, not) {
for (var i = 0; i < elms.length; i++) {
var assert = expect(jqLite(elms[i]));
(not ? assert.not : assert).toHaveClass(className);
}
}
});
});
});
@@ -355,7 +341,7 @@ describe('ngAnimate integration tests', function() {
});
});
inject(function($animate, $compile, $rootScope, $rootElement, $$rAF) {
inject(function($animate, $compile, $rootScope, $rootElement) {
element = jqLite('<div class="animate-me"></div>');
$compile(element)($rootScope);
@@ -407,7 +393,7 @@ describe('ngAnimate integration tests', function() {
expect(isFunction(endAnimation)).toBe(true);
endAnimation();
$$rAF.flush();
$animate.flush();
expect(animateCompleteCallbackFired).toBe(true);
$rootScope.$digest();
@@ -465,6 +451,7 @@ describe('ngAnimate integration tests', function() {
}
$rootScope.$digest();
$animate.flush();
expect(endParentAnimationFn).toBeTruthy();
@@ -529,6 +516,7 @@ describe('ngAnimate integration tests', function() {
}
$rootScope.$digest();
$animate.flush();
expect(endParentAnimationFn).toBeTruthy();
+143
View File
@@ -0,0 +1,143 @@
'use strict';
describe("$$rAFScheduler", function() {
beforeEach(module('ngAnimate'));
it('should accept an array of tasks and run the first task immediately',
inject(function($$rAFScheduler) {
var taskSpy = jasmine.createSpy();
var tasks = [taskSpy];
$$rAFScheduler([tasks]);
expect(taskSpy).toHaveBeenCalled();
}));
it('should run tasks based on how many RAFs have run in comparison to the task index',
inject(function($$rAFScheduler, $$rAF) {
var i, tasks = [];
for (i = 0; i < 5; i++) {
tasks.push([jasmine.createSpy()]);
}
$$rAFScheduler(tasks);
for (i = 1; i < 5; i++) {
var taskSpy = tasks[i][0];
expect(taskSpy).not.toHaveBeenCalled();
$$rAF.flush();
expect(taskSpy).toHaveBeenCalled();
}
}));
it('should space out subarrays by a RAF and run the internals in parallel',
inject(function($$rAFScheduler, $$rAF) {
var spies = {
a: jasmine.createSpy(),
b: jasmine.createSpy(),
c: jasmine.createSpy(),
x: jasmine.createSpy(),
y: jasmine.createSpy(),
z: jasmine.createSpy()
};
var items = [[spies.a, spies.x],
[spies.b, spies.y],
[spies.c, spies.z]];
expect(spies.a).not.toHaveBeenCalled();
expect(spies.x).not.toHaveBeenCalled();
$$rAFScheduler(items);
expect(spies.a).toHaveBeenCalled();
expect(spies.x).toHaveBeenCalled();
expect(spies.b).not.toHaveBeenCalled();
expect(spies.y).not.toHaveBeenCalled();
$$rAF.flush();
expect(spies.b).toHaveBeenCalled();
expect(spies.y).toHaveBeenCalled();
expect(spies.c).not.toHaveBeenCalled();
expect(spies.z).not.toHaveBeenCalled();
$$rAF.flush();
expect(spies.c).toHaveBeenCalled();
expect(spies.z).toHaveBeenCalled();
}));
describe('.waitUntilQuiet', function() {
it('should run the `last` provided function when a RAF fully passes',
inject(function($$rAFScheduler, $$rAF) {
var q1 = jasmine.createSpy();
$$rAFScheduler.waitUntilQuiet(q1);
expect(q1).not.toHaveBeenCalled();
var q2 = jasmine.createSpy();
$$rAFScheduler.waitUntilQuiet(q2);
expect(q1).not.toHaveBeenCalled();
expect(q2).not.toHaveBeenCalled();
var q3 = jasmine.createSpy();
$$rAFScheduler.waitUntilQuiet(q3);
expect(q1).not.toHaveBeenCalled();
expect(q2).not.toHaveBeenCalled();
expect(q3).not.toHaveBeenCalled();
$$rAF.flush();
expect(q1).not.toHaveBeenCalled();
expect(q2).not.toHaveBeenCalled();
expect(q3).toHaveBeenCalled();
}));
it('should always execute itself before the next RAF task tick occurs', function() {
module(provideLog);
inject(function($$rAFScheduler, $$rAF, log) {
var quietFn = log.fn('quiet');
var tasks = [
[log.fn('task1')],
[log.fn('task2')],
[log.fn('task3')],
[log.fn('task4')]
];
$$rAFScheduler(tasks);
expect(log).toEqual(['task1']);
$$rAFScheduler.waitUntilQuiet(quietFn);
expect(log).toEqual(['task1']);
$$rAF.flush();
expect(log).toEqual(['task1', 'quiet', 'task2']);
$$rAF.flush();
expect(log).toEqual(['task1', 'quiet', 'task2', 'task3']);
$$rAFScheduler.waitUntilQuiet(quietFn);
$$rAF.flush();
expect(log).toEqual(['task1', 'quiet', 'task2', 'task3', 'quiet', 'task4']);
});
});
});
});
+8
View File
@@ -297,6 +297,14 @@ describe("resource", function() {
R.get({a: 'foo'});
});
it('should support IPv6 URLs', function() {
var R = $resource('http://[2620:0:861:ed1a::1]/:ed1a/', {}, {}, {stripTrailingSlashes: false});
$httpBackend.expect('GET', 'http://[2620:0:861:ed1a::1]/foo/').respond({});
$httpBackend.expect('GET', 'http://[2620:0:861:ed1a::1]/').respond({});
R.get({ed1a: 'foo'});
R.get({});
});
it('should support overriding provider default trailing-slash stripping configuration', function() {
// Set the new behavior for all new resources created by overriding the
// provider configuration
+17 -11
View File
@@ -712,7 +712,6 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
$animate.triggerCallbacks();
$location.path('/');
$rootScope.$digest();
@@ -750,7 +749,7 @@ describe('ngView animations', function() {
);
it('should render ngClass on ngView',
inject(function($compile, $rootScope, $templateCache, $animate, $location, $timeout) {
inject(function($compile, $rootScope, $templateCache, $animate, $location) {
var item;
$rootScope.tpl = 'one';
@@ -760,6 +759,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
$animate.flush();
//we don't care about the enter animation
$animate.queue.shift();
@@ -776,6 +776,8 @@ describe('ngView animations', function() {
expect($animate.queue.shift().event).toBe('addClass');
expect($animate.queue.shift().event).toBe('removeClass');
$animate.flush();
expect(item.hasClass('classy')).toBe(false);
expect(item.hasClass('boring')).toBe(true);
@@ -810,7 +812,7 @@ describe('ngView animations', function() {
});
});
inject(function($rootScope, $compile, $location, $route, $timeout, $rootElement, $sniffer, $animate, $$rAF) {
inject(function($rootScope, $compile, $location, $route, $timeout, $rootElement, $sniffer, $animate) {
element = $compile(html('<div><ng:view onload="load()" class="my-animation"></ng:view></div>'))($rootScope);
$animate.enabled(true);
@@ -834,7 +836,7 @@ describe('ngView animations', function() {
expect($animate.queue.shift().event).toBe('enter'); //ngRepeat 3
expect($animate.queue.shift().event).toBe('enter'); //ngRepeat 4
$$rAF.flush();
$animate.flush();
expect(element.text()).toEqual('34');
@@ -913,9 +915,11 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
expect(autoScrollSpy).toHaveBeenCalledOnce();
}));
@@ -927,9 +931,11 @@ describe('ngView animations', function() {
$rootScope.value = true;
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
expect(autoScrollSpy).toHaveBeenCalledOnce();
}));
@@ -941,7 +947,6 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
expect(autoScrollSpy).not.toHaveBeenCalled();
}));
@@ -955,7 +960,6 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
expect(autoScrollSpy).not.toHaveBeenCalled();
}));
@@ -972,7 +976,9 @@ describe('ngView animations', function() {
expect(autoScrollSpy).not.toHaveBeenCalled();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$digest();
expect($animate.enter).toHaveBeenCalledOnce();
expect(autoScrollSpy).toHaveBeenCalledOnce();