Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 0d6fc2d

Browse files
matskopetebacondarwin
authored andcommitted
fix(ngAnimate): ensure that only string-based addClass/removeClass values are applied
Related #11268 Closes #12458 Closes #12459
1 parent addb1ae commit 0d6fc2d

File tree

4 files changed

+106
-26
lines changed

4 files changed

+106
-26
lines changed

src/ng/animate.js

+18-18
Original file line numberDiff line numberDiff line change
@@ -106,31 +106,31 @@ var $$CoreAnimateQueueProvider = function() {
106106
};
107107

108108
function addRemoveClassesPostDigest(element, add, remove) {
109-
var data = postDigestQueue.get(element);
110-
var classVal;
109+
var classVal, data = postDigestQueue.get(element);
111110

112111
if (!data) {
113112
postDigestQueue.put(element, data = {});
114113
postDigestElements.push(element);
115114
}
116115

117-
if (add) {
118-
forEach(add.split(' '), function(className) {
119-
if (className) {
120-
data[className] = true;
121-
}
122-
});
123-
}
124-
125-
if (remove) {
126-
forEach(remove.split(' '), function(className) {
127-
if (className) {
128-
data[className] = false;
129-
}
130-
});
131-
}
116+
var updateData = function(classes, value) {
117+
var changed = false;
118+
if (classes) {
119+
classes = isString(classes) ? classes.split(' ') :
120+
isArray(classes) ? classes : [];
121+
forEach(classes, function(className) {
122+
if (className) {
123+
changed = true;
124+
data[className] = value;
125+
}
126+
});
127+
}
128+
return changed;
129+
};
132130

133-
if (postDigestElements.length > 1) return;
131+
var classesAdded = updateData(add, true);
132+
var classesRemoved = updateData(remove, false);
133+
if ((!classesAdded && !classesRemoved) || postDigestElements.length > 1) return;
134134

135135
$rootScope.$$postDigest(function() {
136136
forEach(postDigestElements, function(element) {

src/ngAnimate/animateQueue.js

+16-8
Original file line numberDiff line numberDiff line change
@@ -239,22 +239,22 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
239239
// These methods will become available after the digest has passed
240240
var runner = new $$AnimateRunner();
241241

242-
// there are situations where a directive issues an animation for
243-
// a jqLite wrapper that contains only comment nodes... If this
244-
// happens then there is no way we can perform an animation
245-
if (!node) {
246-
close();
247-
return runner;
248-
}
249-
250242
if (isArray(options.addClass)) {
251243
options.addClass = options.addClass.join(' ');
252244
}
253245

246+
if (options.addClass && !isString(options.addClass)) {
247+
options.addClass = null;
248+
}
249+
254250
if (isArray(options.removeClass)) {
255251
options.removeClass = options.removeClass.join(' ');
256252
}
257253

254+
if (options.removeClass && !isString(options.removeClass)) {
255+
options.removeClass = null;
256+
}
257+
258258
if (options.from && !isObject(options.from)) {
259259
options.from = null;
260260
}
@@ -263,6 +263,14 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
263263
options.to = null;
264264
}
265265

266+
// there are situations where a directive issues an animation for
267+
// a jqLite wrapper that contains only comment nodes... If this
268+
// happens then there is no way we can perform an animation
269+
if (!node) {
270+
close();
271+
return runner;
272+
}
273+
266274
var className = [node.className, options.addClass, options.removeClass].join(' ');
267275
if (!isAnimatableClassName(className)) {
268276
close();

test/ng/animateSpec.js

+43
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,49 @@ describe("$animate", function() {
320320
});
321321
});
322322

323+
it('should not issue a call to addClass if the provided class value is not a string or array', function() {
324+
inject(function($animate, $rootScope, $rootElement) {
325+
var spy = spyOn(window, 'jqLiteAddClass').andCallThrough();
326+
327+
var element = jqLite('<div></div>');
328+
var parent = $rootElement;
329+
330+
$animate.enter(element, parent, null, { addClass: noop });
331+
$rootScope.$digest();
332+
expect(spy).not.toHaveBeenCalled();
333+
334+
$animate.leave(element, { addClass: true });
335+
$rootScope.$digest();
336+
expect(spy).not.toHaveBeenCalled();
337+
338+
$animate.enter(element, parent, null, { addClass: 'fatias' });
339+
$rootScope.$digest();
340+
expect(spy).toHaveBeenCalled();
341+
});
342+
});
343+
344+
it('should not issue a call to removeClass if the provided class value is not a string or array', function() {
345+
inject(function($animate, $rootScope, $rootElement) {
346+
var spy = spyOn(window, 'jqLiteRemoveClass').andCallThrough();
347+
348+
var element = jqLite('<div></div>');
349+
var parent = $rootElement;
350+
351+
$animate.enter(element, parent, null, {removeClass: noop});
352+
$rootScope.$digest();
353+
expect(spy).not.toHaveBeenCalled();
354+
355+
$animate.leave(element, {removeClass: true});
356+
$rootScope.$digest();
357+
expect(spy).not.toHaveBeenCalled();
358+
359+
element.addClass('fatias');
360+
$animate.enter(element, parent, null, { removeClass: 'fatias' });
361+
$rootScope.$digest();
362+
expect(spy).toHaveBeenCalled();
363+
});
364+
});
365+
323366
describe('CSS class DOM manipulation', function() {
324367
var element;
325368
var addClass;

test/ngAnimate/animateSpec.js

+29
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,35 @@ describe("animations", function() {
148148
});
149149
});
150150

151+
they('should nullify both options.$prop when passed into an animation if it is not a string or an array', ['addClass', 'removeClass'], function(prop) {
152+
inject(function($animate, $rootScope) {
153+
var options1 = {};
154+
options1[prop] = function() {};
155+
$animate.enter(element, parent, null, options1);
156+
157+
expect(options1[prop]).toBeFalsy();
158+
$rootScope.$digest();
159+
160+
var options2 = {};
161+
options2[prop] = true;
162+
$animate.leave(element, options2);
163+
164+
expect(options2[prop]).toBeFalsy();
165+
$rootScope.$digest();
166+
167+
capturedAnimation = null;
168+
169+
var options3 = {};
170+
if (prop === 'removeClass') {
171+
element.addClass('fatias');
172+
}
173+
174+
options3[prop] = ['fatias'];
175+
$animate.enter(element, parent, null, options3);
176+
expect(options3[prop]).toBe('fatias');
177+
});
178+
});
179+
151180
it('should throw a minErr if a regex value is used which partially contains or fully matches the `ng-animate` CSS class', function() {
152181
module(function($animateProvider) {
153182
assertError(/ng-animate/, true);

0 commit comments

Comments
 (0)