@@ -81,9 +81,65 @@ var $AnimateProvider = ['$provide', function($provide) {
81
81
return this . $$classNameFilter ;
82
82
} ;
83
83
84
- this . $get = [ '$$q' , '$$asyncCallback' , function ( $$q , $$asyncCallback ) {
84
+ this . $get = [ '$$q' , '$$asyncCallback' , '$rootScope' , function ( $$q , $$asyncCallback , $rootScope ) {
85
85
86
86
var currentDefer ;
87
+ var ELEMENT_NODE = 1 ;
88
+
89
+ function extractElementNodes ( element ) {
90
+ var elements = new Array ( element . length ) ;
91
+ var count = 0 ;
92
+ for ( var i = 0 ; i < element . length ; i ++ ) {
93
+ var elm = element [ i ] ;
94
+ if ( elm . nodeType == ELEMENT_NODE ) {
95
+ elements [ count ++ ] = elm ;
96
+ }
97
+ }
98
+ elements . length = count ;
99
+ return jqLite ( elements ) ;
100
+ }
101
+
102
+ function runAnimationPostDigest ( fn ) {
103
+ var cancelFn , defer = $$q . defer ( ) ;
104
+ defer . promise . $$cancelFn = function ngAnimateMaybeCancel ( ) {
105
+ cancelFn && cancelFn ( ) ;
106
+ } ;
107
+
108
+ $rootScope . $$postDigest ( function ngAnimatePostDigest ( ) {
109
+ cancelFn = fn ( function ngAnimateNotifyComplete ( ) {
110
+ defer . resolve ( ) ;
111
+ } ) ;
112
+ } ) ;
113
+
114
+ return defer . promise ;
115
+ }
116
+
117
+ function resolveElementClasses ( element , cache ) {
118
+ var toAdd = [ ] , toRemove = [ ] ;
119
+ forEach ( cache . classes , function ( status , className ) {
120
+ var hasClass = jqLiteHasClass ( element [ 0 ] , className ) ;
121
+
122
+ // If the most recent class manipulation (via $animate) was to remove the class, and the
123
+ // element currently has the class, the class is scheduled for removal. Otherwise, if
124
+ // the most recent class manipulation (via $animate) was to add the class, and the
125
+ // element does not currently have the class, the class is scheduled to be added.
126
+ if ( status === false && hasClass ) {
127
+ toRemove . push ( className ) ;
128
+ } else if ( status === true && ! hasClass ) {
129
+ toAdd . push ( className ) ;
130
+ }
131
+ } ) ;
132
+
133
+ return ( toAdd . length + toRemove . length ) > 0 && [ toAdd . join ( ' ' ) , toRemove . join ( ' ' ) ] ;
134
+ }
135
+
136
+ function cachedClassManipulation ( cache , classes , op ) {
137
+ for ( var i = 0 , ii = classes . length ; i < ii ; ++ i ) {
138
+ var className = classes [ i ] ;
139
+ cache [ className ] = op ;
140
+ }
141
+ }
142
+
87
143
function asyncPromise ( ) {
88
144
// only serve one instance of a promise in order to save CPU cycles
89
145
if ( ! currentDefer ) {
@@ -187,13 +243,17 @@ var $AnimateProvider = ['$provide', function($provide) {
187
243
* @return {Promise } the animation callback promise
188
244
*/
189
245
addClass : function ( element , className ) {
246
+ return this . setClass ( element , className , [ ] ) ;
247
+ } ,
248
+
249
+ $$addClassImmediately : function addClassImmediately ( element , className ) {
250
+ element = jqLite ( element ) ;
190
251
className = ! isString ( className )
191
252
? ( isArray ( className ) ? className . join ( ' ' ) : '' )
192
253
: className ;
193
254
forEach ( element , function ( element ) {
194
255
jqLiteAddClass ( element , className ) ;
195
256
} ) ;
196
- return asyncPromise ( ) ;
197
257
} ,
198
258
199
259
/**
@@ -209,6 +269,11 @@ var $AnimateProvider = ['$provide', function($provide) {
209
269
* @return {Promise } the animation callback promise
210
270
*/
211
271
removeClass : function ( element , className ) {
272
+ return this . setClass ( element , [ ] , className ) ;
273
+ } ,
274
+
275
+ $$removeClassImmediately : function removeClassImmediately ( element , className ) {
276
+ element = jqLite ( element ) ;
212
277
className = ! isString ( className )
213
278
? ( isArray ( className ) ? className . join ( ' ' ) : '' )
214
279
: className ;
@@ -231,10 +296,50 @@ var $AnimateProvider = ['$provide', function($provide) {
231
296
* @param {string } remove the CSS class which will be removed from the element
232
297
* @return {Promise } the animation callback promise
233
298
*/
234
- setClass : function ( element , add , remove ) {
235
- this . addClass ( element , add ) ;
236
- this . removeClass ( element , remove ) ;
237
- return asyncPromise ( ) ;
299
+ setClass : function ( element , add , remove , runSynchronously ) {
300
+ var self = this ;
301
+ var STORAGE_KEY = '$$animateClasses' ;
302
+ element = extractElementNodes ( jqLite ( element ) ) ;
303
+
304
+ if ( runSynchronously ) {
305
+ self . $$addClassImmediately ( element , add ) ;
306
+ self . $$removeClassImmediately ( element , remove ) ;
307
+ return asyncPromise ( ) ;
308
+ }
309
+
310
+ var cache = element . data ( STORAGE_KEY ) ;
311
+ if ( ! cache ) {
312
+ cache = {
313
+ classes : { }
314
+ } ;
315
+ var createdCache = true ;
316
+ }
317
+
318
+ var classes = cache . classes ;
319
+
320
+ add = isArray ( add ) ? add : add . split ( ' ' ) ;
321
+ remove = isArray ( remove ) ? remove : remove . split ( ' ' ) ;
322
+ cachedClassManipulation ( classes , add , true ) ;
323
+ cachedClassManipulation ( classes , remove , false ) ;
324
+
325
+ if ( createdCache ) {
326
+ cache . promise = runAnimationPostDigest ( function ( done ) {
327
+ var cache = element . data ( STORAGE_KEY ) ;
328
+ element . removeData ( STORAGE_KEY ) ;
329
+
330
+ var classes = cache && resolveElementClasses ( element , cache ) ;
331
+
332
+ if ( classes ) {
333
+ if ( classes [ 0 ] ) self . $$addClassImmediately ( element , classes [ 0 ] ) ;
334
+ if ( classes [ 1 ] ) self . $$removeClassImmediately ( element , classes [ 1 ] ) ;
335
+ }
336
+
337
+ done ( ) ;
338
+ } ) ;
339
+ element . data ( STORAGE_KEY , cache ) ;
340
+ }
341
+
342
+ return cache . promise ;
238
343
} ,
239
344
240
345
enabled : noop ,
0 commit comments