Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f61145475 | |||
| 8c618d896b | |||
| 68d4dc5b71 | |||
| 03a4a96cf9 | |||
| 655c52a621 | |||
| fa3ddba5f2 | |||
| c4a1b6124e | |||
| e52d731bfd | |||
| 9b72843018 | |||
| 9c1f8ea70b | |||
| 9fde5648e4 | |||
| ea829620b2 | |||
| 1731d091f8 | |||
| 9d3704ca46 | |||
| 215dff34dd | |||
| fa8c399fad | |||
| 7295c60ffb | |||
| fa01571036 | |||
| dbc698517f | |||
| a7f3761eda | |||
| 5a98e806ef | |||
| 808f984ec0 | |||
| 698af191de | |||
| 7a413df5e4 | |||
| 4994acd26e |
+2
-1
@@ -48,7 +48,7 @@ install:
|
||||
- npm config set loglevel http
|
||||
- npm install -g npm@2.5
|
||||
# Instal npm dependecies and ensure that npm cache is not stale
|
||||
- scripts/npm/install-dependencies.sh
|
||||
- npm install
|
||||
|
||||
before_script:
|
||||
- mkdir -p $LOGS_DIR
|
||||
@@ -61,6 +61,7 @@ script:
|
||||
- ./scripts/travis/build.sh
|
||||
|
||||
after_script:
|
||||
- ./scripts/travis/tear_down_browser_provider.sh
|
||||
- ./scripts/travis/print_logs.sh
|
||||
|
||||
notifications:
|
||||
|
||||
@@ -1,3 +1,55 @@
|
||||
<a name="1.4.7"></a>
|
||||
# 1.4.7 dark-luminescence (2015-09-29)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** use createMap() for $$observe listeners when initialized from attr interpolation
|
||||
([5a98e806](https://github.com/angular/angular.js/commit/5a98e806ef3c59916bb4668268125610b11effe8),
|
||||
[#10446](https://github.com/angular/angular.js/issues/10446))
|
||||
- **$parse:**
|
||||
- block assigning to fields of a constructor
|
||||
([a7f3761e](https://github.com/angular/angular.js/commit/a7f3761eda5309f76b73c6fb1d3173a270112899),
|
||||
[#12860](https://github.com/angular/angular.js/issues/12860))
|
||||
- do not convert to string computed properties multiple times
|
||||
([698af191](https://github.com/angular/angular.js/commit/698af191ded2465ca4e0f97959b75fede5a531ab))
|
||||
- **filters:** ensure `formatNumber` observes i18n decimal separators
|
||||
([4994acd2](https://github.com/angular/angular.js/commit/4994acd26e582eec8a92b139bfc09ca79a9b8835),
|
||||
[#10342](https://github.com/angular/angular.js/issues/10342), [#12850](https://github.com/angular/angular.js/issues/12850))
|
||||
- **jqLite:** properly handle dash-delimited node names in `jqLiteBuildFragment`
|
||||
([cdd1227a](https://github.com/angular/angular.js/commit/cdd1227a308edd34d31b67f338083b6e0c4c0db9),
|
||||
[#10617](https://github.com/angular/angular.js/issues/10617), [#12759](https://github.com/angular/angular.js/issues/12759))
|
||||
- **ngAnimate:**
|
||||
- ensure anchoring uses body as a container when needed
|
||||
([9d3704ca](https://github.com/angular/angular.js/commit/9d3704ca467081f16b71b011eb50c53d5cdb2f34),
|
||||
[#12872](https://github.com/angular/angular.js/issues/12872))
|
||||
- callback detection should only use RAF when necessary
|
||||
([fa8c399f](https://github.com/angular/angular.js/commit/fa8c399fadc30b78710868fe59d2930fdc17c7a5))
|
||||
- **ngMessages:** prevent race condition with ngAnimate
|
||||
([7295c60f](https://github.com/angular/angular.js/commit/7295c60ffb9f2e4f32043c538ace740b187f565a),
|
||||
[#12856](https://github.com/angular/angular.js/issues/12856), [#12903](https://github.com/angular/angular.js/issues/12903))
|
||||
- **ngOptions:**
|
||||
- skip comments when looking for option elements
|
||||
([68d4dc5b](https://github.com/angular/angular.js/commit/68d4dc5b71b23e4c7c2650e6da3d7200de99f1ae),
|
||||
[#12190](https://github.com/angular/angular.js/issues/12190))
|
||||
- prevent frozen select ui in IE
|
||||
([dbc69851](https://github.com/angular/angular.js/commit/dbc698517ff620b3a6279f65d4a9b6e3c15087b9),
|
||||
[#11314](https://github.com/angular/angular.js/issues/11314), [#11795](https://github.com/angular/angular.js/issues/11795))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$animateCss:** add support for temporary styles via `cleanupStyles`
|
||||
([e52d731b](https://github.com/angular/angular.js/commit/e52d731bfd1fbb6c616125fbde2fb365722254b7),
|
||||
[#12930](https://github.com/angular/angular.js/issues/12930))
|
||||
- **$http:** add `$xhrFactory` service to enable creation of custom xhr objects
|
||||
([7a413df5](https://github.com/angular/angular.js/commit/7a413df5e47e04e20a1c93d35922050bbcbfb492),
|
||||
[#2318](https://github.com/angular/angular.js/issues/2318), [#9319](https://github.com/angular/angular.js/issues/9319), [#12159](https://github.com/angular/angular.js/issues/12159))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
|
||||
<a name="1.4.6"></a>
|
||||
# 1.4.6 multiplicative-elevation (2015-09-17)
|
||||
|
||||
|
||||
+1
-1
@@ -305,7 +305,7 @@ module.exports = function(grunt) {
|
||||
|
||||
shell: {
|
||||
"npm-install": {
|
||||
command: path.normalize('scripts/npm/install-dependencies.sh')
|
||||
command: 'node scripts/npm/check-node-modules.js'
|
||||
},
|
||||
|
||||
"promises-aplus-tests": {
|
||||
|
||||
Vendored
-1
@@ -92,7 +92,6 @@ var angularFiles = {
|
||||
'angularModules': {
|
||||
'ngAnimate': [
|
||||
'src/ngAnimate/shared.js',
|
||||
'src/ngAnimate/body.js',
|
||||
'src/ngAnimate/rafScheduler.js',
|
||||
'src/ngAnimate/animateChildrenDirective.js',
|
||||
'src/ngAnimate/animateCss.js',
|
||||
|
||||
@@ -43,8 +43,7 @@ mirrors the process of compiling source code in
|
||||
Before we can write a directive, we need to know how Angular's {@link guide/compiler HTML compiler}
|
||||
determines when to use a given directive.
|
||||
|
||||
Similar to the terminology used when an [element **matches** a selector]
|
||||
(https://developer.mozilla.org/en-US/docs/Web/API/Element.matches), we say an element **matches** a
|
||||
Similar to the terminology used when an [element **matches** a selector](https://developer.mozilla.org/en-US/docs/Web/API/Element.matches), we say an element **matches** a
|
||||
directive when the directive is part of its declaration.
|
||||
|
||||
In the following example, we say that the `<input>` element **matches** the `ngModel` directive
|
||||
@@ -903,7 +902,7 @@ to which tab is active.
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {},
|
||||
controller: function($scope) {
|
||||
controller: ['$scope', function($scope) {
|
||||
var panes = $scope.panes = [];
|
||||
|
||||
$scope.select = function(pane) {
|
||||
@@ -919,7 +918,7 @@ to which tab is active.
|
||||
}
|
||||
panes.push(pane);
|
||||
};
|
||||
},
|
||||
}],
|
||||
templateUrl: 'my-tabs.html'
|
||||
};
|
||||
})
|
||||
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
|
||||
echo "Shutting down Browserstack tunnel"
|
||||
echo "TODO: implement me"
|
||||
exit 1
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
|
||||
echo "Shutting down Sauce Connect tunnel"
|
||||
|
||||
killall sc
|
||||
|
||||
while [[ -n `ps -ef | grep "sauce-connect-" | grep -v "grep"` ]]; do
|
||||
printf "."
|
||||
sleep .5
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Sauce Connect tunnel has been shut down"
|
||||
@@ -13,6 +13,10 @@
|
||||
"npm": "~2.5"
|
||||
},
|
||||
"engineStrict": true,
|
||||
"scripts": {
|
||||
"preinstall": "node scripts/npm/check-node-modules.js --purge",
|
||||
"postinstall": "node scripts/npm/copy-npm-shrinkwrap.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-benchpress": "0.x.x",
|
||||
"benchmark": "1.x.x",
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// Implementation based on:
|
||||
// https://github.com/angular/angular/blob/3b9c08676a4c921bbfa847802e08566fb601ba7a/tools/npm/check-node-modules.js
|
||||
'use strict';
|
||||
|
||||
// Imports
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
// Constants
|
||||
var PROJECT_ROOT = path.join(__dirname, '../../');
|
||||
var NODE_MODULES_DIR = 'node_modules';
|
||||
var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json';
|
||||
var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json';
|
||||
|
||||
// Run
|
||||
_main();
|
||||
|
||||
// Functions - Definitions
|
||||
function _main() {
|
||||
var purgeIfStale = process.argv.indexOf('--purge') !== -1;
|
||||
|
||||
process.chdir(PROJECT_ROOT);
|
||||
checkNodeModules(purgeIfStale);
|
||||
}
|
||||
|
||||
function checkNodeModules(purgeIfStale) {
|
||||
var nodeModulesOk = compareMarkerFiles(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE);
|
||||
|
||||
if (nodeModulesOk) {
|
||||
console.log(':-) npm dependencies are looking good!');
|
||||
} else if (purgeIfStale) {
|
||||
console.log(':-( npm dependencies are stale or in an unknown state!');
|
||||
console.log(' Purging \'' + NODE_MODULES_DIR + '\'...');
|
||||
deleteDirSync(NODE_MODULES_DIR);
|
||||
} else {
|
||||
var separator = new Array(81).join('!');
|
||||
|
||||
console.warn(separator);
|
||||
console.warn(':-( npm dependencies are stale or in an unknown state!');
|
||||
console.warn('You can rebuild the dependencies by running `npm install`.');
|
||||
console.warn(separator);
|
||||
}
|
||||
|
||||
return nodeModulesOk;
|
||||
}
|
||||
|
||||
function compareMarkerFiles(markerFilePath, cachedMarkerFilePath) {
|
||||
if (!fs.existsSync(cachedMarkerFilePath)) return false;
|
||||
|
||||
var opts = {encoding: 'utf-8'};
|
||||
var markerContent = fs.readFileSync(markerFilePath, opts);
|
||||
var cachedMarkerContent = fs.readFileSync(cachedMarkerFilePath, opts);
|
||||
|
||||
return markerContent === cachedMarkerContent;
|
||||
}
|
||||
|
||||
// Custom implementation of `rm -rf` that works consistently across OSes
|
||||
function deleteDirSync(path) {
|
||||
if (fs.existsSync(path)) {
|
||||
fs.readdirSync(path).forEach(deleteDirOrFileSync);
|
||||
fs.rmdirSync(path);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
function deleteDirOrFileSync(subpath) {
|
||||
var curPath = path + '/' + subpath;
|
||||
|
||||
if (fs.lstatSync(curPath).isDirectory()) {
|
||||
deleteDirSync(curPath);
|
||||
} else {
|
||||
fs.unlinkSync(curPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
// Imports
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
// Constants
|
||||
var PROJECT_ROOT = path.join(__dirname, '../../');
|
||||
var NODE_MODULES_DIR = 'node_modules';
|
||||
var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json';
|
||||
var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json';
|
||||
|
||||
// Run
|
||||
_main();
|
||||
|
||||
// Functions - Definitions
|
||||
function _main() {
|
||||
process.chdir(PROJECT_ROOT);
|
||||
copyFile(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE, onCopied);
|
||||
}
|
||||
|
||||
// Implementation based on:
|
||||
// https://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js#answer-21995878
|
||||
function copyFile(srcPath, dstPath, callback) {
|
||||
var callbackCalled = false;
|
||||
|
||||
if (!fs.existsSync(srcPath)) {
|
||||
done(new Error('Missing source file: ' + srcPath));
|
||||
return;
|
||||
}
|
||||
|
||||
var rs = fs.createReadStream(srcPath);
|
||||
rs.on('error', done);
|
||||
|
||||
var ws = fs.createWriteStream(dstPath);
|
||||
ws.on('error', done);
|
||||
ws.on('finish', done);
|
||||
|
||||
rs.pipe(ws);
|
||||
|
||||
// Helpers
|
||||
function done(err) {
|
||||
if (callback && !callbackCalled) {
|
||||
callbackCalled = true;
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onCopied(err) {
|
||||
if (err) {
|
||||
var separator = new Array(81).join('!');
|
||||
|
||||
console.error(separator);
|
||||
console.error(
|
||||
'Failed to copy `' + NPM_SHRINKWRAP_FILE + '` to `' + NPM_SHRINKWRAP_CACHED_FILE + '`:');
|
||||
console.error(err);
|
||||
console.error(separator);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
SHRINKWRAP_FILE=npm-shrinkwrap.json
|
||||
SHRINKWRAP_CACHED_FILE=node_modules/npm-shrinkwrap.cached.json
|
||||
|
||||
if diff -q $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE; then
|
||||
echo 'No shrinkwrap changes detected. npm install will be skipped...';
|
||||
else
|
||||
echo 'Blowing away node_modules and reinstalling npm dependencies...'
|
||||
rm -rf node_modules
|
||||
npm install
|
||||
cp $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE
|
||||
echo 'npm install successful!'
|
||||
fi
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
# Has to be run from project root directory.
|
||||
|
||||
./lib/${BROWSER_PROVIDER}/teardown_tunnel.sh
|
||||
@@ -72,6 +72,7 @@
|
||||
$HttpParamSerializerProvider,
|
||||
$HttpParamSerializerJQLikeProvider,
|
||||
$HttpBackendProvider,
|
||||
$xhrFactoryProvider,
|
||||
$LocationProvider,
|
||||
$LogProvider,
|
||||
$ParseProvider,
|
||||
@@ -230,6 +231,7 @@ function publishExternalAPI(angular) {
|
||||
$httpParamSerializer: $HttpParamSerializerProvider,
|
||||
$httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
|
||||
$httpBackend: $HttpBackendProvider,
|
||||
$xhrFactory: $xhrFactoryProvider,
|
||||
$location: $LocationProvider,
|
||||
$log: $LogProvider,
|
||||
$parse: $ParseProvider,
|
||||
|
||||
@@ -43,6 +43,13 @@ var $CoreAnimateCssProvider = function() {
|
||||
};
|
||||
|
||||
return function(element, options) {
|
||||
// there is no point in applying the styles since
|
||||
// there is no animation that goes on at all in
|
||||
// this version of $animateCss.
|
||||
if (options.cleanupStyles) {
|
||||
options.from = options.to = null;
|
||||
}
|
||||
|
||||
if (options.from) {
|
||||
element.css(options.from);
|
||||
options.from = null;
|
||||
|
||||
@@ -67,10 +67,10 @@
|
||||
$scope.keys = [];
|
||||
$scope.cache = $cacheFactory('cacheId');
|
||||
$scope.put = function(key, value) {
|
||||
if (isUndefined($scope.cache.get(key))) {
|
||||
if (angular.isUndefined($scope.cache.get(key))) {
|
||||
$scope.keys.push(key);
|
||||
}
|
||||
$scope.cache.put(key, isUndefined(value) ? null : value);
|
||||
$scope.cache.put(key, angular.isUndefined(value) ? null : value);
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
|
||||
+1
-1
@@ -2427,7 +2427,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
compile: function() {
|
||||
return {
|
||||
pre: function attrInterpolatePreLinkFn(scope, element, attr) {
|
||||
var $$observers = (attr.$$observers || (attr.$$observers = {}));
|
||||
var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
|
||||
|
||||
if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
|
||||
throw $compileMinErr('nodomevents',
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
* ### Example - splitting on whitespace
|
||||
* ### Example - splitting on newline
|
||||
* <example name="ngList-directive-newlines">
|
||||
* <file name="index.html">
|
||||
* <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
|
||||
|
||||
@@ -22,7 +22,9 @@ var ngModelMinErr = minErr('ngModel');
|
||||
* @ngdoc type
|
||||
* @name ngModel.NgModelController
|
||||
*
|
||||
* @property {string} $viewValue Actual string value in the view.
|
||||
* @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
|
||||
* String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
|
||||
* is set.
|
||||
* @property {*} $modelValue The value in the model that the control is bound to.
|
||||
* @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
|
||||
the control reads value from the DOM. The functions are called in array order, each passing
|
||||
|
||||
@@ -579,11 +579,16 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
function updateOptionElement(option, element) {
|
||||
option.element = element;
|
||||
element.disabled = option.disabled;
|
||||
if (option.value !== element.value) element.value = option.selectValue;
|
||||
// NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
|
||||
// selects in certain circumstances when multiple selects are next to each other and display
|
||||
// the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
|
||||
// See https://github.com/angular/angular.js/issues/11314 for more info.
|
||||
// This is unfortunately untestable with unit / e2e tests
|
||||
if (option.label !== element.label) {
|
||||
element.label = option.label;
|
||||
element.textContent = option.label;
|
||||
}
|
||||
if (option.value !== element.value) element.value = option.selectValue;
|
||||
}
|
||||
|
||||
function addOrReuseElement(parent, current, type, templateElement) {
|
||||
@@ -624,7 +629,10 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
if (emptyOption_ || unknownOption_) {
|
||||
while (current &&
|
||||
(current === emptyOption_ ||
|
||||
current === unknownOption_)) {
|
||||
current === unknownOption_ ||
|
||||
emptyOption_ && emptyOption_.nodeType === NODE_TYPE_COMMENT)) {
|
||||
// Empty options might have directives that transclude
|
||||
// and insert comments (e.g. ngIf)
|
||||
current = current.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,6 +214,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
|
||||
if (fractionSize > 0 && number < 1) {
|
||||
formatedText = number.toFixed(fractionSize);
|
||||
number = parseFloat(formatedText);
|
||||
formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-31
@@ -425,28 +425,18 @@ function $HttpProvider() {
|
||||
*
|
||||
*
|
||||
* ## General usage
|
||||
* The `$http` service is a function which takes a single argument — a configuration object —
|
||||
* The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
|
||||
* that is used to generate an HTTP request and returns a {@link ng.$q promise}.
|
||||
*
|
||||
* ```js
|
||||
* // Simple GET request example :
|
||||
* $http.get('/someUrl').
|
||||
* then(function(response) {
|
||||
* // Simple GET request example:
|
||||
* $http({
|
||||
* method: 'GET',
|
||||
* url: '/someUrl'
|
||||
* }).then(function successCallback(response) {
|
||||
* // this callback will be called asynchronously
|
||||
* // when the response is available
|
||||
* }, function(response) {
|
||||
* // called asynchronously if an error occurs
|
||||
* // or server returns response with an error status.
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ```js
|
||||
* // Simple POST request example (passing data) :
|
||||
* $http.post('/someUrl', {msg:'hello word!'}).
|
||||
* then(function(response) {
|
||||
* // this callback will be called asynchronously
|
||||
* // when the response is available
|
||||
* }, function(response) {
|
||||
* }, function errorCallback(response) {
|
||||
* // called asynchronously if an error occurs
|
||||
* // or server returns response with an error status.
|
||||
* });
|
||||
@@ -466,25 +456,16 @@ function $HttpProvider() {
|
||||
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
|
||||
* called for such responses.
|
||||
*
|
||||
* ## Writing Unit Tests that use $http
|
||||
* When unit testing (using {@link ngMock ngMock}), it is necessary to call
|
||||
* {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
|
||||
* request using trained responses.
|
||||
*
|
||||
* ```
|
||||
* $httpBackend.expectGET(...);
|
||||
* $http.get(...);
|
||||
* $httpBackend.flush();
|
||||
* ```
|
||||
*
|
||||
* ## Shortcut methods
|
||||
*
|
||||
* Shortcut methods are also available. All shortcut methods require passing in the URL, and
|
||||
* request data must be passed in for POST/PUT requests.
|
||||
* request data must be passed in for POST/PUT requests. An optional config can be passed as the
|
||||
* last argument.
|
||||
*
|
||||
* ```js
|
||||
* $http.get('/someUrl').then(successCallback);
|
||||
* $http.post('/someUrl', data).then(successCallback);
|
||||
* $http.get('/someUrl', config).then(successCallback, errorCallback);
|
||||
* $http.post('/someUrl', data, config).then(successCallback, errorCallback);
|
||||
* ```
|
||||
*
|
||||
* Complete list of shortcut methods:
|
||||
@@ -498,6 +479,17 @@ function $HttpProvider() {
|
||||
* - {@link ng.$http#patch $http.patch}
|
||||
*
|
||||
*
|
||||
* ## Writing Unit Tests that use $http
|
||||
* When unit testing (using {@link ngMock ngMock}), it is necessary to call
|
||||
* {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
|
||||
* request using trained responses.
|
||||
*
|
||||
* ```
|
||||
* $httpBackend.expectGET(...);
|
||||
* $http.get(...);
|
||||
* $httpBackend.flush();
|
||||
* ```
|
||||
*
|
||||
* ## Deprecation Notice
|
||||
* <div class="alert alert-danger">
|
||||
* The `$http` legacy promise methods `success` and `error` have been deprecated.
|
||||
@@ -655,7 +647,7 @@ function $HttpProvider() {
|
||||
*
|
||||
* There are two kinds of interceptors (and two kinds of rejection interceptors):
|
||||
*
|
||||
* * `request`: interceptors get called with a http `config` object. The function is free to
|
||||
* * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
|
||||
* modify the `config` object or create a new one. The function needs to return the `config`
|
||||
* object directly, or a promise containing the `config` or a new `config` object.
|
||||
* * `requestError`: interceptor gets called when a previous interceptor threw an error or
|
||||
|
||||
+31
-5
@@ -1,7 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
function createXhr() {
|
||||
return new window.XMLHttpRequest();
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $xhrFactory
|
||||
*
|
||||
* @description
|
||||
* Factory function used to create XMLHttpRequest objects.
|
||||
*
|
||||
* Replace or decorate this service to create your own custom XMLHttpRequest objects.
|
||||
*
|
||||
* ```
|
||||
* angular.module('myApp', [])
|
||||
* .factory('$xhrFactory', function() {
|
||||
* return function createXhr(method, url) {
|
||||
* return new window.XMLHttpRequest({mozSystem: true});
|
||||
* };
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param {string} method HTTP method of the request (GET, POST, PUT, ..)
|
||||
* @param {string} url URL of the request.
|
||||
*/
|
||||
function $xhrFactoryProvider() {
|
||||
this.$get = function() {
|
||||
return function createXhr() {
|
||||
return new window.XMLHttpRequest();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9,6 +34,7 @@ function createXhr() {
|
||||
* @name $httpBackend
|
||||
* @requires $window
|
||||
* @requires $document
|
||||
* @requires $xhrFactory
|
||||
*
|
||||
* @description
|
||||
* HTTP backend used by the {@link ng.$http service} that delegates to
|
||||
@@ -21,8 +47,8 @@ function createXhr() {
|
||||
* $httpBackend} which can be trained with responses.
|
||||
*/
|
||||
function $HttpBackendProvider() {
|
||||
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
|
||||
return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
|
||||
this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
|
||||
return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -46,7 +72,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
});
|
||||
} else {
|
||||
|
||||
var xhr = createXhr();
|
||||
var xhr = createXhr(method, url);
|
||||
|
||||
xhr.open(method, url, true);
|
||||
forEach(headers, function(value, key) {
|
||||
|
||||
+48
-8
@@ -38,20 +38,30 @@ var $parseMinErr = minErr('$parse');
|
||||
|
||||
|
||||
function ensureSafeMemberName(name, fullExpression) {
|
||||
if (name === "__defineGetter__" || name === "__defineSetter__"
|
||||
|| name === "__lookupGetter__" || name === "__lookupSetter__"
|
||||
|| name === "__proto__") {
|
||||
throw $parseMinErr('isecfld',
|
||||
'Attempting to access a disallowed field in Angular expressions! '
|
||||
+ 'Expression: {0}', fullExpression);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
function getStringValue(name, fullExpression) {
|
||||
// From the JavaScript docs:
|
||||
// Property names must be strings. This means that non-string objects cannot be used
|
||||
// as keys in an object. Any non-string object, including a number, is typecasted
|
||||
// into a string via the toString method.
|
||||
//
|
||||
// So, to ensure that we are checking the same `name` that JavaScript would use,
|
||||
// we cast it to a string, if possible
|
||||
name = (isObject(name) && name.toString) ? name.toString() : name;
|
||||
|
||||
if (name === "__defineGetter__" || name === "__defineSetter__"
|
||||
|| name === "__lookupGetter__" || name === "__lookupSetter__"
|
||||
|| name === "__proto__") {
|
||||
throw $parseMinErr('isecfld',
|
||||
'Attempting to access a disallowed field in Angular expressions! '
|
||||
// we cast it to a string, if possible.
|
||||
// Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
|
||||
// this is, this will handle objects that misbehave.
|
||||
name = name + '';
|
||||
if (!isString(name)) {
|
||||
throw $parseMinErr('iseccst',
|
||||
'Cannot convert object to primitive value! '
|
||||
+ 'Expression: {0}', fullExpression);
|
||||
}
|
||||
return name;
|
||||
@@ -102,6 +112,16 @@ function ensureSafeFunction(obj, fullExpression) {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureSafeAssignContext(obj, fullExpression) {
|
||||
if (obj) {
|
||||
if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
|
||||
obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
|
||||
throw $parseMinErr('isecaf',
|
||||
'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var OPERATORS = createMap();
|
||||
forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
|
||||
var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
|
||||
@@ -816,6 +836,8 @@ ASTCompiler.prototype = {
|
||||
'ensureSafeMemberName',
|
||||
'ensureSafeObject',
|
||||
'ensureSafeFunction',
|
||||
'getStringValue',
|
||||
'ensureSafeAssignContext',
|
||||
'ifDefined',
|
||||
'plus',
|
||||
'text',
|
||||
@@ -824,6 +846,8 @@ ASTCompiler.prototype = {
|
||||
ensureSafeMemberName,
|
||||
ensureSafeObject,
|
||||
ensureSafeFunction,
|
||||
getStringValue,
|
||||
ensureSafeAssignContext,
|
||||
ifDefined,
|
||||
plusFn,
|
||||
expression);
|
||||
@@ -967,6 +991,7 @@ ASTCompiler.prototype = {
|
||||
if (ast.computed) {
|
||||
right = self.nextId();
|
||||
self.recurse(ast.property, right);
|
||||
self.getStringValue(right);
|
||||
self.addEnsureSafeMemberName(right);
|
||||
if (create && create !== 1) {
|
||||
self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
|
||||
@@ -1050,6 +1075,7 @@ ASTCompiler.prototype = {
|
||||
self.if_(self.notNull(left.context), function() {
|
||||
self.recurse(ast.right, right);
|
||||
self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
|
||||
self.addEnsureSafeAssignContext(left.context);
|
||||
expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
|
||||
self.assign(intoId, expression);
|
||||
recursionFn(intoId || expression);
|
||||
@@ -1175,6 +1201,10 @@ ASTCompiler.prototype = {
|
||||
this.current().body.push(this.ensureSafeFunction(item), ';');
|
||||
},
|
||||
|
||||
addEnsureSafeAssignContext: function(item) {
|
||||
this.current().body.push(this.ensureSafeAssignContext(item), ';');
|
||||
},
|
||||
|
||||
ensureSafeObject: function(item) {
|
||||
return 'ensureSafeObject(' + item + ',text)';
|
||||
},
|
||||
@@ -1187,6 +1217,14 @@ ASTCompiler.prototype = {
|
||||
return 'ensureSafeFunction(' + item + ',text)';
|
||||
},
|
||||
|
||||
getStringValue: function(item) {
|
||||
this.assign(item, 'getStringValue(' + item + ',text)');
|
||||
},
|
||||
|
||||
ensureSafeAssignContext: function(item) {
|
||||
return 'ensureSafeAssignContext(' + item + ',text)';
|
||||
},
|
||||
|
||||
lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
|
||||
var self = this;
|
||||
return function() {
|
||||
@@ -1364,6 +1402,7 @@ ASTInterpreter.prototype = {
|
||||
var lhs = left(scope, locals, assign, inputs);
|
||||
var rhs = right(scope, locals, assign, inputs);
|
||||
ensureSafeObject(lhs.value, self.expression);
|
||||
ensureSafeAssignContext(lhs.context);
|
||||
lhs.context[lhs.name] = rhs;
|
||||
return context ? {value: rhs} : rhs;
|
||||
};
|
||||
@@ -1561,6 +1600,7 @@ ASTInterpreter.prototype = {
|
||||
var value;
|
||||
if (lhs != null) {
|
||||
rhs = right(scope, locals, assign, inputs);
|
||||
rhs = getStringValue(rhs);
|
||||
ensureSafeMemberName(rhs, expression);
|
||||
if (create && create !== 1 && lhs && !(lhs[rhs])) {
|
||||
lhs[rhs] = {};
|
||||
|
||||
@@ -187,7 +187,7 @@ var ANIMATE_TIMER_KEY = '$$animateCss';
|
||||
* * `event` - The DOM event (e.g. enter, leave, move). When used, a generated CSS class of `ng-EVENT` and `ng-EVENT-active` will be applied
|
||||
* to the element during the animation. Multiple events can be provided when spaces are used as a separator. (Note that this will not perform any DOM operation.)
|
||||
* * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both).
|
||||
* * `transition` - The raw CSS transition style that will be used (e.g. `1s linear all`).
|
||||
* * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`).
|
||||
* * `keyframeStyle` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`).
|
||||
* * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation.
|
||||
* * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition.
|
||||
@@ -204,6 +204,10 @@ var ANIMATE_TIMER_KEY = '$$animateCss';
|
||||
* * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a
|
||||
* * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
|
||||
* * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occuring on the classes being added and removed.)
|
||||
* * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once
|
||||
* the animation is closed. This is useful for when the styles are used purely for the sake of
|
||||
* the animation and do not have a lasting visual effect on the element (e.g. a colapse and open animation).
|
||||
* By default this value is set to `false`.
|
||||
*
|
||||
* @return {object} an object with start and end methods and details about the animation.
|
||||
*
|
||||
@@ -324,6 +328,23 @@ function createLocalCacheLookup() {
|
||||
};
|
||||
}
|
||||
|
||||
// we do not reassign an already present style value since
|
||||
// if we detect the style property value again we may be
|
||||
// detecting styles that were added via the `from` styles.
|
||||
// We make use of `isDefined` here since an empty string
|
||||
// or null value (which is what getPropertyValue will return
|
||||
// for a non-existing style) will still be marked as a valid
|
||||
// value for the style (a falsy value implies that the style
|
||||
// is to be removed at the end of the animation). If we had a simple
|
||||
// "OR" statement then it would not be enough to catch that.
|
||||
function registerRestorableStyles(backup, node, properties) {
|
||||
forEach(properties, function(prop) {
|
||||
backup[prop] = isDefined(backup[prop])
|
||||
? backup[prop]
|
||||
: node.style.getPropertyValue(prop);
|
||||
});
|
||||
}
|
||||
|
||||
var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
|
||||
var gcsLookup = createLocalCacheLookup();
|
||||
var gcsStaggerLookup = createLocalCacheLookup();
|
||||
@@ -424,6 +445,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
|
||||
}
|
||||
|
||||
return function init(element, options) {
|
||||
var restoreStyles = {};
|
||||
var node = getDomNode(element);
|
||||
if (!node
|
||||
|| !node.parentNode
|
||||
@@ -625,7 +647,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
|
||||
stagger.animationDuration === 0;
|
||||
}
|
||||
|
||||
applyAnimationFromStyles(element, options);
|
||||
if (options.from) {
|
||||
if (options.cleanupStyles) {
|
||||
registerRestorableStyles(restoreStyles, node, Object.keys(options.from));
|
||||
}
|
||||
applyAnimationFromStyles(element, options);
|
||||
}
|
||||
|
||||
if (flags.blockTransition || flags.blockKeyframeAnimation) {
|
||||
applyBlocking(maxDuration);
|
||||
@@ -692,6 +719,13 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
|
||||
applyAnimationClasses(element, options);
|
||||
applyAnimationStyles(element, options);
|
||||
|
||||
if (Object.keys(restoreStyles).length) {
|
||||
forEach(restoreStyles, function(value, prop) {
|
||||
value ? node.style.setProperty(prop, value)
|
||||
: node.style.removeProperty(prop);
|
||||
});
|
||||
}
|
||||
|
||||
// the reason why we have this option is to allow a synchronous closing callback
|
||||
// that is fired as SOON as the animation ends (when the CSS is removed) or if
|
||||
// the animation never takes off at all. A good example is a leave animation since
|
||||
@@ -886,7 +920,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
|
||||
}
|
||||
|
||||
element.on(events.join(' '), onAnimationProgress);
|
||||
applyAnimationToStyles(element, options);
|
||||
if (options.to) {
|
||||
if (options.cleanupStyles) {
|
||||
registerRestorableStyles(restoreStyles, node, Object.keys(options.to));
|
||||
}
|
||||
applyAnimationToStyles(element, options);
|
||||
}
|
||||
}
|
||||
|
||||
function onAnimationExpired() {
|
||||
|
||||
@@ -9,16 +9,25 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
|
||||
var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out';
|
||||
var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in';
|
||||
|
||||
this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$$body', '$sniffer', '$$jqLite',
|
||||
function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $$body, $sniffer, $$jqLite) {
|
||||
function isDocumentFragment(node) {
|
||||
return node.parentNode && node.parentNode.nodeType === 11;
|
||||
}
|
||||
|
||||
this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$sniffer', '$$jqLite', '$document',
|
||||
function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $sniffer, $$jqLite, $document) {
|
||||
|
||||
// only browsers that support these properties can render animations
|
||||
if (!$sniffer.animations && !$sniffer.transitions) return noop;
|
||||
|
||||
var bodyNode = getDomNode($$body);
|
||||
var bodyNode = $document[0].body;
|
||||
var rootNode = getDomNode($rootElement);
|
||||
|
||||
var rootBodyElement = jqLite(bodyNode.parentNode === rootNode ? bodyNode : rootNode);
|
||||
var rootBodyElement = jqLite(
|
||||
// this is to avoid using something that exists outside of the body
|
||||
// we also special case the doc fragement case because our unit test code
|
||||
// appends the $rootElement to the body after the app has been bootstrapped
|
||||
isDocumentFragment(rootNode) || bodyNode.contains(rootNode) ? rootNode : bodyNode
|
||||
);
|
||||
|
||||
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
|
||||
|
||||
|
||||
@@ -66,15 +66,33 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
return (nO.addClass && nO.addClass === cO.removeClass) || (nO.removeClass && nO.removeClass === cO.addClass);
|
||||
});
|
||||
|
||||
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$body', '$$HashMap',
|
||||
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
|
||||
'$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',
|
||||
function($$rAF, $rootScope, $rootElement, $document, $$body, $$HashMap,
|
||||
function($$rAF, $rootScope, $rootElement, $document, $$HashMap,
|
||||
$$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow) {
|
||||
|
||||
var activeAnimationsLookup = new $$HashMap();
|
||||
var disabledElementsLookup = new $$HashMap();
|
||||
var animationsEnabled = null;
|
||||
|
||||
function postDigestTaskFactory() {
|
||||
var postDigestCalled = false;
|
||||
return function(fn) {
|
||||
// we only issue a call to postDigest before
|
||||
// it has first passed. This prevents any callbacks
|
||||
// from not firing once the animation has completed
|
||||
// since it will be out of the digest cycle.
|
||||
if (postDigestCalled) {
|
||||
fn();
|
||||
} else {
|
||||
$rootScope.$$postDigest(function() {
|
||||
postDigestCalled = true;
|
||||
fn();
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Wait until all directive and route-related templates are downloaded and
|
||||
// compiled. The $templateRequest.totalPendingRequests variable keeps track of
|
||||
// all of the remote templates being currently downloaded. If there are no
|
||||
@@ -137,14 +155,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
return matches;
|
||||
}
|
||||
|
||||
function triggerCallback(event, element, phase, data) {
|
||||
$$rAF(function() {
|
||||
forEach(findCallbacks(element, event), function(callback) {
|
||||
callback(element, phase, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
on: function(event, container, callback) {
|
||||
var node = extractElementNode(container);
|
||||
@@ -239,6 +249,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
// These methods will become available after the digest has passed
|
||||
var runner = new $$AnimateRunner();
|
||||
|
||||
// this is used to trigger callbacks in postDigest mode
|
||||
var runInNextPostDigestOrNow = postDigestTaskFactory();
|
||||
|
||||
if (isArray(options.addClass)) {
|
||||
options.addClass = options.addClass.join(' ');
|
||||
}
|
||||
@@ -459,7 +472,20 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
return runner;
|
||||
|
||||
function notifyProgress(runner, event, phase, data) {
|
||||
triggerCallback(event, element, phase, data);
|
||||
runInNextPostDigestOrNow(function() {
|
||||
var callbacks = findCallbacks(element, event);
|
||||
if (callbacks.length) {
|
||||
// do not optimize this call here to RAF because
|
||||
// we don't know how heavy the callback code here will
|
||||
// be and if this code is buffered then this can
|
||||
// lead to a performance regression.
|
||||
$$rAF(function() {
|
||||
forEach(callbacks, function(callback) {
|
||||
callback(element, phase, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
runner.progress(event, phase, data);
|
||||
}
|
||||
|
||||
@@ -502,7 +528,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
}
|
||||
|
||||
function areAnimationsAllowed(element, parentElement, event) {
|
||||
var bodyElementDetected = isMatchingElement(element, $$body) || element[0].nodeName === 'HTML';
|
||||
var bodyElement = jqLite($document[0].body);
|
||||
var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
|
||||
var rootElementDetected = isMatchingElement(element, $rootElement);
|
||||
var parentAnimationDetected = false;
|
||||
var animateChildren;
|
||||
@@ -558,7 +585,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
if (!bodyElementDetected) {
|
||||
// we also need to ensure that the element is or will be apart of the body element
|
||||
// otherwise it is pointless to even issue an animation to be rendered
|
||||
bodyElementDetected = isMatchingElement(parentElement, $$body);
|
||||
bodyElementDetected = isMatchingElement(parentElement, bodyElement);
|
||||
}
|
||||
|
||||
parentElement = parentElement.parent();
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
function $$BodyProvider() {
|
||||
this.$get = ['$document', function($document) {
|
||||
return jqLite($document[0].body);
|
||||
}];
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
/* global angularAnimateModule: true,
|
||||
|
||||
$$BodyProvider,
|
||||
$$AnimateAsyncRunFactory,
|
||||
$$rAFSchedulerFactory,
|
||||
$$AnimateChildrenDirective,
|
||||
@@ -742,8 +741,6 @@
|
||||
* Click here {@link ng.$animate to learn more about animations with `$animate`}.
|
||||
*/
|
||||
angular.module('ngAnimate', [])
|
||||
.provider('$$body', $$BodyProvider)
|
||||
|
||||
.directive('ngAnimateChildren', $$AnimateChildrenDirective)
|
||||
.factory('$$rAFScheduler', $$rAFSchedulerFactory)
|
||||
|
||||
|
||||
@@ -325,6 +325,9 @@ angular.module('ngMessages', [])
|
||||
controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
|
||||
var ctrl = this;
|
||||
var latestKey = 0;
|
||||
var nextAttachId = 0;
|
||||
|
||||
this.getAttachId = function getAttachId() { return nextAttachId++; };
|
||||
|
||||
var messages = this.messages = {};
|
||||
var renderLater, cachedCollection;
|
||||
@@ -636,11 +639,15 @@ function ngMessageDirectiveFactory(restrict) {
|
||||
$animate.enter(elm, null, element);
|
||||
currentElement = elm;
|
||||
|
||||
// Each time we attach this node to a message we get a new id that we can match
|
||||
// when we are destroying the node later.
|
||||
var $$attachId = currentElement.$$attachId = ngMessagesCtrl.getAttachId();
|
||||
|
||||
// in the event that the parent element is destroyed
|
||||
// by any other structural directive then it's time
|
||||
// to deregister the message from the controller
|
||||
currentElement.on('$destroy', function() {
|
||||
if (currentElement) {
|
||||
if (currentElement && currentElement.$$attachId === $$attachId) {
|
||||
ngMessagesCtrl.deregister(commentNode);
|
||||
messageCtrl.detach();
|
||||
}
|
||||
|
||||
@@ -115,6 +115,38 @@ describe("$animateCss", function() {
|
||||
expect(cancelSpy).toHaveBeenCalled();
|
||||
expect(doneSpy).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it("should not bother applying the provided [from] and [to] styles to the element if [cleanupStyles] is present",
|
||||
inject(function($animateCss, $rootScope) {
|
||||
|
||||
var animator = $animateCss(element, {
|
||||
cleanupStyles: true,
|
||||
from: { width: '100px' },
|
||||
to: { width: '900px', height: '1000px' }
|
||||
});
|
||||
|
||||
assertStyleIsEmpty(element, 'width');
|
||||
assertStyleIsEmpty(element, 'height');
|
||||
|
||||
var runner = animator.start();
|
||||
|
||||
assertStyleIsEmpty(element, 'width');
|
||||
assertStyleIsEmpty(element, 'height');
|
||||
|
||||
triggerRAF();
|
||||
|
||||
assertStyleIsEmpty(element, 'width');
|
||||
assertStyleIsEmpty(element, 'height');
|
||||
|
||||
runner.end();
|
||||
|
||||
assertStyleIsEmpty(element, 'width');
|
||||
assertStyleIsEmpty(element, 'height');
|
||||
|
||||
function assertStyleIsEmpty(element, prop) {
|
||||
expect(element[0].style.getPropertyValue(prop)).toBeFalsy();
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -3577,6 +3577,22 @@ describe('$compile', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to interpolate attribute names which are present in Object.prototype', function() {
|
||||
var attrs;
|
||||
module(function() {
|
||||
directive('attrExposer', valueFn({
|
||||
link: function($scope, $element, $attrs) {
|
||||
attrs = $attrs;
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile, $rootScope) {
|
||||
$compile('<div attr-exposer to-string="{{1 + 1}}">')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(attrs.toString).toBe('2');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should not initialize scope value if optional expression binding is not passed', inject(function($compile) {
|
||||
compile('<div my-component></div>');
|
||||
|
||||
@@ -468,12 +468,12 @@ describe('ngClass animations', function() {
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($compile, $rootScope, $browser, $rootElement, $animate, $timeout, $$body) {
|
||||
inject(function($compile, $rootScope, $browser, $rootElement, $animate, $document) {
|
||||
$animate.enabled(true);
|
||||
|
||||
$rootScope.val = 'crazy';
|
||||
element = angular.element('<div ng-class="val"></div>');
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$compile(element)($rootScope);
|
||||
|
||||
|
||||
@@ -2029,7 +2029,9 @@ describe('ngOptions', function() {
|
||||
expect(option.text()).toBe('is blank');
|
||||
});
|
||||
|
||||
it('should support option without a value attribute', function() {
|
||||
it('should be ignored when it has no value attribute', function() {
|
||||
// The option value is set to the textContent if there's no value attribute,
|
||||
// so in that case it doesn't count as a blank option
|
||||
createSingleSelect('<option>--select--</option>');
|
||||
scope.$apply(function() {
|
||||
scope.values = [{name: 'A'}, {name: 'B'}, {name: 'C'}];
|
||||
@@ -2084,6 +2086,30 @@ describe('ngOptions', function() {
|
||||
expect(element[0].selectedIndex).toEqual(0);
|
||||
expect(scope.selected).toEqual([]);
|
||||
});
|
||||
|
||||
|
||||
it('should be possible to use ngIf in the blank option', function() {
|
||||
var option;
|
||||
createSingleSelect('<option ng-if="isBlank" value="">blank</option>');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.values = [{name: 'A'}];
|
||||
scope.isBlank = true;
|
||||
});
|
||||
|
||||
expect(element.find('option').length).toBe(2);
|
||||
option = element.find('option').eq(0);
|
||||
expect(option.val()).toBe('');
|
||||
expect(option.text()).toBe('blank');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.isBlank = false;
|
||||
});
|
||||
|
||||
expect(element.find('option').length).toBe(1);
|
||||
option = element.find('option').eq(0);
|
||||
expect(option.text()).toBe('A');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ describe('filters', function() {
|
||||
it('should format according different separators', function() {
|
||||
var num = formatNumber(1234567.1, pattern, '.', ',', 2);
|
||||
expect(num).toBe('1.234.567,10');
|
||||
num = formatNumber(1e-14, pattern, '.', ',', 14);
|
||||
expect(num).toBe('0,00000000000001');
|
||||
});
|
||||
|
||||
it('should format with or without fractionSize', function() {
|
||||
|
||||
@@ -233,6 +233,13 @@ describe('$httpBackend', function() {
|
||||
expect(MockXhr.$$lastInstance.withCredentials).toBe(true);
|
||||
});
|
||||
|
||||
it('should call $xhrFactory with method and url', function() {
|
||||
var mockXhrFactory = jasmine.createSpy('mockXhrFactory').andCallFake(createMockXhr);
|
||||
$backend = createHttpBackend($browser, mockXhrFactory, $browser.defer, callbacks, fakeDocument);
|
||||
$backend('GET', '/some-url', 'some-data', noop);
|
||||
expect(mockXhrFactory).toHaveBeenCalledWith('GET', '/some-url');
|
||||
});
|
||||
|
||||
|
||||
describe('responseType', function() {
|
||||
|
||||
|
||||
@@ -2692,6 +2692,15 @@ describe('parser', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should prevent the exploit', function() {
|
||||
expect(function() {
|
||||
scope.$eval('(1)[{0: "__proto__", 1: "__proto__", 2: "__proto__", 3: "safe", length: 4, toString: [].pop}].foo = 1');
|
||||
}).toThrow();
|
||||
if (!msie || msie > 10) {
|
||||
expect((1)['__proto__'].foo).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('should prevent the exploit', function() {
|
||||
expect(function() {
|
||||
scope.$eval('' +
|
||||
@@ -2703,6 +2712,35 @@ describe('parser', function() {
|
||||
'');
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('should prevent assigning in the context of a constructor', function() {
|
||||
expect(function() {
|
||||
scope.$eval("''.constructor.join");
|
||||
}).not.toThrow();
|
||||
expect(function() {
|
||||
scope.$eval("''.constructor.join = ''.constructor.join");
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
scope.$eval("''.constructor[0] = ''");
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
scope.$eval("(0).constructor[0] = ''");
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
scope.$eval("{}.constructor[0] = ''");
|
||||
}).toThrow();
|
||||
// foo.constructor is the object constructor.
|
||||
expect(function() {
|
||||
scope.$eval("foo.constructor[0] = ''", {foo: {}});
|
||||
}).toThrow();
|
||||
// foo.constructor is not a constructor.
|
||||
expect(function() {
|
||||
scope.$eval("foo.constructor[0] = ''", {foo: {constructor: ''}});
|
||||
}).not.toThrow();
|
||||
expect(function() {
|
||||
scope.$eval("objConstructor = {}.constructor; objConstructor.join = ''");
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call the function from the received instance and not from a new one', function() {
|
||||
|
||||
@@ -121,7 +121,7 @@ describe("ngAnimate $$animateCssDriver", function() {
|
||||
var from, to, fromAnimation, toAnimation;
|
||||
|
||||
beforeEach(module(function() {
|
||||
return function($rootElement, $$body) {
|
||||
return function($rootElement, $document) {
|
||||
from = element;
|
||||
to = jqLite('<div></div>');
|
||||
fromAnimation = { element: from, event: 'enter' };
|
||||
@@ -129,8 +129,14 @@ describe("ngAnimate $$animateCssDriver", function() {
|
||||
$rootElement.append(from);
|
||||
$rootElement.append(to);
|
||||
|
||||
// we need to do this so that style detection works
|
||||
$$body.append($rootElement);
|
||||
var doc = $document[0];
|
||||
|
||||
// there is one test in here that expects the rootElement
|
||||
// to superceed the body node
|
||||
if (!$rootElement[0].contains(doc.body)) {
|
||||
// we need to do this so that style detection works
|
||||
jqLite(doc.body).append($rootElement);
|
||||
}
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -975,6 +981,39 @@ describe("ngAnimate $$animateCssDriver", function() {
|
||||
|
||||
expect(completed).toBe(true);
|
||||
}));
|
||||
|
||||
it("should use <body> as the element container if the rootElement exists outside of the <body> tag", function() {
|
||||
module(function($provide) {
|
||||
$provide.factory('$rootElement', function($document) {
|
||||
return jqLite($document[0].querySelector('html'));
|
||||
});
|
||||
});
|
||||
inject(function($rootElement, $rootScope, $animate, $document) {
|
||||
ss.addRule('.ending-element', 'width:9999px; height:6666px; display:inline-block;');
|
||||
|
||||
var fromAnchor = jqLite('<div></div>');
|
||||
from.append(fromAnchor);
|
||||
|
||||
var toAnchor = jqLite('<div></div>');
|
||||
to.append(toAnchor);
|
||||
|
||||
$rootElement.append(fromAnchor);
|
||||
$rootElement.append(toAnchor);
|
||||
|
||||
var completed = false;
|
||||
driver({
|
||||
from: fromAnimation,
|
||||
to: toAnimation,
|
||||
anchors: [{
|
||||
'out': fromAnchor,
|
||||
'in': toAnchor
|
||||
}]
|
||||
}).start();
|
||||
|
||||
var clone = captureLog[2].element[0];
|
||||
expect(clone.parentNode).toBe($document[0].body);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,12 +35,12 @@ describe("ngAnimate $animateCss", function() {
|
||||
});
|
||||
|
||||
it("should return false if neither transitions or keyframes are supported by the browser",
|
||||
inject(function($animateCss, $sniffer, $rootElement, $$body) {
|
||||
inject(function($animateCss, $sniffer, $rootElement, $document) {
|
||||
|
||||
var animator;
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$sniffer.transitions = $sniffer.animations = false;
|
||||
animator = $animateCss(element, {
|
||||
@@ -54,13 +54,13 @@ describe("ngAnimate $animateCss", function() {
|
||||
if (!browserSupportsCssAnimations()) return;
|
||||
|
||||
it("should not attempt an animation if animations are globally disabled",
|
||||
inject(function($animateCss, $animate, $rootElement, $$body) {
|
||||
inject(function($animateCss, $animate, $rootElement, $document) {
|
||||
|
||||
$animate.enabled(false);
|
||||
|
||||
var animator, element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
animator = $animateCss(element, {
|
||||
duration: 10,
|
||||
@@ -109,9 +109,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
describe("rAF usage", function() {
|
||||
it("should buffer all requests into a single requestAnimationFrame call",
|
||||
inject(function($animateCss, $$rAF, $rootScope, $$body, $rootElement) {
|
||||
inject(function($animateCss, $$rAF, $rootScope, $document, $rootElement) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var count = 0;
|
||||
var runners = [];
|
||||
@@ -149,8 +149,8 @@ describe("ngAnimate $animateCss", function() {
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($animateCss, $$rAF, $$body, $rootElement) {
|
||||
$$body.append($rootElement);
|
||||
inject(function($animateCss, $$rAF, $document, $rootElement) {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
function makeRequest() {
|
||||
var element = jqLite('<div></div>');
|
||||
@@ -169,10 +169,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
describe("animator and runner", function() {
|
||||
var animationDuration = 5;
|
||||
var element, animator;
|
||||
beforeEach(inject(function($animateCss, $rootElement, $$body) {
|
||||
beforeEach(inject(function($animateCss, $rootElement, $document) {
|
||||
element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
animator = $animateCss(element, {
|
||||
event: 'enter',
|
||||
@@ -365,10 +365,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
{ timeStamp: Date.now() + ((delay || 1) * 1000), elapsedTime: duration });
|
||||
}
|
||||
|
||||
beforeEach(inject(function($rootElement, $$body) {
|
||||
beforeEach(inject(function($rootElement, $document) {
|
||||
element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
options = { event: 'enter', structural: true };
|
||||
}));
|
||||
|
||||
@@ -638,9 +638,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
describe("staggering", function() {
|
||||
it("should apply a stagger based when an active ng-EVENT-stagger class with a transition-delay is detected",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', 'transition-delay:0.2s');
|
||||
ss.addRule('.ng-enter', 'transition:2s linear all');
|
||||
@@ -679,9 +679,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should apply a stagger based when for all provided addClass/removeClass CSS classes",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.red-add-stagger,' +
|
||||
'.blue-remove-stagger,' +
|
||||
@@ -749,9 +749,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should block the transition animation between start and animate when staggered",
|
||||
inject(function($animateCss, $$body, $rootElement) {
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', 'transition-delay:0.2s');
|
||||
ss.addRule('.ng-enter', 'transition:2s linear all;');
|
||||
@@ -780,9 +780,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should block (pause) the keyframe animation between start and animate when staggered",
|
||||
inject(function($animateCss, $$body, $rootElement) {
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', prefix + 'animation-delay:0.2s');
|
||||
ss.addRule('.ng-enter', prefix + 'animation:my_animation 2s;');
|
||||
@@ -809,9 +809,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should not apply a stagger if the transition delay value is inherited from a earlier CSS class",
|
||||
inject(function($animateCss, $$body, $rootElement) {
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.transition-animation', 'transition:2s 5s linear all;');
|
||||
|
||||
@@ -828,9 +828,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should apply a stagger only if the transition duration value is zero when inherited from a earlier CSS class",
|
||||
inject(function($animateCss, $$body, $rootElement) {
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.transition-animation', 'transition:2s 5s linear all;');
|
||||
ss.addRule('.transition-animation.ng-enter-stagger',
|
||||
@@ -854,9 +854,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
|
||||
it("should ignore animation staggers if only transition animations were detected",
|
||||
inject(function($animateCss, $$body, $rootElement) {
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', prefix + 'animation-delay:0.2s');
|
||||
ss.addRule('.transition-animation', 'transition:2s 5s linear all;');
|
||||
@@ -874,9 +874,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should ignore transition staggers if only keyframe animations were detected",
|
||||
inject(function($animateCss, $$body, $rootElement) {
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', 'transition-delay:0.2s');
|
||||
ss.addRule('.transition-animation', prefix + 'animation:2s 5s my_animation;');
|
||||
@@ -894,9 +894,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should start on the highest stagger value if both transition and keyframe staggers are used together",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout, $browser) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout, $browser) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', 'transition-delay:0.5s;' +
|
||||
prefix + 'animation-delay:1s');
|
||||
@@ -932,9 +932,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should apply the closing timeout ontop of the stagger timeout",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout, $browser) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout, $browser) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', 'transition-delay:1s;');
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all;');
|
||||
@@ -959,9 +959,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should apply the closing timeout ontop of the stagger timeout with an added delay",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout, $browser) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout, $browser) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', 'transition-delay:1s;');
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all; transition-delay:50s;');
|
||||
@@ -986,9 +986,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should issue a stagger if a stagger value is provided in the options",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
ss.addRule('.ng-enter', 'transition:2s linear all');
|
||||
|
||||
var elm, i, elements = [];
|
||||
@@ -1025,9 +1025,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should only add/remove classes once the stagger timeout has passed",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout) {
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var element = jqLite('<div class="green"></div>');
|
||||
$rootElement.append(element);
|
||||
@@ -1052,13 +1052,13 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
describe("closing timeout", function() {
|
||||
it("should close off the animation after 150% of the animation time has passed",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout) {
|
||||
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, { event: 'enter', structural: true });
|
||||
animator.start();
|
||||
@@ -1075,13 +1075,13 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should close off the animation after 150% of the animation time has passed and consider the detected delay value",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout) {
|
||||
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all; transition-delay:30s;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, { event: 'enter', structural: true });
|
||||
animator.start();
|
||||
@@ -1098,13 +1098,13 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should still resolve the animation once expired",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout, $animate, $rootScope) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout, $animate, $rootScope) {
|
||||
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, { event: 'enter', structural: true });
|
||||
|
||||
@@ -1123,13 +1123,13 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should not resolve/reject after passing if the animation completed successfully",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout, $rootScope, $animate) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout, $rootScope, $animate) {
|
||||
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, { event: 'enter', structural: true });
|
||||
|
||||
@@ -1160,7 +1160,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it("should close all stacked animations after the last timeout runs on the same element",
|
||||
inject(function($animateCss, $$body, $rootElement, $timeout, $animate) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout, $animate) {
|
||||
|
||||
var now = 0;
|
||||
spyOn(Date, 'now').andCallFake(function() {
|
||||
@@ -1177,7 +1177,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
var element = jqLite('<div class="elm"></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
// timeout will be at 1500s
|
||||
animate(element, 'red', doneSpy);
|
||||
@@ -1218,11 +1218,11 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
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) {
|
||||
inject(function($animateCss, $document, $rootElement, $timeout, $animate) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.red', 'transition:1s linear all;');
|
||||
|
||||
@@ -1255,8 +1255,8 @@ describe("ngAnimate $animateCss", function() {
|
||||
}
|
||||
}));
|
||||
|
||||
return function($$body, $rootElement) {
|
||||
$$body.append($rootElement);
|
||||
return function($document, $rootElement) {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -1340,7 +1340,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
});
|
||||
|
||||
it('should avoid applying the same cache to an element a follow-up animation is run on the same element',
|
||||
inject(function($animateCss, $rootElement, $$body) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
|
||||
function endTransition(element, elapsedTime) {
|
||||
browserTrigger(element, 'transitionend',
|
||||
@@ -1357,7 +1357,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
startAnimation(element, 0.5, 'red');
|
||||
expect(element.attr('style')).toContain('transition');
|
||||
@@ -1377,14 +1377,14 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
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) {
|
||||
inject(function($animateCss, $rootElement, $document, $$rAF) {
|
||||
var element = jqLite('<div class="rclass"></div>');
|
||||
var options = {
|
||||
event: 'enter',
|
||||
structural: true
|
||||
};
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
var animator = $animateCss(element, options);
|
||||
expect(animator.$$willAnimate).toBeFalsy();
|
||||
|
||||
@@ -1396,11 +1396,11 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
it('should apply a custom temporary class when a non-structural animation is used',
|
||||
inject(function($animateCss, $rootElement, $$body) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animateCss(element, {
|
||||
event: 'super',
|
||||
@@ -1416,10 +1416,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
describe("structural animations", function() {
|
||||
they('should decorate the element with the ng-$prop CSS class',
|
||||
['enter', 'leave', 'move'], function(event) {
|
||||
inject(function($animateCss, $rootElement, $$body) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animateCss(element, {
|
||||
event: event,
|
||||
@@ -1433,10 +1433,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
they('should decorate the element with the ng-$prop-active CSS class',
|
||||
['enter', 'leave', 'move'], function(event) {
|
||||
inject(function($animateCss, $rootElement, $$body) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, {
|
||||
event: event,
|
||||
@@ -1454,10 +1454,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
they('should remove the ng-$prop and ng-$prop-active CSS classes from the element once the animation is done',
|
||||
['enter', 'leave', 'move'], function(event) {
|
||||
inject(function($animateCss, $rootElement, $$body) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, {
|
||||
event: event,
|
||||
@@ -1511,10 +1511,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
they('should place a CSS transition block after the preparation function to block accidental style changes',
|
||||
['enter', 'leave', 'move', 'addClass', 'removeClass'], function(event) {
|
||||
|
||||
inject(function($animateCss, $rootElement, $$body) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.cool-animation', 'transition:1.5s linear all;');
|
||||
element.addClass('cool-animation');
|
||||
@@ -1541,10 +1541,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
they('should not place a CSS transition block if options.skipBlocking is provided',
|
||||
['enter', 'leave', 'move', 'addClass', 'removeClass'], function(event) {
|
||||
|
||||
inject(function($animateCss, $rootElement, $$body, $window) {
|
||||
inject(function($animateCss, $rootElement, $document, $window) {
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.cool-animation', 'transition:1.5s linear all;');
|
||||
element.addClass('cool-animation');
|
||||
@@ -1582,10 +1582,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
they('should place a CSS transition block after the preparation function even if a duration is provided',
|
||||
['enter', 'leave', 'move', 'addClass', 'removeClass'], function(event) {
|
||||
|
||||
inject(function($animateCss, $rootElement, $$body) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.cool-animation', 'transition:1.5s linear all;');
|
||||
element.addClass('cool-animation');
|
||||
@@ -1616,11 +1616,11 @@ describe("ngAnimate $animateCss", function() {
|
||||
});
|
||||
|
||||
it('should allow multiple events to be animated at the same time',
|
||||
inject(function($animateCss, $rootElement, $$body) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animateCss(element, {
|
||||
event: ['enter', 'leave', 'move'],
|
||||
@@ -1688,10 +1688,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
they('should remove the class-$prop-add and class-$prop-active CSS classes from the element once the animation is done',
|
||||
['enter', 'leave', 'move'], function(event) {
|
||||
inject(function($animateCss, $rootElement, $$body) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var options = {};
|
||||
options.event = event;
|
||||
@@ -1713,7 +1713,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
they('should allow the class duration styles to be recalculated once started if the CSS classes being applied result new transition styles',
|
||||
['add', 'remove'], function(event) {
|
||||
inject(function($animateCss, $rootElement, $$body) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
|
||||
@@ -1728,7 +1728,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
}
|
||||
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var options = {};
|
||||
options[event + 'Class'] = 'natural-class';
|
||||
@@ -1749,13 +1749,13 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
they('should force the class-based values to be applied early if no options.applyClassEarly is used as an option',
|
||||
['enter', 'leave', 'move'], function(event) {
|
||||
inject(function($animateCss, $rootElement, $$body) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
|
||||
ss.addRule('.blue.ng-' + event, 'transition:2s linear all;');
|
||||
|
||||
var element = jqLite('<div class="red"></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var runner = $animateCss(element, {
|
||||
addClass: 'blue',
|
||||
@@ -1790,8 +1790,8 @@ describe("ngAnimate $animateCss", function() {
|
||||
describe("options", function() {
|
||||
var element;
|
||||
beforeEach(module(function() {
|
||||
return function($rootElement, $$body) {
|
||||
$$body.append($rootElement);
|
||||
return function($rootElement, $document) {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
@@ -2741,10 +2741,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
describe("[easing]", function() {
|
||||
|
||||
var element;
|
||||
beforeEach(inject(function($$body, $rootElement) {
|
||||
beforeEach(inject(function($document, $rootElement) {
|
||||
element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
}));
|
||||
|
||||
it("should apply easing to a transition animation if it exists", inject(function($animateCss) {
|
||||
@@ -2786,6 +2786,71 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
});
|
||||
|
||||
describe("[cleanupStyles]", function() {
|
||||
it("should cleanup [from] and [to] styles that have been applied for the animation when true",
|
||||
inject(function($animateCss) {
|
||||
|
||||
var runner = $animateCss(element, {
|
||||
duration: 1,
|
||||
from: { background: 'gold' },
|
||||
to: { color: 'brown' },
|
||||
cleanupStyles: true
|
||||
}).start();
|
||||
|
||||
assertStyleIsPresent(element, 'background', true);
|
||||
assertStyleIsPresent(element, 'color', false);
|
||||
|
||||
triggerAnimationStartFrame();
|
||||
|
||||
assertStyleIsPresent(element, 'background', true);
|
||||
assertStyleIsPresent(element, 'color', true);
|
||||
|
||||
runner.end();
|
||||
|
||||
assertStyleIsPresent(element, 'background', false);
|
||||
assertStyleIsPresent(element, 'color', false);
|
||||
|
||||
function assertStyleIsPresent(element, style, bool) {
|
||||
expect(element[0].style[style])[bool ? 'toBeTruthy' : 'toBeFalsy']();
|
||||
}
|
||||
}));
|
||||
|
||||
it('should restore existing overidden styles already present on the element when true',
|
||||
inject(function($animateCss) {
|
||||
|
||||
element.css('height', '100px');
|
||||
element.css('width', '111px');
|
||||
|
||||
var runner = $animateCss(element, {
|
||||
duration: 1,
|
||||
from: { height: '200px', 'font-size':'66px' },
|
||||
to: { height: '300px', 'font-size': '99px', width: '222px' },
|
||||
cleanupStyles: true
|
||||
}).start();
|
||||
|
||||
assertStyle(element, 'height', '200px');
|
||||
assertStyle(element, 'font-size', '66px');
|
||||
assertStyle(element, 'width', '111px');
|
||||
|
||||
triggerAnimationStartFrame();
|
||||
|
||||
assertStyle(element, 'height', '300px');
|
||||
assertStyle(element, 'width', '222px');
|
||||
assertStyle(element, 'font-size', '99px');
|
||||
|
||||
runner.end();
|
||||
|
||||
assertStyle(element, 'width', '111px');
|
||||
assertStyle(element, 'height', '100px');
|
||||
|
||||
expect(element[0].style.getPropertyValue('font-size')).not.toBe('66px');
|
||||
|
||||
function assertStyle(element, prop, value) {
|
||||
expect(element[0].style.getPropertyValue(prop)).toBe(value);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
it('should round up long elapsedTime values to close off a CSS3 animation',
|
||||
inject(function($animateCss) {
|
||||
|
||||
@@ -2812,13 +2877,13 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
describe('SVG', function() {
|
||||
it('should properly apply transitions on an SVG element',
|
||||
inject(function($animateCss, $rootScope, $compile, $$body, $rootElement) {
|
||||
inject(function($animateCss, $rootScope, $compile, $document, $rootElement) {
|
||||
|
||||
var element = $compile('<svg width="500" height="500">' +
|
||||
'<circle cx="15" cy="5" r="100" fill="orange" />' +
|
||||
'</svg>')($rootScope);
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
$rootElement.append(element);
|
||||
|
||||
$animateCss(element, {
|
||||
|
||||
@@ -26,12 +26,12 @@ describe("animations", function() {
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($animate, $rootScope, $$body) {
|
||||
inject(function($animate, $rootScope, $document) {
|
||||
$animate.enabled(true);
|
||||
|
||||
element = jqLite('<div></div>');
|
||||
|
||||
$animate.enter(element, $$body);
|
||||
$animate.enter(element, jqLite($document[0].body));
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(capturedAnimation).toBeTruthy();
|
||||
@@ -116,7 +116,7 @@ describe("animations", function() {
|
||||
return overriddenAnimationRunner || defaultFakeAnimationRunner;
|
||||
});
|
||||
|
||||
return function($rootElement, $q, $animate, $$AnimateRunner, $$body) {
|
||||
return function($rootElement, $q, $animate, $$AnimateRunner, $document) {
|
||||
defaultFakeAnimationRunner = new $$AnimateRunner();
|
||||
$animate.enabled(true);
|
||||
|
||||
@@ -126,7 +126,7 @@ describe("animations", function() {
|
||||
|
||||
$rootElement.append(parent);
|
||||
$rootElement.append(parent2);
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -749,7 +749,7 @@ describe("animations", function() {
|
||||
}));
|
||||
|
||||
it("should disable all child animations for atleast one turn when a structural animation is issued",
|
||||
inject(function($animate, $rootScope, $compile, $$body, $rootElement, $$AnimateRunner) {
|
||||
inject(function($animate, $rootScope, $compile, $document, $rootElement, $$AnimateRunner) {
|
||||
|
||||
element = $compile(
|
||||
'<div><div class="if-animation" ng-if="items.length">' +
|
||||
@@ -759,7 +759,7 @@ describe("animations", function() {
|
||||
'</div></div>'
|
||||
)($rootScope);
|
||||
|
||||
$$body.append($rootElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
$rootElement.append(element);
|
||||
|
||||
var runner = new $$AnimateRunner();
|
||||
@@ -890,7 +890,6 @@ describe("animations", function() {
|
||||
});
|
||||
|
||||
$rootScope.$digest();
|
||||
$animate.flush();
|
||||
expect(capturedAnimation).toBeTruthy();
|
||||
expect(runner1done).toBeFalsy();
|
||||
|
||||
@@ -910,7 +909,6 @@ describe("animations", function() {
|
||||
});
|
||||
|
||||
$rootScope.$digest();
|
||||
$animate.flush();
|
||||
expect(capturedAnimation).toBeTruthy();
|
||||
expect(runner2done).toBeFalsy();
|
||||
|
||||
@@ -990,8 +988,6 @@ describe("animations", function() {
|
||||
var doneHandler = jasmine.createSpy('addClass done');
|
||||
runner.done(doneHandler);
|
||||
|
||||
$animate.flush();
|
||||
|
||||
expect(doneHandler).not.toHaveBeenCalled();
|
||||
|
||||
$animate.removeClass(element, 'active-class');
|
||||
@@ -1011,8 +1007,6 @@ describe("animations", function() {
|
||||
var doneHandler = jasmine.createSpy('addClass done');
|
||||
runner.done(doneHandler);
|
||||
|
||||
$animate.flush();
|
||||
|
||||
expect(doneHandler).not.toHaveBeenCalled();
|
||||
|
||||
$animate.addClass(element, 'active-class');
|
||||
@@ -1284,8 +1278,8 @@ describe("animations", function() {
|
||||
return new $$AnimateRunner();
|
||||
};
|
||||
});
|
||||
return function($rootElement, $$body, $animate) {
|
||||
$$body.append($rootElement);
|
||||
return function($rootElement, $document, $animate) {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
parent = jqLite('<div class="parent"></div>');
|
||||
element = jqLite('<div class="element"></div>');
|
||||
child = jqLite('<div class="child"></div>');
|
||||
@@ -1389,14 +1383,14 @@ describe("animations", function() {
|
||||
}));
|
||||
|
||||
it('should allow an element to pinned elsewhere and still be available in animations',
|
||||
inject(function($animate, $compile, $$body, $rootElement, $rootScope) {
|
||||
inject(function($animate, $compile, $document, $rootElement, $rootScope) {
|
||||
|
||||
var innerParent = jqLite('<div></div>');
|
||||
$$body.append(innerParent);
|
||||
jqLite($document[0].body).append(innerParent);
|
||||
innerParent.append($rootElement);
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$$body.append(element);
|
||||
jqLite($document[0].body).append(element);
|
||||
|
||||
$animate.addClass(element, 'red');
|
||||
$rootScope.$digest();
|
||||
@@ -1412,16 +1406,16 @@ describe("animations", function() {
|
||||
}));
|
||||
|
||||
it('should adhere to the disabled state of the hosted parent when an element is pinned',
|
||||
inject(function($animate, $compile, $$body, $rootElement, $rootScope) {
|
||||
inject(function($animate, $compile, $document, $rootElement, $rootScope) {
|
||||
|
||||
var innerParent = jqLite('<div></div>');
|
||||
$$body.append(innerParent);
|
||||
jqLite($document[0].body).append(innerParent);
|
||||
innerParent.append($rootElement);
|
||||
var innerChild = jqLite('<div></div>');
|
||||
$rootElement.append(innerChild);
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$$body.append(element);
|
||||
jqLite($document[0].body).append(element);
|
||||
|
||||
$animate.pin(element, innerChild);
|
||||
|
||||
@@ -1456,17 +1450,17 @@ describe("animations", function() {
|
||||
};
|
||||
});
|
||||
|
||||
return function($$body, $rootElement, $animate) {
|
||||
$$body.append($rootElement);
|
||||
return function($document, $rootElement, $animate) {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
$animate.enabled(true);
|
||||
};
|
||||
}));
|
||||
|
||||
it('should trigger a callback for an enter animation',
|
||||
inject(function($animate, $rootScope, $rootElement, $$body) {
|
||||
inject(function($animate, $rootScope, $rootElement, $document) {
|
||||
|
||||
var callbackTriggered = false;
|
||||
$animate.on('enter', $$body, function() {
|
||||
$animate.on('enter', jqLite($document[0].body), function() {
|
||||
callbackTriggered = true;
|
||||
});
|
||||
|
||||
@@ -1480,12 +1474,12 @@ describe("animations", function() {
|
||||
}));
|
||||
|
||||
it('should fire the callback with the signature of (element, phase, data)',
|
||||
inject(function($animate, $rootScope, $rootElement, $$body) {
|
||||
inject(function($animate, $rootScope, $rootElement, $document) {
|
||||
|
||||
var capturedElement;
|
||||
var capturedPhase;
|
||||
var capturedData;
|
||||
$animate.on('enter', $$body,
|
||||
$animate.on('enter', jqLite($document[0].body),
|
||||
function(element, phase, data) {
|
||||
|
||||
capturedElement = element;
|
||||
@@ -1519,7 +1513,6 @@ describe("animations", function() {
|
||||
element = jqLite('<div></div>');
|
||||
$animate.enter(element, $rootElement);
|
||||
$rootScope.$digest();
|
||||
$animate.flush();
|
||||
|
||||
expect(callbackTriggered).toBe(false);
|
||||
}));
|
||||
@@ -1544,13 +1537,13 @@ describe("animations", function() {
|
||||
}));
|
||||
|
||||
it('should remove all the event-based event listeners when $animate.off(event) is called',
|
||||
inject(function($animate, $rootScope, $rootElement, $$body) {
|
||||
inject(function($animate, $rootScope, $rootElement, $document) {
|
||||
|
||||
element = jqLite('<div></div>');
|
||||
|
||||
var count = 0;
|
||||
$animate.on('enter', element, counter);
|
||||
$animate.on('enter', $$body, counter);
|
||||
$animate.on('enter', jqLite($document[0].body), counter);
|
||||
|
||||
function counter(element, phase) {
|
||||
count++;
|
||||
@@ -1572,13 +1565,13 @@ describe("animations", function() {
|
||||
}));
|
||||
|
||||
it('should remove the container-based event listeners when $animate.off(event, container) is called',
|
||||
inject(function($animate, $rootScope, $rootElement, $$body) {
|
||||
inject(function($animate, $rootScope, $rootElement, $document) {
|
||||
|
||||
element = jqLite('<div></div>');
|
||||
|
||||
var count = 0;
|
||||
$animate.on('enter', element, counter);
|
||||
$animate.on('enter', $$body, counter);
|
||||
$animate.on('enter', jqLite($document[0].body), counter);
|
||||
|
||||
function counter(element, phase) {
|
||||
if (phase === 'start') {
|
||||
@@ -1592,7 +1585,7 @@ describe("animations", function() {
|
||||
|
||||
expect(count).toBe(2);
|
||||
|
||||
$animate.off('enter', $$body);
|
||||
$animate.off('enter', jqLite($document[0].body));
|
||||
|
||||
$animate.enter(element, $rootElement);
|
||||
$rootScope.$digest();
|
||||
@@ -1638,13 +1631,13 @@ describe("animations", function() {
|
||||
}));
|
||||
|
||||
it('should fire a `start` callback when the animation starts with the matching element',
|
||||
inject(function($animate, $rootScope, $rootElement, $$body) {
|
||||
inject(function($animate, $rootScope, $rootElement, $document) {
|
||||
|
||||
element = jqLite('<div></div>');
|
||||
|
||||
var capturedState;
|
||||
var capturedElement;
|
||||
$animate.on('enter', $$body, function(element, phase) {
|
||||
$animate.on('enter', jqLite($document[0].body), function(element, phase) {
|
||||
capturedState = phase;
|
||||
capturedElement = element;
|
||||
});
|
||||
@@ -1658,13 +1651,13 @@ describe("animations", function() {
|
||||
}));
|
||||
|
||||
it('should fire a `close` callback when the animation ends with the matching element',
|
||||
inject(function($animate, $rootScope, $rootElement, $$body) {
|
||||
inject(function($animate, $rootScope, $rootElement, $document) {
|
||||
|
||||
element = jqLite('<div></div>');
|
||||
|
||||
var capturedState;
|
||||
var capturedElement;
|
||||
$animate.on('enter', $$body, function(element, phase) {
|
||||
$animate.on('enter', jqLite($document[0].body), function(element, phase) {
|
||||
capturedState = phase;
|
||||
capturedElement = element;
|
||||
});
|
||||
@@ -1707,6 +1700,69 @@ describe("animations", function() {
|
||||
expect(count).toBe(1);
|
||||
}));
|
||||
|
||||
it('should always detect registered callbacks after one postDigest has fired',
|
||||
inject(function($animate, $rootScope, $rootElement) {
|
||||
|
||||
element = jqLite('<div></div>');
|
||||
|
||||
var spy = jasmine.createSpy();
|
||||
registerCallback();
|
||||
|
||||
var runner = $animate.enter(element, $rootElement);
|
||||
registerCallback();
|
||||
|
||||
$rootScope.$digest();
|
||||
registerCallback();
|
||||
|
||||
expect(spy.callCount).toBe(0);
|
||||
$animate.flush();
|
||||
|
||||
// this is not 3 since the 3rd callback
|
||||
// was added after the first callback
|
||||
// was fired
|
||||
expect(spy.callCount).toBe(2);
|
||||
|
||||
spy.reset();
|
||||
runner.end();
|
||||
|
||||
$animate.flush();
|
||||
|
||||
// now we expect all three callbacks
|
||||
// to fire when the animation ends since
|
||||
// the callback detection happens again
|
||||
expect(spy.callCount).toBe(3);
|
||||
|
||||
function registerCallback() {
|
||||
$animate.on('enter', element, spy);
|
||||
}
|
||||
}));
|
||||
|
||||
it('should use RAF if there are detected callbacks within the hierachy of the element being animated',
|
||||
inject(function($animate, $rootScope, $rootElement, $$rAF) {
|
||||
|
||||
var runner;
|
||||
|
||||
element = jqLite('<div></div>');
|
||||
runner = $animate.enter(element, $rootElement);
|
||||
$rootScope.$digest();
|
||||
runner.end();
|
||||
|
||||
assertRAFsUsed(false);
|
||||
|
||||
var spy = jasmine.createSpy();
|
||||
$animate.on('leave', element, spy);
|
||||
|
||||
runner = $animate.leave(element, $rootElement);
|
||||
$rootScope.$digest();
|
||||
runner.end();
|
||||
|
||||
assertRAFsUsed(true);
|
||||
|
||||
function assertRAFsUsed(bool) {
|
||||
expect($$rAF.queue.length)[bool ? 'toBeGreaterThan' : 'toBe'](0);
|
||||
}
|
||||
}));
|
||||
|
||||
it('leave: should remove the element even if another animation is called after',
|
||||
inject(function($animate, $rootScope, $rootElement) {
|
||||
|
||||
|
||||
@@ -836,8 +836,8 @@ describe('$$animation', function() {
|
||||
element = jqLite('<div></div>');
|
||||
parent = jqLite('<div></div>');
|
||||
|
||||
return function($$AnimateRunner, $q, $rootElement, $$body) {
|
||||
$$body.append($rootElement);
|
||||
return function($$AnimateRunner, $rootElement, $document) {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
$rootElement.append(parent);
|
||||
|
||||
mockedDriverFn = function(element, method, options, domOperation) {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('$$body', function() {
|
||||
beforeEach(module('ngAnimate'));
|
||||
|
||||
it("should inject $document", inject(function($$body, $document) {
|
||||
expect($$body).toEqual(jqLite($document[0].body));
|
||||
}));
|
||||
});
|
||||
@@ -7,12 +7,12 @@ describe('ngAnimate integration tests', function() {
|
||||
|
||||
var element, html, ss;
|
||||
beforeEach(module(function() {
|
||||
return function($rootElement, $document, $$body, $window, $animate) {
|
||||
return function($rootElement, $document, $window, $animate) {
|
||||
$animate.enabled(true);
|
||||
|
||||
ss = createMockStyleSheet($document, $window);
|
||||
|
||||
var body = $$body;
|
||||
var body = jqLite($document[0].body);
|
||||
html = function(element) {
|
||||
body.append($rootElement);
|
||||
$rootElement.append(element);
|
||||
@@ -319,6 +319,38 @@ describe('ngAnimate integration tests', function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should trigger callbacks at the start and end of an animation',
|
||||
inject(function($rootScope, $rootElement, $animate, $compile) {
|
||||
|
||||
ss.addRule('.animate-me', 'transition:2s linear all;');
|
||||
|
||||
var parent = jqLite('<div><div ng-if="exp" class="animate-me"></div></div>');
|
||||
element = parent.find('div');
|
||||
html(parent);
|
||||
|
||||
$compile(parent)($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
var spy = jasmine.createSpy();
|
||||
$animate.on('enter', parent, spy);
|
||||
|
||||
$rootScope.exp = true;
|
||||
$rootScope.$digest();
|
||||
|
||||
element = parent.find('div');
|
||||
|
||||
$animate.flush();
|
||||
|
||||
expect(spy.callCount).toBe(1);
|
||||
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2 });
|
||||
$animate.flush();
|
||||
|
||||
expect(spy.callCount).toBe(2);
|
||||
|
||||
dealoc(element);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('JS animations', function() {
|
||||
|
||||
@@ -372,6 +372,50 @@ describe('ngMessages', function() {
|
||||
expect(trim(element.text())).toEqual("Enter something");
|
||||
}));
|
||||
|
||||
// issue #12856
|
||||
it('should only detach the message object that is associated with the message node being removed',
|
||||
inject(function($rootScope, $compile, $animate) {
|
||||
|
||||
// We are going to spy on the `leave` method to give us control over
|
||||
// when the element is actually removed
|
||||
spyOn($animate, 'leave');
|
||||
|
||||
// Create a basic ng-messages set up
|
||||
element = $compile('<div ng-messages="col">' +
|
||||
' <div ng-message="primary">Enter something</div>' +
|
||||
'</div>')($rootScope);
|
||||
|
||||
// Trigger the message to be displayed
|
||||
$rootScope.col = { primary: true };
|
||||
$rootScope.$digest();
|
||||
expect(messageChildren(element).length).toEqual(1);
|
||||
var oldMessageNode = messageChildren(element)[0];
|
||||
|
||||
// Remove the message
|
||||
$rootScope.col = { primary: undefined };
|
||||
$rootScope.$digest();
|
||||
|
||||
// Since we have spied on the `leave` method, the message node is still in the DOM
|
||||
expect($animate.leave).toHaveBeenCalledOnce();
|
||||
var nodeToRemove = $animate.leave.mostRecentCall.args[0][0];
|
||||
expect(nodeToRemove).toBe(oldMessageNode);
|
||||
$animate.leave.reset();
|
||||
|
||||
// Add the message back in
|
||||
$rootScope.col = { primary: true };
|
||||
$rootScope.$digest();
|
||||
|
||||
// Simulate the animation completing on the node
|
||||
jqLite(nodeToRemove).remove();
|
||||
|
||||
// We should not get another call to `leave`
|
||||
expect($animate.leave).not.toHaveBeenCalled();
|
||||
|
||||
// There should only be the new message node
|
||||
expect(messageChildren(element).length).toEqual(1);
|
||||
var newMessageNode = messageChildren(element)[0];
|
||||
expect(newMessageNode).not.toBe(oldMessageNode);
|
||||
}));
|
||||
|
||||
it('should render animations when the active/inactive classes are added/removed', function() {
|
||||
module('ngAnimate');
|
||||
|
||||
Reference in New Issue
Block a user