8
8
9
9
function classDirective ( name , selector ) {
10
10
name = 'ngClass' + name ;
11
- return [ '$animate' , function ( $animate ) {
11
+ return [ '$animate' , '$parse' , function ( $animate , $parse ) {
12
12
return {
13
13
restrict : 'AC' ,
14
- link : function ( scope , element , attr ) {
15
- var oldVal ;
16
-
17
- scope . $watch ( attr [ name ] , ngClassWatchAction , true ) ;
18
-
19
- attr . $observe ( 'class' , function ( value ) {
20
- ngClassWatchAction ( scope . $eval ( attr [ name ] ) ) ;
14
+ compile : function ( tElement , tAttr ) {
15
+ var classFn = $parse ( tAttr [ name ] , function ( value ) {
16
+ return arrayClassesL1 ( value ) ;
21
17
} ) ;
18
+ return function ( scope , element , attr ) {
19
+ var oldClasses ;
22
20
23
21
24
- if ( name !== 'ngClass' ) {
25
- scope . $watch ( '$index' , function ( $index , old$index ) {
26
- /* eslint-disable no-bitwise */
27
- var mod = $index & 1 ;
28
- if ( mod !== ( old$index & 1 ) ) {
29
- var classes = arrayClasses ( scope . $eval ( attr [ name ] ) ) ;
30
- if ( mod === selector ) {
31
- addClasses ( classes ) ;
32
- } else {
33
- removeClasses ( classes ) ;
34
- }
35
- }
36
- /* eslint-enable */
37
- } ) ;
38
- }
22
+ scope . $watch ( classFn , ngClassWatchAction , classArrayEquals ) ;
39
23
40
- function addClasses ( classes ) {
41
- var newClasses = digestClassCounts ( classes , 1 ) ;
42
- attr . $addClass ( newClasses ) ;
43
- }
24
+ attr . $observe ( 'class' , function ( value ) {
25
+ ngClassWatchAction ( classFn ( scope ) ) ;
26
+ } ) ;
44
27
45
- function removeClasses ( classes ) {
46
- var newClasses = digestClassCounts ( classes , - 1 ) ;
47
- attr . $removeClass ( newClasses ) ;
48
- }
49
28
50
- function digestClassCounts ( classes , count ) {
51
- // Use createMap() to prevent class assumptions involving property
52
- // names in Object.prototype
53
- var classCounts = element . data ( '$classCounts' ) || createMap ( ) ;
54
- var classesToUpdate = [ ] ;
55
- forEach ( classes , function ( className ) {
56
- if ( count > 0 || classCounts [ className ] ) {
57
- classCounts [ className ] = ( classCounts [ className ] || 0 ) + count ;
58
- if ( classCounts [ className ] === + ( count > 0 ) ) {
59
- classesToUpdate . push ( className ) ;
29
+ if ( name !== 'ngClass' ) {
30
+ scope . $watch ( '$index' , function ( $index , old$index ) {
31
+ // eslint-disable-next-line no-bitwise
32
+ var mod = $index & 1 ;
33
+ // eslint-disable-next-line no-bitwise
34
+ if ( mod !== ( old$index & 1 ) ) {
35
+ var classes = classFn ( scope ) . join ( ' ' ) . split ( ' ' ) ;
36
+ if ( mod === selector ) {
37
+ addClasses ( classes ) ;
38
+ } else {
39
+ removeClasses ( classes ) ;
40
+ }
60
41
}
61
- }
62
- } ) ;
63
- element . data ( '$classCounts' , classCounts ) ;
64
- return classesToUpdate . join ( ' ' ) ;
65
- }
42
+ } ) ;
43
+ }
66
44
67
- function updateClasses ( oldClasses , newClasses ) {
68
- var toAdd = arrayDifference ( newClasses , oldClasses ) ;
69
- var toRemove = arrayDifference ( oldClasses , newClasses ) ;
70
- toAdd = digestClassCounts ( toAdd , 1 ) ;
71
- toRemove = digestClassCounts ( toRemove , - 1 ) ;
72
- if ( toAdd && toAdd . length ) {
73
- $animate . addClass ( element , toAdd ) ;
45
+ function addClasses ( classes ) {
46
+ var newClasses = digestClassCounts ( classes , 1 ) ;
47
+ attr . $addClass ( newClasses ) ;
74
48
}
75
- if ( toRemove && toRemove . length ) {
76
- $animate . removeClass ( element , toRemove ) ;
49
+
50
+ function removeClasses ( classes ) {
51
+ var newClasses = digestClassCounts ( classes , - 1 ) ;
52
+ attr . $removeClass ( newClasses ) ;
77
53
}
78
- }
79
54
80
- function ngClassWatchAction ( newVal ) {
81
- // eslint-disable-next-line no-bitwise
82
- if ( selector === true || ( scope . $index & 1 ) === selector ) {
83
- var newClasses = arrayClasses ( newVal || [ ] ) ;
84
- if ( ! oldVal ) {
85
- addClasses ( newClasses ) ;
86
- } else if ( ! equals ( newVal , oldVal ) ) {
87
- var oldClasses = arrayClasses ( oldVal ) ;
88
- updateClasses ( oldClasses , newClasses ) ;
55
+ function digestClassCounts ( classes , count ) {
56
+ // Use createMap() to prevent class assumptions involving property
57
+ // names in Object.prototype
58
+ var classCounts = element . data ( '$classCounts' ) || createMap ( ) ;
59
+ var classesToUpdate = [ ] ;
60
+ forEach ( classes , function ( className ) {
61
+ if ( count > 0 || classCounts [ className ] ) {
62
+ classCounts [ className ] = ( classCounts [ className ] || 0 ) + count ;
63
+ if ( classCounts [ className ] === + ( count > 0 ) ) {
64
+ classesToUpdate . push ( className ) ;
65
+ }
66
+ }
67
+ } ) ;
68
+ element . data ( '$classCounts' , classCounts ) ;
69
+ return classesToUpdate . join ( ' ' ) ;
70
+ }
71
+
72
+ function updateClasses ( oldClasses , newClasses ) {
73
+ var toAdd = arrayDifference ( newClasses , oldClasses ) ;
74
+ var toRemove = arrayDifference ( oldClasses , newClasses ) ;
75
+ toAdd = digestClassCounts ( toAdd , 1 ) ;
76
+ toRemove = digestClassCounts ( toRemove , - 1 ) ;
77
+ if ( toAdd && toAdd . length ) {
78
+ $animate . addClass ( element , toAdd ) ;
79
+ }
80
+ if ( toRemove && toRemove . length ) {
81
+ $animate . removeClass ( element , toRemove ) ;
89
82
}
90
83
}
91
- if ( isArray ( newVal ) ) {
92
- oldVal = newVal . map ( function ( v ) { return shallowCopy ( v ) ; } ) ;
93
- } else {
94
- oldVal = shallowCopy ( newVal ) ;
84
+
85
+ function ngClassWatchAction ( newVal ) {
86
+ // eslint-disable-next-line no-bitwise
87
+ if ( selector === true || ( scope . $index & 1 ) === selector ) {
88
+ var newClasses = ( newVal || [ ] ) . join ( ' ' ) . split ( ' ' ) ;
89
+ if ( ! oldClasses ) {
90
+ addClasses ( newClasses ) ;
91
+ } else if ( ! classArrayEquals ( oldClasses , newClasses ) ) {
92
+ updateClasses ( oldClasses , newClasses ) ;
93
+ }
94
+ oldClasses = shallowCopy ( newClasses ) ;
95
+ }
95
96
}
96
- }
97
+ } ;
97
98
}
98
99
} ;
99
100
@@ -111,25 +112,43 @@ function classDirective(name, selector) {
111
112
return values ;
112
113
}
113
114
114
- function arrayClasses ( classVal ) {
115
- var classes = [ ] ;
115
+ function arrayClassesL1 ( classVal ) {
116
+ var classes ;
116
117
if ( isArray ( classVal ) ) {
117
- forEach ( classVal , function ( v ) {
118
- classes = classes . concat ( arrayClasses ( v ) ) ;
119
- } ) ;
120
- return classes ;
121
- } else if ( isString ( classVal ) ) {
122
- return classVal . split ( ' ' ) ;
118
+ classes = classVal . map ( arrayClassesL2 ) ;
123
119
} else if ( isObject ( classVal ) ) {
124
- forEach ( classVal , function ( v , k ) {
125
- if ( v ) {
126
- classes = classes . concat ( k . split ( ' ' ) ) ;
120
+ classes = [ ] ;
121
+ forEach ( classVal , function ( val , key ) {
122
+ if ( val ) {
123
+ classes . push ( key ) ;
124
+ } else if ( isUndefined ( val ) ) {
125
+ classes . push ( val ) ;
127
126
}
128
127
} ) ;
129
- return classes ;
128
+ } else if ( isString ( classVal ) ) {
129
+ classes = [ classVal ] ;
130
+ } else {
131
+ classes = classVal ;
132
+ }
133
+
134
+ return classes ;
135
+ }
136
+
137
+ function arrayClassesL2 ( values ) {
138
+ if ( isObject ( values ) ) {
139
+ return Object . keys ( values ) . filter ( function ( k ) {
140
+ return values [ k ] ;
141
+ } ) . join ( ' ' ) ;
142
+ } else {
143
+ return values ;
130
144
}
131
- return classVal ;
132
145
}
146
+
147
+ function classArrayEquals ( a1 , a2 ) {
148
+ return a1 === a2 ||
149
+ isArray ( a1 ) && isArray ( a2 ) && a1 . join ( ' ' ) === a2 . join ( ' ' ) ;
150
+ }
151
+
133
152
} ] ;
134
153
}
135
154
0 commit comments