9
9
function classDirective ( name , selector ) {
10
10
name = 'ngClass' + name ;
11
11
12
- return [ function ( ) {
12
+ return [ '$parse' , function ( $parse ) {
13
13
return {
14
14
restrict : 'AC' ,
15
15
link : function ( scope , element , attr ) {
16
+ var expression = attr [ name ] . trim ( ) ;
17
+ var isOneTime = ( expression . charAt ( 0 ) === ':' ) && ( expression . charAt ( 1 ) === ':' ) ;
18
+
19
+ var watchInterceptor = isOneTime ? toFlatValue : toClassString ;
20
+ var watchExpression = $parse ( expression , watchInterceptor ) ;
21
+ var watchAction = isOneTime ? ngClassOneTimeWatchAction : ngClassWatchAction ;
22
+
16
23
var classCounts = element . data ( '$classCounts' ) ;
17
24
var oldModulo = true ;
18
- var oldVal ;
25
+ var oldClassString ;
19
26
20
27
if ( ! classCounts ) {
21
28
// Use createMap() to prevent class assumptions involving property
@@ -30,34 +37,47 @@ function classDirective(name, selector) {
30
37
var newModulo = $index & 1 ;
31
38
32
39
if ( newModulo !== oldModulo ) {
33
- var classes = arrayClasses ( oldVal ) ;
34
40
if ( newModulo === selector ) {
35
- addClasses ( classes ) ;
41
+ addClasses ( oldClassString ) ;
36
42
} else {
37
- removeClasses ( classes ) ;
43
+ removeClasses ( oldClassString ) ;
38
44
}
39
45
40
46
oldModulo = newModulo ;
41
47
}
42
48
} ) ;
43
49
}
44
50
45
- scope . $watch ( attr [ name ] , ngClassWatchAction , true ) ;
51
+ scope . $watch ( watchExpression , watchAction , isOneTime ) ;
52
+
53
+ function addClasses ( classString ) {
54
+ classString = digestClassCounts ( split ( classString ) , 1 ) ;
55
+ attr . $addClass ( classString ) ;
56
+ }
46
57
47
- function addClasses ( classes ) {
48
- var newClasses = digestClassCounts ( classes , 1 ) ;
49
- attr . $addClass ( newClasses ) ;
58
+ function removeClasses ( classString ) {
59
+ classString = digestClassCounts ( split ( classString ) , - 1 ) ;
60
+ attr . $removeClass ( classString ) ;
50
61
}
51
62
52
- function removeClasses ( classes ) {
53
- var newClasses = digestClassCounts ( classes , - 1 ) ;
54
- attr . $removeClass ( newClasses ) ;
63
+ function updateClasses ( oldClassString , newClassString ) {
64
+ var oldClassArray = split ( oldClassString ) ;
65
+ var newClassArray = split ( newClassString ) ;
66
+
67
+ var toRemoveArray = arrayDifference ( oldClassArray , newClassArray ) ;
68
+ var toAddArray = arrayDifference ( newClassArray , oldClassArray ) ;
69
+
70
+ var toRemoveString = digestClassCounts ( toRemoveArray , - 1 ) ;
71
+ var toAddString = digestClassCounts ( toAddArray , 1 ) ;
72
+
73
+ attr . $addClass ( toAddString ) ;
74
+ attr . $removeClass ( toRemoveString ) ;
55
75
}
56
76
57
- function digestClassCounts ( classes , count ) {
77
+ function digestClassCounts ( classArray , count ) {
58
78
var classesToUpdate = [ ] ;
59
79
60
- forEach ( classes , function ( className ) {
80
+ forEach ( classArray , function ( className ) {
61
81
if ( count > 0 || classCounts [ className ] ) {
62
82
classCounts [ className ] = ( classCounts [ className ] || 0 ) + count ;
63
83
if ( classCounts [ className ] === + ( count > 0 ) ) {
@@ -69,31 +89,20 @@ function classDirective(name, selector) {
69
89
return classesToUpdate . join ( ' ' ) ;
70
90
}
71
91
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 ) ;
92
+ function ngClassOneTimeWatchAction ( newClassValue ) {
93
+ var newClassString = toClassString ( newClassValue ) ;
77
94
78
- attr . $addClass ( toAdd ) ;
79
- attr . $removeClass ( toRemove ) ;
95
+ if ( newClassString !== oldClassString ) {
96
+ ngClassWatchAction ( newClassString ) ;
97
+ }
80
98
}
81
99
82
- function ngClassWatchAction ( newVal ) {
100
+ function ngClassWatchAction ( newClassString ) {
83
101
if ( oldModulo === selector ) {
84
- var newClasses = arrayClasses ( newVal || [ ] ) ;
85
- if ( ! oldVal ) {
86
- addClasses ( newClasses ) ;
87
- } else if ( ! equals ( newVal , oldVal ) ) {
88
- var oldClasses = arrayClasses ( oldVal ) ;
89
- updateClasses ( oldClasses , newClasses ) ;
90
- }
91
- }
92
- if ( isArray ( newVal ) ) {
93
- oldVal = newVal . map ( function ( v ) { return shallowCopy ( v ) ; } ) ;
94
- } else {
95
- oldVal = shallowCopy ( newVal ) ;
102
+ updateClasses ( oldClassString , newClassString ) ;
96
103
}
104
+
105
+ oldClassString = newClassString ;
97
106
}
98
107
}
99
108
} ;
@@ -118,24 +127,50 @@ function classDirective(name, selector) {
118
127
return values ;
119
128
}
120
129
121
- function arrayClasses ( classVal ) {
122
- var classes = [ ] ;
123
- if ( isArray ( classVal ) ) {
124
- forEach ( classVal , function ( v ) {
125
- classes = classes . concat ( arrayClasses ( v ) ) ;
126
- } ) ;
127
- return classes ;
128
- } else if ( isString ( classVal ) ) {
129
- return classVal . split ( ' ' ) ;
130
- } else if ( isObject ( classVal ) ) {
131
- forEach ( classVal , function ( v , k ) {
132
- if ( v ) {
133
- classes = classes . concat ( k . split ( ' ' ) ) ;
130
+ function split ( classString ) {
131
+ return classString && classString . split ( ' ' ) ;
132
+ }
133
+
134
+ function toClassString ( classValue ) {
135
+ var classString = classValue ;
136
+
137
+ if ( isArray ( classValue ) ) {
138
+ classString = classValue . map ( toClassString ) . join ( ' ' ) ;
139
+ } else if ( isObject ( classValue ) ) {
140
+ classString = Object . keys ( classValue ) .
141
+ filter ( function ( key ) { return classValue [ key ] ; } ) .
142
+ join ( ' ' ) ;
143
+ }
144
+
145
+ return classString ;
146
+ }
147
+
148
+ function toFlatValue ( classValue ) {
149
+ var flatValue = classValue ;
150
+
151
+ if ( isArray ( classValue ) ) {
152
+ flatValue = classValue . map ( toFlatValue ) ;
153
+ } else if ( isObject ( classValue ) ) {
154
+ var hasUndefined = false ;
155
+
156
+ flatValue = Object . keys ( classValue ) . filter ( function ( key ) {
157
+ var value = classValue [ key ] ;
158
+
159
+ if ( ! hasUndefined && isUndefined ( value ) ) {
160
+ hasUndefined = true ;
134
161
}
162
+
163
+ return value ;
135
164
} ) ;
136
- return classes ;
165
+
166
+ if ( hasUndefined ) {
167
+ // Prevent the `oneTimeLiteralWatchInterceptor` from unregistering
168
+ // the watcher, by including at least one `undefined` value.
169
+ flatValue . push ( undefined ) ;
170
+ }
137
171
}
138
- return classVal ;
172
+
173
+ return flatValue ;
139
174
}
140
175
}
141
176
0 commit comments