diff --git a/benchmarks/parsed-expressions-bp/app.js b/benchmarks/parsed-expressions-bp/app.js new file mode 100755 index 000000000000..e8206fd9c915 --- /dev/null +++ b/benchmarks/parsed-expressions-bp/app.js @@ -0,0 +1,86 @@ +var app = angular.module('parsedExpressionBenchmark', []); + +app.config(function($compileProvider) { + if ($compileProvider.debugInfoEnabled) { + $compileProvider.debugInfoEnabled(false); + } +}); + +app.filter('noop', function() { + return function(input) { + return input; + }; +}); + +//Executes the specified expression as a watcher +app.directive('bmPeWatch', function() { + return { + restrict: 'A', + compile: function($element, $attrs) { + $element.text( $attrs.bmPeWatch ); + return function($scope, $element, $attrs) { + $scope.$watch($attrs.bmPeWatch, function(val) { + $element.text(val); + + }); + }; + } + }; +}); + +//Executes the specified expression as a watcher +//Adds a simple wrapper method to allow use of $watch instead of $watchCollection +app.directive('bmPeWatchLiteral', function($parse) { + function retZero() { + return 0; + } + + return { + restrict: 'A', + compile: function($element, $attrs) { + $element.text( $attrs.bmPeWatchLiteral ); + return function($scope, $element, $attrs) { + $scope.$watch( $parse($attrs.bmPeWatchLiteral, retZero) ); + }; + } + }; +}); + +app.controller('DataController', function($scope, $rootScope) { + var totalRows = 10000; + + var data = $scope.data = []; + + var star = '*'; + + $scope.func = function() { return star;}; + + for (var i=0; i +
+
+

+ Tests the execution of $parse()ed expressions. Each test tries to isolate specific expression types. Expressions should (probably) not be constant so they get evaluated per digest. +

+ +
    +
  • + + +
  • + +
  • + + +
  • + +
  • + + +
  • + +
  • + + +
  • + +
  • + + +
  • + +
  • + + +
  • + +
  • + + +
  • + +
  • + + +
  • + +
  • + + +
  • +
+ + + +
    +
  • + + + + + + + + + + + +
  • + +
  • + + + + + + + + + + + + +
  • + +
  • + + + + + + + + + + + + +
  • + +
  • + + + + + + + + + + + + + +
  • + +
  • + + + + + + + + + + + + + +
  • + +
  • + + + + + + + + + + + + +
  • + +
  • + + + + + + + + + + + + +
  • + +
  • + + + + + + +
  • + +
  • + + + + + + + + + + + + +
  • +
+
+
+ diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index ec9ba4ac5b61..75a50ffac8ea 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -3205,7 +3205,7 @@ "version": "0.0.1" }, "socket.io": { - "version": "0.9.17", + "version": "0.9.16", "dependencies": { "socket.io-client": { "version": "0.9.16", @@ -3214,13 +3214,13 @@ "version": "1.2.5" }, "ws": { - "version": "0.4.32", + "version": "0.4.31", "dependencies": { "commander": { - "version": "2.1.0" + "version": "0.6.1" }, "nan": { - "version": "1.0.0" + "version": "0.3.2" }, "tinycolor": { "version": "0.0.1" @@ -3255,12 +3255,10 @@ } }, "chokidar": { - "version": "0.8.4", + "version": "0.8.2", "dependencies": { "fsevents": { - "version": "0.2.1", - "from": "git://github.com/pipobscure/fsevents#7dcdf9fa3f8956610fd6f69f72c67bace2de7138", - "resolved": "git://github.com/pipobscure/fsevents#7dcdf9fa3f8956610fd6f69f72c67bace2de7138", + "version": "0.2.0", "dependencies": { "nan": { "version": "0.8.0" @@ -3273,21 +3271,10 @@ } }, "glob": { - "version": "3.2.11", + "version": "3.2.9", "dependencies": { "inherits": { "version": "2.0.1" - }, - "minimatch": { - "version": "0.3.0", - "dependencies": { - "lru-cache": { - "version": "2.5.0" - }, - "sigmund": { - "version": "1.0.0" - } - } } } }, @@ -3321,12 +3308,7 @@ "version": "0.3.2" }, "mkdirp": { - "version": "0.5.0", - "dependencies": { - "minimist": { - "version": "0.0.8" - } - } + "version": "0.4.0" }, "ncp": { "version": "0.4.2" @@ -3342,12 +3324,12 @@ "version": "0.0.2" }, "minimist": { - "version": "0.0.10" + "version": "0.0.8" } } }, "rimraf": { - "version": "2.2.8" + "version": "2.2.6" }, "q": { "version": "0.9.7" @@ -3362,7 +3344,7 @@ "version": "1.2.11" }, "log4js": { - "version": "0.6.20", + "version": "0.6.14", "dependencies": { "async": { "version": "0.1.15" @@ -3371,7 +3353,7 @@ "version": "1.1.4" }, "readable-stream": { - "version": "1.0.31", + "version": "1.0.27-1", "dependencies": { "core-util-is": { "version": "1.0.1" @@ -3380,7 +3362,7 @@ "version": "0.0.1" }, "string_decoder": { - "version": "0.10.31" + "version": "0.10.25-1" }, "inherits": { "version": "2.0.1" @@ -3390,7 +3372,7 @@ } }, "useragent": { - "version": "2.0.9", + "version": "2.0.8", "dependencies": { "lru-cache": { "version": "2.2.4" @@ -3439,7 +3421,7 @@ "version": "0.0.3" }, "debug": { - "version": "0.8.1" + "version": "0.8.0" }, "methods": { "version": "0.1.0" @@ -3454,7 +3436,7 @@ "version": "2.2.0", "dependencies": { "readable-stream": { - "version": "1.1.13", + "version": "1.1.13-1", "dependencies": { "core-util-is": { "version": "1.0.1" @@ -3463,7 +3445,7 @@ "version": "0.0.1" }, "string_decoder": { - "version": "0.10.31" + "version": "0.10.25-1" }, "inherits": { "version": "2.0.1" @@ -3478,7 +3460,7 @@ } }, "source-map": { - "version": "0.1.38", + "version": "0.1.33", "dependencies": { "amdefine": { "version": "0.1.0" diff --git a/src/.jshintrc b/src/.jshintrc index 99ea5a68de32..5eda6c464777 100644 --- a/src/.jshintrc +++ b/src/.jshintrc @@ -124,6 +124,8 @@ "jqLiteAddNodes": false, "jqLiteController": false, "jqLiteInheritedData": false, + "jqLiteBuildFragment": false, + "jqLiteParseHTML": false, "getBooleanAttrName": false, "getAliasedAttrName": false, "createEventHandler": false, diff --git a/src/ng/compile.js b/src/ng/compile.js index 13d0488aa59a..f89b97feee94 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -997,10 +997,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { ? JQLitePrototype.clone.call(namespaceAdaptedCompileNodes) // IMPORTANT!!! : namespaceAdaptedCompileNodes; - if ( $linkNode.length === 0 && parentBoundTranscludeFn ) { - $linkNode = parentBoundTranscludeFn(scope); - } - if (transcludeControllers) { for (var controllerName in transcludeControllers) { $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance); diff --git a/src/ng/directive/ngInclude.js b/src/ng/directive/ngInclude.js index e78e72bc1910..7c6f0856e714 100644 --- a/src/ng/directive/ngInclude.js +++ b/src/ng/directive/ngInclude.js @@ -267,6 +267,18 @@ var ngIncludeFillContentDirective = ['$compile', priority: -400, require: 'ngInclude', link: function(scope, $element, $attr, ctrl) { + if (/SVG/.test($element[0].toString())) { + // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not + // support innerHTML, so detect this here and try to generate the contents + // specially. + $element.empty(); + $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope, + function namespaceAdaptedClone(clone) { + $element.append(clone); + }, undefined, undefined, $element); + return; + } + $element.html(ctrl.template); $compile($element.contents())(scope); } diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index f1fb5a3a1539..3ab8e3b115a4 100644 --- a/src/ng/directive/ngRepeat.js +++ b/src/ng/directive/ngRepeat.js @@ -379,7 +379,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { block = lastBlockMap[blockKey]; elementsToRemove = getBlockNodes(block.clone); $animate.leave(elementsToRemove); - if (elementsToRemove[0].parent) { + if (elementsToRemove[0].parentNode) { // if the element was not removed yet because of pending animation, mark it as deleted // so that we can ignore it later for (index = 0, length = elementsToRemove.length; index < length; index++) { diff --git a/src/ng/parse.js b/src/ng/parse.js index 29fb42233a20..69e71e201698 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -80,12 +80,21 @@ function ensureSafeFunction(obj, fullExpression) { } } +//Keyword constants +var CONSTANTS = createMap(); +forEach({ + 'null': function() { return null; }, + 'true': function() { return true; }, + 'false': function() { return false; }, + 'undefined': function() {} +}, function(constantGetter, name) { + constantGetter.constant = constantGetter.literal = constantGetter.sharedGetter = true; + CONSTANTS[name] = constantGetter; +}); + +//Operators - will be wrapped by binaryFn/unaryFn/assignment/filter var OPERATORS = extend(createMap(), { /* jshint bitwise : false */ - 'null':function(){return null;}, - 'true':function(){return true;}, - 'false':function(){return false;}, - undefined:noop, '+':function(self, locals, a,b){ a=a(self, locals); b=b(self, locals); if (isDefined(a)) { @@ -305,30 +314,11 @@ Lexer.prototype = { } } - - var token = { + this.tokens.push({ index: start, - text: ident - }; - - var fn = OPERATORS[ident]; - - if (fn) { - token.fn = fn; - token.constant = true; - } else { - var getter = getterFn(ident, this.options, expression); - // TODO(perf): consider exposing the getter reference - token.fn = extend(function $parsePathGetter(self, locals) { - return getter(self, locals); - }, { - assign: function(self, value) { - return setter(self, ident, value, expression); - } - }); - } - - this.tokens.push(token); + text: ident, + fn: CONSTANTS[ident] || getterFn(ident, this.options, expression) + }); if (methodName) { this.tokens.push({ @@ -397,6 +387,7 @@ var Parser = function (lexer, $filter, options) { Parser.ZERO = extend(function () { return 0; }, { + sharedGetter: true, constant: true }); @@ -747,7 +738,7 @@ Parser.prototype = { if (args) { var i = argsFn.length; while (i--) { - args[i] = argsFn[i](scope, locals); + args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText); } } @@ -835,11 +826,12 @@ Parser.prototype = { ////////////////////////////////////////////////// function setter(obj, path, setValue, fullExp) { + ensureSafeObject(obj, fullExp); var element = path.split('.'), key; for (var i = 0; element.length > 1; i++) { key = ensureSafeMemberName(element.shift(), fullExp); - var propertyObj = obj[key]; + var propertyObj = ensureSafeObject(obj[key], fullExp); if (!propertyObj) { propertyObj = {}; obj[key] = propertyObj; @@ -847,7 +839,6 @@ function setter(obj, path, setValue, fullExp) { obj = propertyObj; } key = ensureSafeMemberName(element.shift(), fullExp); - ensureSafeObject(obj, fullExp); ensureSafeObject(obj[key], fullExp); obj[key] = setValue; return setValue; @@ -935,9 +926,14 @@ function getterFn(path, options, fullExp) { var evaledFnGetter = new Function('s', 'l', code); // s=scope, l=locals /* jshint +W054 */ evaledFnGetter.toString = valueFn(code); + evaledFnGetter.assign = function(self, value) { + return setter(self, path, value, path); + }; + fn = evaledFnGetter; } + fn.sharedGetter = true; getterFnCache[path] = fn; return fn; } @@ -1004,6 +1000,21 @@ function $ParseProvider() { this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { $parseOptions.csp = $sniffer.csp; + function wrapSharedExpression(exp) { + var wrapped = exp; + + if (exp.sharedGetter) { + wrapped = function $parseWrapper(self, locals) { + return exp(self, locals); + }; + wrapped.literal = exp.literal; + wrapped.constant = exp.constant; + wrapped.assign = exp.assign; + } + + return wrapped; + } + return function $parse(exp, interceptorFn) { var parsedExpression, oneTime, cacheKey; @@ -1026,6 +1037,9 @@ function $ParseProvider() { if (parsedExpression.constant) { parsedExpression.$$watchDelegate = constantWatchDelegate; } else if (oneTime) { + //oneTime is not part of the exp passed to the Parser so we may have to + //wrap the parsedExpression before adding a $$watchDelegate + parsedExpression = wrapSharedExpression(parsedExpression); parsedExpression.$$watchDelegate = parsedExpression.literal ? oneTimeLiteralWatchDelegate : oneTimeWatchDelegate; } diff --git a/test/helpers/testabilityPatch.js b/test/helpers/testabilityPatch.js index 31a5a63f76b1..47d1742bfe9c 100644 --- a/test/helpers/testabilityPatch.js +++ b/test/helpers/testabilityPatch.js @@ -323,14 +323,17 @@ function provideLog($provide) { } function pending() { - dump('PENDING'); + window.dump('PENDING'); } function trace(name) { - dump(new Error(name).stack); + window.dump(new Error(name).stack); } -var karmaDump = dump; +var karmaDump = window.dump || function() { + window.console.log.apply(window.console, arguments); +}; + window.dump = function () { karmaDump.apply(undefined, map(arguments, function(arg) { return angular.mock.dump(arg); diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index f7d7fd1f4b95..8fbb10b0cd99 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -4927,37 +4927,6 @@ describe('$compile', function() { }); - describe('collocated nested transcludes', function() { - - beforeEach(module(function($compileProvider) { - - $compileProvider.directive('inner', valueFn({ - restrict: 'E', - transclude: true, - template: '' - })); - - $compileProvider.directive('outer', valueFn({ - restrict: 'E', - transclude: true, - template: '' - })); - - })); - - - // Issue #8914 - it('should render nested transclusion at the root of a template', inject(function($compile, $rootScope) { - - element = $compile('
transcluded content
')($rootScope); - $rootScope.$digest(); - expect(element.text()).toEqual('transcluded content'); - - })); - - }); - - describe('nested transcludes', function() { beforeEach(module(function($compileProvider) { diff --git a/test/ng/directive/ngIncludeSpec.js b/test/ng/directive/ngIncludeSpec.js index 4dca3f803dc8..e3f366be6e7e 100644 --- a/test/ng/directive/ngIncludeSpec.js +++ b/test/ng/directive/ngIncludeSpec.js @@ -362,6 +362,46 @@ describe('ngInclude', function() { })); + it('should construct SVG template elements with correct namespace', function() { + if (!window.SVGRectElement) return; + module(function($compileProvider) { + $compileProvider.directive('test', valueFn({ + templateNamespace: 'svg', + templateUrl: 'my-rect.html', + replace: true + })); + }); + inject(function($compile, $rootScope, $httpBackend) { + $httpBackend.expectGET('my-rect.html').respond(''); + $httpBackend.expectGET('include.svg').respond(''); + element = $compile('')($rootScope); + $httpBackend.flush(); + var child = element.find('rect'); + expect(child.length).toBe(2); + expect(child[0] instanceof SVGRectElement).toBe(true); + }); + }); + + + it('should compile only the template content of an SVG template', function() { + if (!window.SVGRectElement) return; + module(function($compileProvider) { + $compileProvider.directive('test', valueFn({ + templateNamespace: 'svg', + templateUrl: 'my-rect.html', + replace: true + })); + }); + inject(function($compile, $rootScope, $httpBackend) { + $httpBackend.expectGET('my-rect.html').respond(''); + $httpBackend.expectGET('include.svg').respond(''); + element = $compile('')($rootScope); + $httpBackend.flush(); + expect(element.find('a').length).toBe(0); + }); + }); + + describe('autoscroll', function() { var autoScrollSpy; diff --git a/test/ng/directive/ngRepeatSpec.js b/test/ng/directive/ngRepeatSpec.js index 25378143d0a3..6ee1cbd06bd8 100644 --- a/test/ng/directive/ngRepeatSpec.js +++ b/test/ng/directive/ngRepeatSpec.js @@ -1332,6 +1332,28 @@ describe('ngRepeat and transcludes', function() { dealoc(element); }); }); + + + it('should work with svg elements when the svg container is transcluded', function() { + module(function($compileProvider) { + $compileProvider.directive('svgContainer', function() { + return { + template: '', + replace: true, + transclude: true + }; + }); + }); + inject(function($compile, $rootScope) { + var element = $compile('')($rootScope); + $rootScope.rows = [1]; + $rootScope.$apply(); + + var circle = element.find('circle'); + expect(circle[0].toString()).toMatch(/SVG/); + dealoc(element); + }); + }); }); describe('ngRepeat animations', function() { @@ -1343,6 +1365,7 @@ describe('ngRepeat animations', function() { return element; } + beforeEach(module('ngAnimate')); beforeEach(module('ngAnimateMock')); beforeEach(module(function() { @@ -1355,8 +1378,7 @@ describe('ngRepeat animations', function() { })); afterEach(function(){ - dealoc(body); - dealoc(element); + body.empty(); }); it('should fire off the enter animation', @@ -1424,6 +1446,42 @@ describe('ngRepeat animations', function() { expect(item.element.text()).toBe('2'); })); + it('should not change the position of the block that is being animated away via a leave animation', + inject(function($compile, $rootScope, $animate, $document, $window, $sniffer, $timeout) { + if (!$sniffer.transitions) return; + + var item; + var ss = createMockStyleSheet($document, $window); + + try { + + $animate.enabled(true); + + ss.addRule('.animate-me div', + '-webkit-transition:1s linear all; transition:1s linear all;'); + + element = $compile(html('
' + + '
{{ item }}
' + + '
'))($rootScope); + + $rootScope.items = ['1','2','3']; + $rootScope.$digest(); + expect(element.text()).toBe('123'); + + $rootScope.items = ['1','3']; + $rootScope.$digest(); + + expect(element.text()).toBe('123'); // the original order should be preserved + $animate.triggerReflow(); + $timeout.flush(1500); // 1s * 1.5 closing buffer + expect(element.text()).toBe('13'); + + } finally { + ss.destroy(); + } + }) + ); + it('should fire off the move animation', inject(function($compile, $rootScope, $animate) { @@ -1464,25 +1522,4 @@ describe('ngRepeat animations', function() { expect(item.element.text()).toBe('3'); }) ); - - it('should work with svg elements when the svg container is transcluded', function() { - module(function($compileProvider) { - $compileProvider.directive('svgContainer', function() { - return { - template: '', - replace: true, - transclude: true - }; - }); - }); - inject(function($compile, $rootScope) { - element = $compile('')($rootScope); - $rootScope.rows = [1]; - $rootScope.$apply(); - - var circle = element.find('circle'); - expect(circle[0].toString()).toMatch(/SVG/); - }); - }); - }); diff --git a/test/ng/parseSpec.js b/test/ng/parseSpec.js index 265f12c67b54..618149cee2fe 100644 --- a/test/ng/parseSpec.js +++ b/test/ng/parseSpec.js @@ -722,9 +722,46 @@ describe('parser', function() { scope.$eval('a.toString.constructor = 1', scope); }).toThrowMinErr( '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + - 'Expression: a.toString.constructor = 1'); + 'Expression: a.toString.constructor'); }); + it('should disallow traversing the Function object in a setter: E02', function() { + expect(function() { + // This expression by itself isn't dangerous. However, one can use this to + // automatically call an object (e.g. a Function object) when it is automatically + // toString'd/valueOf'd by setting the RHS to Function.prototype.call. + scope.$eval('hasOwnProperty.constructor.prototype.valueOf = 1'); + }).toThrowMinErr( + '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + + 'Expression: hasOwnProperty.constructor.prototype.valueOf'); + }); + + it('should disallow passing the Function object as a parameter: E03', function() { + expect(function() { + // This expression constructs a function but does not execute it. It does lead the + // way to execute it if one can get the toString/valueOf of it to call the function. + scope.$eval('["a", "alert(1)"].sort(hasOwnProperty.constructor)'); + }).toThrow(); + }); + + it('should prevent exploit E01', function() { + // This is a tracking exploit. The two individual tests, it('should … : E02') and + // it('should … : E03') test for two parts to block this exploit. This exploit works + // as follows: + // + // • Array.sort takes a comparison function and passes it 2 parameters to compare. If + // the result is non-primitive, sort then invokes valueOf() on the result. + // • The Function object conveniently accepts two string arguments so we can use this + // to construct a function. However, this doesn't do much unless we can execute it. + // • We set the valueOf property on Function.prototype to Function.prototype.call. + // This causes the function that we constructed to be executed when sort calls + // .valueOf() on the result of the comparison. + expect(function() { + scope.$eval('' + + 'hasOwnProperty.constructor.prototype.valueOf=valueOf.call;' + + '["a","alert(1)"].sort(hasOwnProperty.constructor)'); + }).toThrow(); + }); it('should NOT allow access to Function constructor that has been aliased', function() { scope.foo = { "bar": Function }; diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index e6e71845eb97..a571464e2142 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -156,7 +156,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($compile, $rootScope, $animate, $sniffer, $rootElement, $timeout) { + inject(function($compile, $rootScope, $animate, $sniffer, $rootElement) { $animate.enabled(true); var elm1 = $compile('
')($rootScope); @@ -214,7 +214,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($compile, $rootScope, $animate, $sniffer, $rootElement, $timeout) { + inject(function($compile, $rootScope, $animate) { $animate.enabled(true); var elm1 = $compile('
')($rootScope); @@ -241,8 +241,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($animate, $rootElement, $rootScope, $compile, $timeout) { - var initialState; + inject(function($animate, $rootElement, $rootScope, $compile) { angular.bootstrap(rootElm, ['ngAnimate']); $animate.enabled(true); @@ -341,7 +340,7 @@ describe("ngAnimate", function() { it("should animate the enter animation event", - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope, $sniffer) { element[0].removeChild(child[0]); expect(element.contents().length).toBe(0); @@ -359,7 +358,7 @@ describe("ngAnimate", function() { })); it("should animate the enter animation event with native dom elements", - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope, $sniffer) { element[0].removeChild(child[0]); expect(element.contents().length).toBe(0); @@ -378,7 +377,7 @@ describe("ngAnimate", function() { it("should animate the leave animation event", - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope, $sniffer) { expect(element.contents().length).toBe(1); $animate.leave(child); @@ -395,7 +394,7 @@ describe("ngAnimate", function() { })); it("should animate the leave animation event with native dom elements", - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope, $sniffer) { expect(element.contents().length).toBe(1); $animate.leave(child[0]); @@ -450,7 +449,7 @@ describe("ngAnimate", function() { })); it("should animate the show animation event", - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope, $sniffer) { $rootScope.$digest(); child.addClass('ng-hide'); @@ -469,7 +468,7 @@ describe("ngAnimate", function() { })); it("should animate the hide animation event", - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope, $sniffer) { $rootScope.$digest(); expect(child).toBeShown(); @@ -510,7 +509,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope) { child.attr('class','classify no'); $animate.setClass(child, 'yes', 'no'); $rootScope.$digest(); @@ -549,7 +548,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope) { child.attr('class','classify no'); $animate.setClass(child[0], 'yes', 'no'); $rootScope.$digest(); @@ -591,7 +590,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope) { child.attr('class','classify no'); $animate.setClass(child, 'yes', 'no'); $rootScope.$digest(); @@ -604,7 +603,7 @@ describe("ngAnimate", function() { }); it("should assign the ng-event className to all animation events when transitions/keyframes are used", - inject(function($animate, $sniffer, $rootScope, $timeout) { + inject(function($animate, $sniffer, $rootScope) { if (!$sniffer.transitions) return; @@ -682,7 +681,7 @@ describe("ngAnimate", function() { } }); }); - inject(function($animate, $sniffer, $rootScope, $timeout) { + inject(function($animate, $sniffer, $rootScope) { var promise; $animate.enabled(true); @@ -818,7 +817,7 @@ describe("ngAnimate", function() { it("should retain existing styles of the animated element", - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope, $sniffer) { element.append(child); child.attr('style', 'width: 20px'); @@ -880,7 +879,7 @@ describe("ngAnimate", function() { }); }); - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope) { $animate.addClass(element, 'hide'); $rootScope.$digest(); @@ -904,7 +903,7 @@ describe("ngAnimate", function() { }); it("should skip a class-based animation if the same element already has an ongoing structural animation", - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope, $sniffer) { var completed = false; $animate.enter(child, element, null).then(function() { @@ -940,7 +939,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($animate, $rootScope, $sniffer, $timeout) { + inject(function($animate, $rootScope) { $animate.enabled(true); $animate.enabled(false, element); @@ -1080,7 +1079,7 @@ describe("ngAnimate", function() { describe("Animations", function() { it("should properly detect and make use of CSS Animations", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer) { ss.addRule('.ng-hide-add', '-webkit-animation: some_animation 4s linear 0s 1 alternate;' + @@ -1105,7 +1104,7 @@ describe("ngAnimate", function() { it("should properly detect and make use of CSS Animations with multiple iterations", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer) { var style = '-webkit-animation-duration: 2s;' + '-webkit-animation-iteration-count: 3;' + @@ -1131,7 +1130,7 @@ describe("ngAnimate", function() { it("should not consider the animation delay is provided", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer) { var style = '-webkit-animation-duration: 2s;' + '-webkit-animation-delay: 10s;' + @@ -1159,7 +1158,7 @@ describe("ngAnimate", function() { it("should skip animations if disabled and run when enabled", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function($animate, $rootScope, $compile) { $animate.enabled(false); var style = '-webkit-animation: some_animation 2s linear 0s 1 alternate;' + 'animation: some_animation 2s linear 0s 1 alternate;'; @@ -1177,7 +1176,7 @@ describe("ngAnimate", function() { it("should finish the previous animation when a new animation is started", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer) { var style = '-webkit-animation: some_animation 2s linear 0s 1 alternate;' + 'animation: some_animation 2s linear 0s 1 alternate;'; @@ -1215,7 +1214,7 @@ describe("ngAnimate", function() { it("should pause the playstate when performing a stagger animation", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) { + inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { if(!$sniffer.animations) return; @@ -1279,7 +1278,7 @@ describe("ngAnimate", function() { it("should block and unblock keyframe animations when a stagger animation kicks in while skipping the first element", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) { + inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { if(!$sniffer.animations) return; @@ -1373,7 +1372,7 @@ describe("ngAnimate", function() { describe("Transitions", function() { it("should skip transitions if disabled and run when enabled", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer) { var style = '-webkit-transition: 1s linear all;' + 'transition: 1s linear all;'; @@ -1406,7 +1405,7 @@ describe("ngAnimate", function() { it("should skip animations if disabled and run when enabled picking the longest specified duration", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer) { var style = '-webkit-transition-duration: 1s, 2000ms, 1s;' + '-webkit-transition-property: height, left, opacity;' + @@ -1435,7 +1434,7 @@ describe("ngAnimate", function() { it("should skip animations if disabled and run when enabled picking the longest specified duration/delay combination", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer) { $animate.enabled(false); var style = '-webkit-transition-duration: 1s, 0s, 1s; ' + '-webkit-transition-delay: 2s, 1000ms, 2s; ' + @@ -1474,7 +1473,7 @@ describe("ngAnimate", function() { it("should NOT overwrite styles with outdated values when animation completes", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer) { if(!$sniffer.transitions) return; @@ -1504,7 +1503,7 @@ describe("ngAnimate", function() { })); it("should NOT overwrite styles when a transition with a specific property is used", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer) { if(!$sniffer.transitions) return; @@ -1525,7 +1524,7 @@ describe("ngAnimate", function() { it("should animate for the highest duration", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function ($animate, $rootScope, $compile, $sniffer) { var style = '-webkit-transition:1s linear all 2s;' + 'transition:1s linear all 2s;' + '-webkit-animation:my_ani 10s 1s;' + @@ -1555,7 +1554,7 @@ describe("ngAnimate", function() { it("should finish the previous transition when a new animation is started", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer) { var style = '-webkit-transition: 1s linear all;' + 'transition: 1s linear all;'; @@ -1590,7 +1589,7 @@ describe("ngAnimate", function() { ); it("should place a hard block when a structural CSS transition is run", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) { + inject(function($animate, $rootScope, $compile, $sniffer) { if(!$sniffer.transitions) return; @@ -1614,7 +1613,7 @@ describe("ngAnimate", function() { })); it("should not place a hard block when a class-based CSS transition is run", - inject(function($animate, $rootScope, $compile, $sniffer, $timeout, $document, $rootElement) { + inject(function($animate, $rootScope, $compile, $sniffer) { if(!$sniffer.transitions) return; @@ -1801,7 +1800,6 @@ describe("ngAnimate", function() { element[0].className = 'animate-me'; $rootScope.items = [1,2,3,4,5,6,7,8,9,10]; - var totalOperations = $rootScope.items.length; $rootScope.$digest(); @@ -2087,7 +2085,7 @@ describe("ngAnimate", function() { it('should only append active to the newly append CSS className values', - inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $sniffer, $rootElement) { ss.addRule('.ng-enter', '-webkit-transition:9s linear all;' + 'transition:9s linear all;'); @@ -2142,7 +2140,7 @@ describe("ngAnimate", function() { it("should fire the enter callback", - inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer, $rootElement) { var parent = jqLite('
'); var element = parent.find('span'); @@ -2162,7 +2160,7 @@ describe("ngAnimate", function() { it("should fire the leave callback", - inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer, $rootElement) { var parent = jqLite('
'); var element = parent.find('span'); @@ -2182,7 +2180,7 @@ describe("ngAnimate", function() { it("should fire the move callback", - inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer, $rootElement) { var parent = jqLite('
'); var parent2 = jqLite('
'); @@ -2206,7 +2204,7 @@ describe("ngAnimate", function() { it("should fire the addClass/removeClass callbacks", - inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer, $rootElement) { var parent = jqLite('
'); var element = parent.find('span'); @@ -2232,7 +2230,7 @@ describe("ngAnimate", function() { })); it("should fire the setClass callback", - inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer, $rootElement) { var parent = jqLite('
'); var element = parent.find('span'); @@ -2257,7 +2255,7 @@ describe("ngAnimate", function() { })); it('should fire DOM callbacks on the element being animated', - inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer, $rootElement) { if(!$sniffer.transitions) return; @@ -2310,7 +2308,7 @@ describe("ngAnimate", function() { })); it('should fire the DOM callbacks even if no animation is rendered', - inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer, $rootElement) { $animate.enabled(true); @@ -2353,7 +2351,7 @@ describe("ngAnimate", function() { })); it("should fire a done callback when provided with no animation", - inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer, $rootElement) { var parent = jqLite('
'); var element = parent.find('span'); @@ -2372,7 +2370,7 @@ describe("ngAnimate", function() { it("should fire a done callback when provided with a css animation/transition", - inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer, $rootElement) { ss.addRule('.ng-hide-add', '-webkit-transition:1s linear all;' + 'transition:1s linear all;'); @@ -2400,7 +2398,7 @@ describe("ngAnimate", function() { it("should fire a done callback when provided with a JS animation", - inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer, $rootElement) { var parent = jqLite('
'); $rootElement.append(parent); @@ -2420,7 +2418,7 @@ describe("ngAnimate", function() { it("should fire the callback right away if another animation is called right after", - inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $sniffer, $rootElement) { ss.addRule('.ng-hide-add', '-webkit-transition:9s linear all;' + 'transition:9s linear all;'); @@ -2457,7 +2455,7 @@ describe("ngAnimate", function() { var captured; beforeEach(function() { - module(function($animateProvider, $provide) { + module(function($animateProvider) { $animateProvider.register('.klassy', function($timeout) { return { addClass : function(element, className, done) { @@ -2476,7 +2474,7 @@ describe("ngAnimate", function() { it("should not perform an animation, and the followup DOM operation, if the class is " + "already present during addClass or not present during removeClass on the element", - inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout, $browser) { + inject(function($animate, $rootScope, $sniffer, $rootElement) { var element = jqLite('
'); $rootElement.append(element); @@ -2514,7 +2512,7 @@ describe("ngAnimate", function() { })); it("should perform the animation if passed native dom element", - inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout, $browser) { + inject(function($animate, $rootScope, $sniffer, $rootElement) { var element = jqLite('
'); $rootElement.append(element); @@ -2576,7 +2574,7 @@ describe("ngAnimate", function() { it("should add and remove CSS classes with a callback", - inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $sniffer, $rootElement) { var parent = jqLite('
'); $rootElement.append(parent); @@ -2606,7 +2604,7 @@ describe("ngAnimate", function() { it("should end the current addClass animation, add the CSS class and then run the removeClass animation", - inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $sniffer, $rootElement) { ss.addRule('.klass-add', '-webkit-transition:3s linear all;' + 'transition:3s linear all;'); @@ -2728,7 +2726,7 @@ describe("ngAnimate", function() { })); it("should properly execute CSS animations/transitions and use callbacks when using addClass / removeClass", - inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $sniffer, $rootElement) { ss.addRule('.klass-add', '-webkit-transition:11s linear all;' + 'transition:11s linear all;'); @@ -2781,7 +2779,7 @@ describe("ngAnimate", function() { it("should allow for multiple css classes to be animated plus a callback when added", - inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $sniffer, $rootElement) { ss.addRule('.one-add', '-webkit-transition:7s linear all;' + 'transition:7s linear all;'); @@ -2825,7 +2823,7 @@ describe("ngAnimate", function() { it("should allow for multiple css classes to be animated plus a callback when removed", - inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) { + inject(function($animate, $rootScope, $sniffer, $rootElement) { ss.addRule('.one-remove', '-webkit-transition:9s linear all;' + 'transition:9s linear all;'); @@ -2890,7 +2888,7 @@ describe("ngAnimate", function() { it("should properly animate and parse CSS3 transitions", - inject(function($compile, $rootScope, $animate, $sniffer, $timeout) { + inject(function($compile, $rootScope, $animate, $sniffer) { ss.addRule('.ng-enter', '-webkit-transition:1s linear all;' + 'transition:1s linear all;'); @@ -2914,7 +2912,7 @@ describe("ngAnimate", function() { it("should properly animate and parse CSS3 animations", - inject(function($compile, $rootScope, $animate, $sniffer, $timeout) { + inject(function($compile, $rootScope, $animate, $sniffer) { ss.addRule('.ng-enter', '-webkit-animation: some_animation 4s linear 1s 2 alternate;' + 'animation: some_animation 4s linear 1s 2 alternate;'); @@ -2966,7 +2964,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($compile, $rootScope, $animate, $sniffer, $timeout) { + inject(function($compile, $rootScope, $animate, $sniffer) { ss.addRule('.ng-enter', '-webkit-transition: 1s linear all;' + 'transition: 1s linear all;'); @@ -3038,7 +3036,7 @@ describe("ngAnimate", function() { it("should not perform the active class animation if the animation has been cancelled before the reflow occurs", function() { - inject(function($compile, $rootScope, $animate, $sniffer, $timeout) { + inject(function($compile, $rootScope, $animate, $sniffer) { if(!$sniffer.transitions) return; ss.addRule('.animated.ng-enter', '-webkit-transition: 2s linear all;' + @@ -3179,7 +3177,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($compile, $rootScope, $animate, $timeout) { + inject(function($compile, $rootScope, $animate) { var parent = html($compile('
')($rootScope)); var one = $compile('
')($rootScope); var two = $compile('
')($rootScope); @@ -3305,7 +3303,7 @@ describe("ngAnimate", function() { var step, animationState; module(function($animateProvider) { - $animateProvider.register('.animan', function($timeout) { + $animateProvider.register('.animan', function() { return { enter : function(element, done) { animationState = 'enter'; @@ -3458,7 +3456,7 @@ describe("ngAnimate", function() { $animateProvider.register('.animate', function() { return { addClass : spy, - removeClass : spy, + removeClass : spy }; }); }); @@ -3761,7 +3759,7 @@ describe("ngAnimate", function() { it("should cancel and perform the dom operation only after the reflow has run", - inject(function($compile, $rootScope, $animate, $sniffer, $timeout) { + inject(function($compile, $rootScope, $animate, $sniffer) { if (!$sniffer.transitions) return; @@ -3804,9 +3802,11 @@ describe("ngAnimate", function() { function assertClasses(str) { var className = element.attr('class'); - str.length === 0 - ? className.length === 0 - : expect(className.split(/\s+/)).toEqual(str.split(' ')); + if (str.length === 0) { + expect(className.length).toBe(0); + } else { + expect(className.split(/\s+/)).toEqual(str.split(' ')); + } } $rootScope.className = ''; @@ -3856,7 +3856,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($compile, $rootScope, $animate, $sniffer, $timeout) { + inject(function($compile, $rootScope, $animate) { var element = $compile('
')($rootScope); $rootElement.append(element); jqLite($document[0].body).append($rootElement); @@ -3890,7 +3890,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($compile, $rootScope, $animate, $sniffer, $rootElement, $timeout) { + inject(function($compile, $rootScope, $animate, $sniffer, $rootElement) { $rootElement.addClass('animated'); $animate.addClass($rootElement, 'green'); @@ -3924,7 +3924,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($animate, $rootScope, $compile, $rootElement, $timeout) { + inject(function($animate, $rootScope, $compile, $rootElement) { $animate.enabled(true); var element = $compile('
')($rootScope); @@ -3975,7 +3975,7 @@ describe("ngAnimate", function() { it('should only perform the DOM operation once', - inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) { + inject(function($sniffer, $compile, $rootScope, $rootElement, $animate) { if (!$sniffer.transitions) return; @@ -4010,7 +4010,7 @@ describe("ngAnimate", function() { it('should block and unblock transitions before the dom operation occurs', - inject(function($rootScope, $compile, $rootElement, $document, $animate, $sniffer, $timeout) { + inject(function($rootScope, $compile, $rootElement, $document, $animate, $sniffer) { if (!$sniffer.transitions) return; @@ -4046,7 +4046,7 @@ describe("ngAnimate", function() { it('should not block keyframe animations around the reflow operation', - inject(function($rootScope, $compile, $rootElement, $document, $animate, $sniffer, $timeout) { + inject(function($rootScope, $compile, $rootElement, $document, $animate, $sniffer) { if (!$sniffer.animations) return; @@ -4103,8 +4103,6 @@ describe("ngAnimate", function() { ss.addRule('.special', '-webkit-animation:1s special_animation;' + 'animation:1s special_animation;'); - var capturedProperty = 'none'; - var element = $compile('
')($rootScope); $rootElement.append(element); jqLite($document[0].body).append($rootElement); @@ -4123,7 +4121,7 @@ describe("ngAnimate", function() { it('should round up long elapsedTime values to close off a CSS3 animation', - inject(function($rootScope, $compile, $rootElement, $document, $animate, $sniffer, $timeout, $window) { + inject(function($rootScope, $compile, $rootElement, $document, $animate, $sniffer) { if (!$sniffer.animations) return; ss.addRule('.millisecond-transition.ng-leave', '-webkit-transition:510ms linear all;' + @@ -4293,7 +4291,7 @@ describe("ngAnimate", function() { }); it('should respect the most relevant CSS transition property if defined in multiple classes', - inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) { + inject(function($sniffer, $compile, $rootScope, $rootElement, $animate) { if (!$sniffer.transitions) return; @@ -4337,7 +4335,7 @@ describe("ngAnimate", function() { })); it('should not apply a transition upon removal of a class that has a transition', - inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) { + inject(function($sniffer, $compile, $rootScope, $rootElement, $animate) { if (!$sniffer.transitions) return; @@ -4362,7 +4360,7 @@ describe("ngAnimate", function() { })); it('should immediately close the former animation if the same CSS class is added/removed', - inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) { + inject(function($sniffer, $compile, $rootScope, $rootElement, $animate) { if (!$sniffer.transitions) return; @@ -4399,7 +4397,7 @@ describe("ngAnimate", function() { it('should cancel the previous reflow when new animations are added', function() { var cancelReflowCallback = jasmine.createSpy('callback'); module(function($provide) { - $provide.value('$$animateReflow', function(fn) { + $provide.value('$$animateReflow', function() { return cancelReflowCallback; }); }); @@ -4443,7 +4441,7 @@ describe("ngAnimate", function() { }; }); }); - inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) { + inject(function($sniffer, $compile, $rootScope, $rootElement, $animate) { $animate.enabled(true);