@@ -81,52 +81,27 @@ function $AriaProvider() {
81
81
config = angular . extend ( config , newConfig ) ;
82
82
} ;
83
83
84
- function dashCase ( input ) {
85
- return input . replace ( / [ A - Z ] / g, function ( letter , pos ) {
86
- return ( pos ? '-' : '' ) + letter . toLowerCase ( ) ;
84
+ function camelCase ( input ) {
85
+ return input . replace ( / - . / g, function ( letter , pos ) {
86
+ return letter [ 1 ] . toUpperCase ( ) ;
87
87
} ) ;
88
88
}
89
89
90
- function watchAttr ( attrName , ariaName ) {
91
- var ariaDashName = dashCase ( ariaName ) ;
92
- return function ( scope , elem , attr ) {
93
- if ( ! config [ ariaName ] || elem . attr ( ariaDashName ) ) {
94
- return ;
95
- }
96
- var destroyWatcher = attr . $observe ( attrName , function ( newVal ) {
97
- elem . attr ( ariaDashName , ! angular . isUndefined ( newVal ) ) ;
98
- } ) ;
99
- scope . $on ( '$destroy' , destroyWatcher ) ;
100
- } ;
101
- }
102
90
103
- function watchExpr ( attrName , ariaName , negate ) {
104
- var ariaDashName = dashCase ( ariaName ) ;
91
+ function watchExpr ( attrName , ariaAttr , negate ) {
92
+ var ariaCamelName = camelCase ( ariaAttr ) ;
105
93
return function ( scope , elem , attr ) {
106
- if ( config [ ariaName ] && ! attr [ ariaName ] ) {
94
+ if ( config [ ariaCamelName ] && ! attr [ ariaCamelName ] ) {
107
95
scope . $watch ( attr [ attrName ] , function ( boolVal ) {
108
96
if ( negate ) {
109
97
boolVal = ! boolVal ;
110
98
}
111
- elem . attr ( ariaDashName , boolVal ) ;
99
+ elem . attr ( ariaAttr , boolVal ) ;
112
100
} ) ;
113
101
}
114
102
} ;
115
103
}
116
104
117
- function watchNgModelProperty ( prop , watchFn ) {
118
- var ariaAttrName = 'aria-' + prop ,
119
- configName = 'aria' + prop [ 0 ] . toUpperCase ( ) + prop . substr ( 1 ) ;
120
- return function watchNgModelPropertyLinkFn ( scope , elem , attr , ngModel ) {
121
- if ( ! config [ configName ] || elem . attr ( ariaAttrName ) || ! ngModel ) {
122
- return ;
123
- }
124
- scope . $watch ( watchFn ( ngModel ) , function ( newVal ) {
125
- elem . attr ( ariaAttrName , ! ! newVal ) ;
126
- } ) ;
127
- } ;
128
- }
129
-
130
105
/**
131
106
* @ngdoc service
132
107
* @name $aria
@@ -140,135 +115,128 @@ function $AriaProvider() {
140
115
*/
141
116
this . $get = function ( ) {
142
117
return {
143
- watchExpr : watchExpr ,
144
- ariaChecked : watchExpr ( 'ngModel' , 'ariaChecked' ) ,
145
- ariaDisabled : watchExpr ( 'ngDisabled' , 'ariaDisabled' ) ,
146
- ariaRequired : watchNgModelProperty ( 'required' , function ( ngModel ) {
147
- return function ngAriaModelWatch ( ) {
148
- return ngModel . $error . required ;
149
- } ;
150
- } ) ,
151
- ariaInvalid : watchNgModelProperty ( 'invalid' , function ( ngModel ) {
152
- return function ngAriaModelWatch ( ) {
153
- return ngModel . $invalid ;
154
- } ;
155
- } ) ,
156
- ariaValue : function ( scope , elem , attr , ngModel ) {
157
- if ( config . ariaValue ) {
158
- if ( attr . min && ! elem . attr ( 'aria-valuemin' ) ) {
159
- elem . attr ( 'aria-valuemin' , attr . min ) ;
160
- }
161
- if ( attr . max && ! elem . attr ( 'aria-valuemax' ) ) {
162
- elem . attr ( 'aria-valuemax' , attr . max ) ;
163
- }
164
- if ( ngModel && ! elem . attr ( 'aria-valuenow' ) ) {
165
- scope . $watch ( function ngAriaModelWatch ( ) {
166
- return ngModel . $modelValue ;
167
- } , function ngAriaValueNowReaction ( newVal ) {
168
- elem . attr ( 'aria-valuenow' , newVal ) ;
169
- } ) ;
170
- }
171
- }
172
- } ,
173
- radio : function ( scope , elem , attr , ngModel ) {
174
- if ( config . ariaChecked && ngModel && ! elem . attr ( 'aria-checked' ) ) {
175
- var needsTabIndex = config . tabindex && ! elem . attr ( 'tabindex' ) ;
176
- scope . $watch ( function ( ) {
177
- return ngModel . $modelValue ;
178
- } , function ( newVal ) {
179
- elem . attr ( 'aria-checked' , newVal === attr . value ) ;
180
- if ( needsTabIndex ) {
181
- elem . attr ( 'tabindex' , 0 - ( newVal !== attr . value ) ) ;
182
- }
183
- } ) ;
184
- }
185
- } ,
186
- multiline : function ( scope , elem , attr ) {
187
- if ( config . ariaMultiline && ! elem . attr ( 'aria-multiline' ) ) {
188
- elem . attr ( 'aria-multiline' , true ) ;
189
- }
118
+ config : function ( key ) {
119
+ return config [ camelCase ( key ) ] ;
190
120
} ,
191
- roleChecked : function ( scope , elem , attr ) {
192
- if ( config . ariaChecked && attr . checked && ! elem . attr ( 'aria-checked' ) ) {
193
- elem . attr ( 'aria-checked' , true ) ;
194
- }
195
- } ,
196
- tabindex : function ( scope , elem , attr ) {
197
- if ( config . tabindex && ! elem . attr ( 'tabindex' ) ) {
198
- elem . attr ( 'tabindex' , 0 ) ;
199
- }
200
- }
121
+ $$watchExpr : watchExpr
201
122
} ;
202
123
} ;
203
124
}
204
125
205
- var ngAriaRequired = [ '$aria' , function ( $aria ) {
206
- return {
207
- require : '?ngModel' ,
208
- link : $aria . ariaRequired
209
- } ;
210
- } ] ;
211
-
212
126
var ngAriaTabindex = [ '$aria' , function ( $aria ) {
213
- return $aria . tabindex ;
127
+ return function ( scope , elem , attr ) {
128
+ if ( $aria . config ( 'tabindex' ) && ! elem . attr ( 'tabindex' ) ) {
129
+ elem . attr ( 'tabindex' , 0 ) ;
130
+ }
131
+ } ;
214
132
} ] ;
215
133
216
134
ngAriaModule . directive ( 'ngShow' , [ '$aria' , function ( $aria ) {
217
- return $aria . watchExpr ( 'ngShow' , 'ariaHidden ' , true ) ;
135
+ return $aria . $$ watchExpr( 'ngShow' , 'aria-hidden ' , true ) ;
218
136
} ] )
219
137
. directive ( 'ngHide' , [ '$aria' , function ( $aria ) {
220
- return $aria . watchExpr ( 'ngHide' , 'ariaHidden ' , false ) ;
138
+ return $aria . $$ watchExpr( 'ngHide' , 'aria-hidden ' , false ) ;
221
139
} ] )
222
- . directive ( 'input' , [ '$aria' , function ( $aria ) {
140
+ . directive ( 'ngModel' , [ '$aria' , function ( $aria ) {
141
+
142
+ function shouldAttachAttr ( attr , elem ) {
143
+ return $aria . config ( attr ) && ! elem . attr ( attr ) ;
144
+ }
145
+
146
+ function getShape ( attr , elem ) {
147
+ var type = attr . type ,
148
+ role = attr . role ;
149
+
150
+ return ( ( type || role ) === 'checkbox' || role === 'menuitemcheckbox' ) ? 'checkbox' :
151
+ ( ( type || role ) === 'radio' || role === 'menuitemradio' ) ? 'radio' :
152
+ ( type === 'range' || role === 'progressbar' || role === 'slider' ) ? 'range' :
153
+ ( type || role ) === 'textbox' || elem [ 0 ] . nodeName === 'TEXTAREA' ? 'multiline' : '' ;
154
+ }
155
+
223
156
return {
224
- restrict : 'E ' ,
157
+ restrict : 'A ' ,
225
158
require : '?ngModel' ,
226
159
link : function ( scope , elem , attr , ngModel ) {
227
- if ( attr . type === 'checkbox' ) {
228
- $aria . ariaChecked ( scope , elem , attr ) ;
229
- } else if ( attr . type === 'radio' ) {
230
- $aria . radio ( scope , elem , attr , ngModel ) ;
231
- } else if ( attr . type === 'range' ) {
232
- $aria . ariaValue ( scope , elem , attr , ngModel ) ;
160
+ var shape = getShape ( attr , elem ) ;
161
+ var needsTabIndex = shouldAttachAttr ( 'tabindex' , elem ) ;
162
+
163
+ function ngAriaWatchModelValue ( ) {
164
+ return ngModel . $modelValue ;
165
+ }
166
+
167
+ function getRadioReaction ( ) {
168
+ if ( needsTabIndex ) {
169
+ needsTabIndex = false ;
170
+ return function ngAriaRadioReaction ( newVal ) {
171
+ var boolVal = newVal === attr . value ;
172
+ elem . attr ( 'aria-checked' , boolVal ) ;
173
+ elem . attr ( 'tabindex' , 0 - ! boolVal ) ;
174
+ } ;
175
+ } else {
176
+ return function ngAriaRadioReaction ( newVal ) {
177
+ elem . attr ( 'aria-checked' , newVal === attr . value ) ;
178
+ } ;
179
+ }
180
+ }
181
+
182
+ function ngAriaCheckboxReaction ( newVal ) {
183
+ elem . attr ( 'aria-checked' , ! ! newVal ) ;
184
+ }
185
+
186
+ switch ( shape ) {
187
+ case 'radio' :
188
+ case 'checkbox' :
189
+ if ( shouldAttachAttr ( 'aria-checked' , elem ) ) {
190
+ scope . $watch ( ngAriaWatchModelValue , shape === 'radio' ?
191
+ getRadioReaction ( ) : ngAriaCheckboxReaction ) ;
192
+ }
193
+ break ;
194
+ case 'range' :
195
+ if ( $aria . config ( 'ariaValue' ) ) {
196
+ if ( attr . min && ! elem . attr ( 'aria-valuemin' ) ) {
197
+ elem . attr ( 'aria-valuemin' , attr . min ) ;
198
+ }
199
+ if ( attr . max && ! elem . attr ( 'aria-valuemax' ) ) {
200
+ elem . attr ( 'aria-valuemax' , attr . max ) ;
201
+ }
202
+ if ( ! elem . attr ( 'aria-valuenow' ) ) {
203
+ scope . $watch ( ngAriaWatchModelValue , function ngAriaValueNowReaction ( newVal ) {
204
+ elem . attr ( 'aria-valuenow' , newVal ) ;
205
+ } ) ;
206
+ }
207
+ }
208
+ break ;
209
+ case 'multiline' :
210
+ if ( shouldAttachAttr ( 'aria-multiline' , elem ) ) {
211
+ elem . attr ( 'aria-multiline' , true ) ;
212
+ }
213
+ break ;
214
+ }
215
+
216
+ if ( needsTabIndex ) {
217
+ elem . attr ( 'tabindex' , 0 ) ;
218
+ }
219
+
220
+ if ( ngModel . $validators . required && shouldAttachAttr ( 'aria-required' , elem ) ) {
221
+ scope . $watch ( function ngAriaRequiredWatch ( ) {
222
+ return ngModel . $error . required ;
223
+ } , function ngAriaRequiredReaction ( newVal ) {
224
+ elem . attr ( 'aria-required' , ! ! newVal ) ;
225
+ } ) ;
226
+ }
227
+
228
+ if ( shouldAttachAttr ( 'aria-invalid' , elem ) ) {
229
+ scope . $watch ( function ngAriaInvalidWatch ( ) {
230
+ return ngModel . $invalid ;
231
+ } , function ngAriaInvalidReaction ( newVal ) {
232
+ elem . attr ( 'aria-invalid' , ! ! newVal ) ;
233
+ } ) ;
233
234
}
234
- $aria . ariaInvalid ( scope , elem , attr , ngModel ) ;
235
- }
236
- } ;
237
- } ] )
238
- . directive ( 'textarea' , [ '$aria' , function ( $aria ) {
239
- return {
240
- restrict : 'E' ,
241
- require : '?ngModel' ,
242
- link : function ( scope , elem , attr , ngModel ) {
243
- $aria . ariaInvalid ( scope , elem , attr , ngModel ) ;
244
- $aria . multiline ( scope , elem , attr ) ;
245
235
}
246
236
} ;
247
237
} ] )
248
- . directive ( 'ngRequired' , ngAriaRequired )
249
- . directive ( 'required' , ngAriaRequired )
250
238
. directive ( 'ngDisabled' , [ '$aria' , function ( $aria ) {
251
- return $aria . ariaDisabled ;
252
- } ] )
253
- . directive ( 'role' , [ '$aria' , function ( $aria ) {
254
- return {
255
- restrict : 'A' ,
256
- require : '?ngModel' ,
257
- link : function ( scope , elem , attr , ngModel ) {
258
- if ( attr . role === 'textbox' ) {
259
- $aria . multiline ( scope , elem , attr ) ;
260
- } else if ( attr . role === 'progressbar' || attr . role === 'slider' ) {
261
- $aria . ariaValue ( scope , elem , attr , ngModel ) ;
262
- } else if ( attr . role === 'checkbox' || attr . role === 'menuitemcheckbox' ) {
263
- $aria . roleChecked ( scope , elem , attr ) ;
264
- $aria . tabindex ( scope , elem , attr ) ;
265
- } else if ( attr . role === 'radio' || attr . role === 'menuitemradio' ) {
266
- $aria . radio ( scope , elem , attr , ngModel ) ;
267
- } else if ( attr . role === 'button' ) {
268
- $aria . tabindex ( scope , elem , attr ) ;
269
- }
270
- }
271
- } ;
239
+ return $aria . $$watchExpr ( 'ngDisabled' , 'aria-disabled' ) ;
272
240
} ] )
273
241
. directive ( 'ngClick' , ngAriaTabindex )
274
242
. directive ( 'ngDblclick' , ngAriaTabindex ) ;
0 commit comments