@@ -48,55 +48,135 @@ $.widget( "mobile.selectmenu", $.mobile.selectmenu, {
48
48
return this . _super ( ) ;
49
49
} ,
50
50
51
+ _handleSelectFocus : function ( ) {
52
+ this . element . blur ( ) ;
53
+ this . button . focus ( ) ;
54
+ } ,
55
+
56
+ _handleButtonVclickKeydown : function ( event ) {
57
+ if ( this . options . disabled || this . isOpen ) {
58
+ return ;
59
+ }
60
+
61
+ if ( event . type === "vclick" ||
62
+ event . keyCode && ( event . keyCode === $ . mobile . keyCode . ENTER || event . keyCode === $ . mobile . keyCode . SPACE ) ) {
63
+
64
+ this . _decideFormat ( ) ;
65
+ if ( this . menuType === "overlay" ) {
66
+ this . button . attr ( "href" , "#" + this . popupId ) . attr ( "data-" + ( $ . mobile . ns || "" ) + "rel" , "popup" ) ;
67
+ } else {
68
+ this . button . attr ( "href" , "#" + this . dialogId ) . attr ( "data-" + ( $ . mobile . ns || "" ) + "rel" , "dialog" ) ;
69
+ }
70
+ this . isOpen = true ;
71
+ // Do not prevent default, so the navigation may have a chance to actually open the chosen format
72
+ }
73
+ } ,
74
+
75
+ _handleListFocus : function ( e ) {
76
+ var params = ( e . type === "focusin" ) ?
77
+ { tabindex : "0" , event : "vmouseover" } :
78
+ { tabindex : "-1" , event : "vmouseout" } ;
79
+
80
+ $ ( e . target )
81
+ . attr ( "tabindex" , params . tabindex )
82
+ . trigger ( params . event ) ;
83
+ } ,
84
+
85
+ _handleListKeydown : function ( event ) {
86
+ var target = $ ( event . target ) ,
87
+ li = target . closest ( "li" ) ;
88
+
89
+ // switch logic based on which key was pressed
90
+ switch ( event . keyCode ) {
91
+ // up or left arrow keys
92
+ case 38 :
93
+ goToAdjacentItem ( li , target , "prev" ) ;
94
+ return false ;
95
+ // down or right arrow keys
96
+ case 40 :
97
+ goToAdjacentItem ( li , target , "next" ) ;
98
+ return false ;
99
+ // If enter or space is pressed, trigger click
100
+ case 13 :
101
+ case 32 :
102
+ target . trigger ( "click" ) ;
103
+ return false ;
104
+ }
105
+ } ,
106
+
107
+ _handleMenuPageHide : function ( ) {
108
+ // TODO centralize page removal binding / handling in the page plugin.
109
+ // Suggestion from @jblas to do refcounting
110
+ //
111
+ // TODO extremely confusing dependency on the open method where the pagehide.remove
112
+ // bindings are stripped to prevent the parent page from disappearing. The way
113
+ // we're keeping pages in the DOM right now sucks
114
+ //
115
+ // rebind the page remove that was unbound in the open function
116
+ // to allow for the parent page removal from actions other than the use
117
+ // of a dialog sized custom select
118
+ //
119
+ // doing this here provides for the back button on the custom select dialog
120
+ $ . mobile . _bindPageRemove . call ( this . thisPage ) ;
121
+ } ,
122
+
123
+ _handleHeaderCloseClick : function ( ) {
124
+ if ( this . menuType === "overlay" ) {
125
+ this . close ( ) ;
126
+ return false ;
127
+ }
128
+ } ,
129
+
51
130
build : function ( ) {
52
- var selectID , prefix , popupID , dialogID , label , thisPage , isMultiple , menuId , themeAttr , overlayThemeAttr ,
53
- dividerThemeAttr , menuPage , listbox , list , header , headerTitle , menuPageContent , menuPageClose , headerClose , self ;
131
+ var selectId , prefix , popupId , dialogId , label , thisPage , isMultiple , menuId , themeAttr , overlayThemeAttr ,
132
+ dividerThemeAttr , menuPage , listbox , list , header , headerTitle , menuPageContent , menuPageClose , headerClose , self ,
133
+ o = this . options ;
54
134
55
- if ( this . options . nativeMenu ) {
135
+ if ( o . nativeMenu ) {
56
136
return this . _super ( ) ;
57
137
}
58
138
59
139
self = this ;
60
- selectID = this . selectID ;
61
- prefix = ( selectID ? selectID : ( ( $ . mobile . ns || "" ) + "uuid-" + this . uuid ) ) ;
62
- popupID = prefix + "-listbox" ;
63
- dialogID = prefix + "-dialog" ;
140
+ selectId = this . selectId ;
141
+ prefix = ( selectId ? selectId : ( ( $ . mobile . ns || "" ) + "uuid-" + this . uuid ) ) ;
142
+ popupId = prefix + "-listbox" ;
143
+ dialogId = prefix + "-dialog" ;
64
144
label = this . label ;
65
145
thisPage = this . element . closest ( ".ui-page" ) ;
66
146
isMultiple = this . element [ 0 ] . multiple ;
67
- menuId = selectID + "-menu" ;
68
- themeAttr = this . options . theme ? ( " data-" + $ . mobile . ns + "theme='" + this . options . theme + "'" ) : "" ;
69
- overlayThemeAttr = this . options . overlayTheme ? ( " data-" + $ . mobile . ns + "theme='" + this . options . overlayTheme + "'" ) : "" ;
70
- dividerThemeAttr = ( this . options . dividerTheme && isMultiple ) ? ( " data-" + $ . mobile . ns + "divider-theme='" + this . options . dividerTheme + "'" ) : "" ;
71
- menuPage = $ ( "<div data-" + $ . mobile . ns + "role='dialog' class='ui-selectmenu' id='" + dialogID + "'" + themeAttr + overlayThemeAttr + ">" +
147
+ menuId = selectId + "-menu" ;
148
+ themeAttr = o . theme ? ( " data-" + $ . mobile . ns + "theme='" + o . theme + "'" ) : "" ;
149
+ overlayThemeAttr = o . overlayTheme ? ( " data-" + $ . mobile . ns + "theme='" + o . overlayTheme + "'" ) : "" ;
150
+ dividerThemeAttr = ( o . dividerTheme && isMultiple ) ? ( " data-" + $ . mobile . ns + "divider-theme='" + o . dividerTheme + "'" ) : "" ;
151
+ menuPage = $ ( "<div data-" + $ . mobile . ns + "role='dialog' class='ui-selectmenu' id='" + dialogId + "'" + themeAttr + overlayThemeAttr + ">" +
72
152
"<div data-" + $ . mobile . ns + "role='header'>" +
73
153
"<div class='ui-title'>" + label . getEncodedText ( ) + "</div>" +
74
154
"</div>" +
75
155
"<div data-" + $ . mobile . ns + "role='content'></div>" +
76
156
"</div>" ) ;
77
- listbox = $ ( "<div id='" + popupID + "' class='ui-selectmenu'>" ) . insertAfter ( this . select ) . popup ( { theme : this . options . overlayTheme } ) ;
157
+ listbox = $ ( "<div id='" + popupId + "' class='ui-selectmenu'>" ) . insertAfter ( this . select ) . popup ( { theme : o . overlayTheme } ) ;
78
158
list = $ ( "<ul class='ui-selectmenu-list' id='" + menuId + "' role='listbox' aria-labelledby='" + this . buttonId + "'" + themeAttr + dividerThemeAttr + ">" ) . appendTo ( listbox ) ;
79
- header = $ ( "<div class='ui-header ui-bar-" + ( this . options . theme ? this . options . theme : "inherit" ) + "'>" ) . prependTo ( listbox ) ;
159
+ header = $ ( "<div class='ui-header ui-bar-" + ( o . theme ? o . theme : "inherit" ) + "'>" ) . prependTo ( listbox ) ;
80
160
headerTitle = $ ( "<h1 class='ui-title'>" ) . appendTo ( header ) ;
81
161
82
162
if ( this . isMultiple ) {
83
163
headerClose = $ ( "<a>" , {
84
- "text" : this . options . closeText ,
164
+ "text" : o . closeText ,
85
165
"href" : "#" ,
86
166
"class" : "ui-btn ui-corner-all ui-btn-left ui-btn-icon-notext ui-icon-delete"
87
167
} ) . appendTo ( header ) ;
88
168
}
89
169
90
170
$ . extend ( this , {
91
- selectID : selectID ,
171
+ selectId : selectId ,
92
172
menuId : menuId ,
93
- popupID : popupID ,
94
- dialogID : dialogID ,
173
+ popupId : popupId ,
174
+ dialogId : dialogId ,
95
175
thisPage : thisPage ,
96
176
menuPage : menuPage ,
97
177
label : label ,
98
178
isMultiple : isMultiple ,
99
- theme : this . options . theme ,
179
+ theme : o . theme ,
100
180
listbox : listbox ,
101
181
list : list ,
102
182
header : header ,
@@ -108,59 +188,38 @@ $.widget( "mobile.selectmenu", $.mobile.selectmenu, {
108
188
} ) ;
109
189
110
190
// Create list from select, update state
111
- self . refresh ( ) ;
191
+ this . refresh ( ) ;
112
192
113
- if ( self . _origTabIndex === undefined ) {
114
- // Map undefined to false, because self ._origTabIndex === undefined
193
+ if ( this . _origTabIndex === undefined ) {
194
+ // Map undefined to false, because this ._origTabIndex === undefined
115
195
// indicates that we have not yet checked whether the select has
116
196
// originally had a tabindex attribute, whereas false indicates that
117
197
// we have checked the select for such an attribute, and have found
118
198
// none present.
119
- self . _origTabIndex = ( self . select [ 0 ] . getAttribute ( "tabindex" ) === null ) ? false : self . select . attr ( "tabindex" ) ;
199
+ this . _origTabIndex = ( this . select [ 0 ] . getAttribute ( "tabindex" ) === null ) ? false : this . select . attr ( "tabindex" ) ;
120
200
}
121
- self . select . attr ( "tabindex" , "-1" ) . focus ( function ( ) {
122
- $ ( this ) . blur ( ) ;
123
- self . button . focus ( ) ;
124
- } ) ;
201
+ this . select . attr ( "tabindex" , "-1" ) ;
202
+ this . _on ( this . select , { focus : "_handleSelectFocus" } ) ;
125
203
126
204
// Button events
127
- self . button . bind ( "vclick keydown" , function ( event ) {
128
- if ( self . options . disabled || self . isOpen ) {
129
- return ;
130
- }
131
-
132
- if ( event . type === "vclick" ||
133
- event . keyCode && ( event . keyCode === $ . mobile . keyCode . ENTER || event . keyCode === $ . mobile . keyCode . SPACE ) ) {
134
-
135
- self . _decideFormat ( ) ;
136
- if ( self . menuType === "overlay" ) {
137
- self . button . attr ( "href" , "#" + self . popupID ) . attr ( "data-" + ( $ . mobile . ns || "" ) + "rel" , "popup" ) ;
138
- } else {
139
- self . button . attr ( "href" , "#" + self . dialogID ) . attr ( "data-" + ( $ . mobile . ns || "" ) + "rel" , "dialog" ) ;
140
- }
141
- self . isOpen = true ;
142
- // Do not prevent default, so the navigation may have a chance to actually open the chosen format
143
- }
205
+ this . _on ( this . button , {
206
+ vclick : "_handleButtonVclickKeydown" ,
207
+ keydown : "_handleButtonVclickKeydown"
144
208
} ) ;
145
209
146
210
// Events for list items
147
- self . list . attr ( "role" , "listbox" )
148
- . bind ( "focusin" , function ( e ) {
149
- $ ( e . target )
150
- . attr ( "tabindex" , "0" )
151
- . trigger ( "vmouseover" ) ;
152
-
153
- } )
154
- . bind ( "focusout" , function ( e ) {
155
- $ ( e . target )
156
- . attr ( "tabindex" , "-1" )
157
- . trigger ( "vmouseout" ) ;
158
- } )
211
+ this . list . attr ( "role" , "listbox" ) ;
212
+ this . _on ( this . list , {
213
+ focusin : "_handleListFocus" ,
214
+ focusout : "_handleListFocus" ,
215
+ keydown : "_handleListKeydown"
216
+ } ) ;
217
+ this . list
159
218
. delegate ( "li:not(.ui-disabled, .ui-li-divider)" , "click" , function ( event ) {
160
219
161
220
// index of option tag to be selected
162
221
var oldIndex = self . select [ 0 ] . selectedIndex ,
163
- newIndex = self . list . find ( "li:not(.ui-li-divider)" ) . index ( this ) ,
222
+ newIndex = $ . mobile . getAttribute ( this , "option-index" , true ) ,
164
223
option = self . _selectOptions ( ) . eq ( newIndex ) [ 0 ] ;
165
224
166
225
// toggle selected status on the tag for multi selects
@@ -189,67 +248,20 @@ $.widget( "mobile.selectmenu", $.mobile.selectmenu, {
189
248
}
190
249
191
250
event . preventDefault ( ) ;
192
- } )
193
- . keydown ( function ( event ) { //keyboard events for menu items
194
- var target = $ ( event . target ) ,
195
- li = target . closest ( "li" ) ;
196
-
197
- // switch logic based on which key was pressed
198
- switch ( event . keyCode ) {
199
- // up or left arrow keys
200
- case 38 :
201
- goToAdjacentItem ( li , target , "prev" ) ;
202
- return false ;
203
- // down or right arrow keys
204
- case 40 :
205
- goToAdjacentItem ( li , target , "next" ) ;
206
- return false ;
207
- // If enter or space is pressed, trigger click
208
- case 13 :
209
- case 32 :
210
- target . trigger ( "click" ) ;
211
-
212
- return false ;
213
- }
214
251
} ) ;
215
252
216
253
// button refocus ensures proper height calculation
217
254
// by removing the inline style and ensuring page inclusion
218
- self . menuPage . bind ( "pagehide" , function ( ) {
219
- // TODO centralize page removal binding / handling in the page plugin.
220
- // Suggestion from @jblas to do refcounting
221
- //
222
- // TODO extremely confusing dependency on the open method where the pagehide.remove
223
- // bindings are stripped to prevent the parent page from disappearing. The way
224
- // we're keeping pages in the DOM right now sucks
225
- //
226
- // rebind the page remove that was unbound in the open function
227
- // to allow for the parent page removal from actions other than the use
228
- // of a dialog sized custom select
229
- //
230
- // doing this here provides for the back button on the custom select dialog
231
- $ . mobile . _bindPageRemove . call ( self . thisPage ) ;
232
- } ) ;
255
+ this . _on ( this . menuPage , { pagehide : "_handleMenuPageHide" } ) ;
233
256
234
257
// Events on the popup
235
- self . listbox . bind ( "popupafterclose" , function ( ) {
236
- self . close ( ) ;
237
- } ) ;
258
+ this . _on ( this . listbox , { popupafterclose : "close" } ) ;
238
259
239
260
// Close button on small overlays
240
- if ( self . isMultiple ) {
241
- self . headerClose . click ( function ( ) {
242
- if ( self . menuType === "overlay" ) {
243
- self . close ( ) ;
244
- return false ;
245
- }
246
- } ) ;
261
+ if ( this . isMultiple ) {
262
+ this . _on ( this . headerClose , { click : "_handleHeaderCloseClick" } ) ;
247
263
}
248
264
249
- // track this dependency so that when the parent page
250
- // is removed on pagehide it will also remove the menupage
251
- self . thisPage . addDependents ( this . menuPage ) ;
252
-
253
265
return this ;
254
266
} ,
255
267
@@ -332,6 +344,14 @@ $.widget( "mobile.selectmenu", $.mobile.selectmenu, {
332
344
this . button . click ( ) ;
333
345
} ,
334
346
347
+ _focusMenuItem : function ( ) {
348
+ var selector = this . list . find ( "a." + $ . mobile . activeBtnClass ) ;
349
+ if ( selector . length === 0 ) {
350
+ selector = this . list . find ( "li:not(" + unfocusableItemSelector + ") a.ui-btn" ) ;
351
+ }
352
+ selector . first ( ) . focus ( ) ;
353
+ } ,
354
+
335
355
_decideFormat : function ( ) {
336
356
var self = this ,
337
357
$window = $ . mobile . window ,
@@ -341,14 +361,6 @@ $.widget( "mobile.selectmenu", $.mobile.selectmenu, {
341
361
btnOffset = self . button . offset ( ) . top ,
342
362
screenHeight = $window . height ( ) ;
343
363
344
- function focusMenuItem ( ) {
345
- var selector = self . list . find ( "a." + $ . mobile . activeBtnClass ) ;
346
- if ( selector . length === 0 ) {
347
- selector = self . list . find ( "li:not(" + unfocusableItemSelector + ") a.ui-btn" ) ;
348
- }
349
- selector . first ( ) . focus ( ) ;
350
- }
351
-
352
364
if ( menuHeight > screenHeight - 80 || ! $ . support . scrollTop ) {
353
365
354
366
self . menuPage . appendTo ( $ . mobile . pageContainer ) . page ( ) ;
@@ -367,21 +379,18 @@ $.widget( "mobile.selectmenu", $.mobile.selectmenu, {
367
379
} ) ;
368
380
}
369
381
370
- self . menuPage
371
- . one ( "pageshow" , function ( ) {
372
- focusMenuItem ( ) ;
373
- } )
374
- . one ( "pagehide" , function ( ) {
375
- self . close ( ) ;
376
- } ) ;
382
+ self . menuPage . one ( {
383
+ pageshow : $ . proxy ( this , "_focusMenuItem" ) ,
384
+ pagehide : $ . proxy ( this , "close" )
385
+ } ) ;
377
386
378
387
self . menuType = "page" ;
379
388
self . menuPageContent . append ( self . list ) ;
380
389
self . menuPage . find ( "div .ui-title" ) . text ( self . label . text ( ) ) ;
381
390
} else {
382
391
self . menuType = "overlay" ;
383
392
384
- self . listbox . one ( " popupafteropen" , focusMenuItem ) ;
393
+ self . listbox . one ( { popupafteropen : $ . proxy ( this , "_focusMenuItem" ) } ) ;
385
394
}
386
395
} ,
387
396
0 commit comments