mirror of
https://github.com/Hopiu/jquery-mobile.git
synced 2026-05-28 23:48:15 +00:00
minimize extension in individual widgets
This commit is contained in:
parent
5c76de355a
commit
76adbc9293
3 changed files with 374 additions and 397 deletions
|
|
@ -25,408 +25,397 @@ $.widget( "mobile.customselect", $.mobile.widget, {
|
||||||
initSelector: "select:not(:jqmData(role='slider'))"
|
initSelector: "select:not(:jqmData(role='slider'))"
|
||||||
},
|
},
|
||||||
|
|
||||||
_common: $.mobile.selectShared,
|
_shared: $.mobile.selectShared,
|
||||||
|
|
||||||
_custom: function() {
|
_create: function() {
|
||||||
var widget = this, common = this._common();
|
var widget = this, shared = this._shared();
|
||||||
|
|
||||||
return $.extend(common, {
|
$.extend( widget, shared, {
|
||||||
typeName: 'custom',
|
typeName: 'custom',
|
||||||
|
|
||||||
button: $( "<a>", {
|
button: $( "<a>", {
|
||||||
"href": "#",
|
"href": "#",
|
||||||
"role": "button",
|
"role": "button",
|
||||||
// TODO value is undefined at creation
|
// TODO value is undefined at creation
|
||||||
"id": common.buttonId,
|
"id": shared.buttonId,
|
||||||
"aria-haspopup": "true",
|
"aria-haspopup": "true",
|
||||||
|
|
||||||
// TODO value is undefined at creation
|
// TODO value is undefined at creation
|
||||||
"aria-owns": common.menuId
|
"aria-owns": shared.menuId
|
||||||
}),
|
})
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
build: function() {
|
build: function() {
|
||||||
var self = this, parentWidget = widget;
|
var self = this, widget = this;
|
||||||
|
|
||||||
// Create list from select, update state
|
// Create list from select, update state
|
||||||
widget.refresh();
|
widget.refresh();
|
||||||
|
|
||||||
self.select.attr( "tabindex", "-1" )
|
self.select.attr( "tabindex", "-1" )
|
||||||
.focus(function() {
|
.focus(function() {
|
||||||
$(this).blur();
|
$(this).blur();
|
||||||
button.focus();
|
button.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Button events
|
// Button events
|
||||||
self.button.bind( "vclick keydown" , function( event ) {
|
self.button.bind( "vclick keydown" , function( event ) {
|
||||||
if ( event.type == "vclick" ||
|
if ( event.type == "vclick" ||
|
||||||
event.keyCode && ( event.keyCode === $.mobile.keyCode.ENTER ||
|
event.keyCode && ( event.keyCode === $.mobile.keyCode.ENTER ||
|
||||||
event.keyCode === $.mobile.keyCode.SPACE ) ) {
|
event.keyCode === $.mobile.keyCode.SPACE ) ) {
|
||||||
|
|
||||||
widget.open();
|
widget.open();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Events for list items
|
// Events for list items
|
||||||
self.list.attr( "role", "listbox" )
|
self.list.attr( "role", "listbox" )
|
||||||
.delegate( ".ui-li>a", "focusin", function() {
|
.delegate( ".ui-li>a", "focusin", function() {
|
||||||
$( this ).attr( "tabindex", "0" );
|
$( this ).attr( "tabindex", "0" );
|
||||||
})
|
})
|
||||||
.delegate( ".ui-li>a", "focusout", function() {
|
.delegate( ".ui-li>a", "focusout", function() {
|
||||||
$( this ).attr( "tabindex", "-1" );
|
$( this ).attr( "tabindex", "-1" );
|
||||||
})
|
})
|
||||||
.delegate( "li:not(.ui-disabled, .ui-li-divider)", "vclick", function( event ) {
|
.delegate( "li:not(.ui-disabled, .ui-li-divider)", "vclick", function( event ) {
|
||||||
|
|
||||||
// index of option tag to be selected
|
// index of option tag to be selected
|
||||||
var oldIndex = self.select[ 0 ].selectedIndex,
|
var oldIndex = self.select[ 0 ].selectedIndex,
|
||||||
newIndex = self.list.find( "li:not(.ui-li-divider)" ).index( this ),
|
newIndex = self.list.find( "li:not(.ui-li-divider)" ).index( this ),
|
||||||
option = widget.optionElems.eq( newIndex )[ 0 ];
|
option = widget.optionElems.eq( newIndex )[ 0 ];
|
||||||
|
|
||||||
// toggle selected status on the tag for multi selects
|
// toggle selected status on the tag for multi selects
|
||||||
option.selected = self.isMultiple ? !option.selected : true;
|
option.selected = self.isMultiple ? !option.selected : true;
|
||||||
|
|
||||||
// toggle checkbox class for multiple selects
|
// toggle checkbox class for multiple selects
|
||||||
if ( self.isMultiple ) {
|
if ( self.isMultiple ) {
|
||||||
$( this ).find( ".ui-icon" )
|
$( this ).find( ".ui-icon" )
|
||||||
.toggleClass( "ui-icon-checkbox-on", option.selected )
|
.toggleClass( "ui-icon-checkbox-on", option.selected )
|
||||||
.toggleClass( "ui-icon-checkbox-off", !option.selected );
|
.toggleClass( "ui-icon-checkbox-off", !option.selected );
|
||||||
}
|
}
|
||||||
|
|
||||||
// trigger change if value changed
|
// trigger change if value changed
|
||||||
if ( self.isMultiple || oldIndex !== newIndex ) {
|
if ( self.isMultiple || oldIndex !== newIndex ) {
|
||||||
self.select.trigger( "change" );
|
self.select.trigger( "change" );
|
||||||
}
|
}
|
||||||
|
|
||||||
//hide custom select for single selects only
|
//hide custom select for single selects only
|
||||||
if ( !self.isMultiple ) {
|
if ( !self.isMultiple ) {
|
||||||
widget.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
})
|
|
||||||
.keydown(function( event ) { //keyboard events for menu items
|
|
||||||
var target = $( event.target ),
|
|
||||||
li = target.closest( "li" ),
|
|
||||||
prev, next;
|
|
||||||
|
|
||||||
// switch logic based on which key was pressed
|
|
||||||
switch ( event.keyCode ) {
|
|
||||||
// up or left arrow keys
|
|
||||||
case 38:
|
|
||||||
prev = li.prev();
|
|
||||||
|
|
||||||
// if there's a previous option, focus it
|
|
||||||
if ( prev.length ) {
|
|
||||||
target
|
|
||||||
.blur()
|
|
||||||
.attr( "tabindex", "-1" );
|
|
||||||
|
|
||||||
prev.find( "a" ).first().focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// down or right arrow keys
|
|
||||||
case 40:
|
|
||||||
next = li.next();
|
|
||||||
|
|
||||||
// if there's a next option, focus it
|
|
||||||
if ( next.length ) {
|
|
||||||
target
|
|
||||||
.blur()
|
|
||||||
.attr( "tabindex", "-1" );
|
|
||||||
|
|
||||||
next.find( "a" ).first().focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// If enter or space is pressed, trigger click
|
|
||||||
case 13:
|
|
||||||
case 32:
|
|
||||||
target.trigger( "vclick" );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// button refocus ensures proper height calculation
|
|
||||||
// by removing the inline style and ensuring page inclusion
|
|
||||||
self.menuPage.bind( "pagehide", function() {
|
|
||||||
self.list.appendTo( self.listbox );
|
|
||||||
self._focusButton();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Events on "screen" overlay
|
|
||||||
self.screen.bind( "vclick", function( event ) {
|
|
||||||
widget.close();
|
widget.close();
|
||||||
});
|
|
||||||
|
|
||||||
// Close button on small overlays
|
|
||||||
self.headerClose.click( function() {
|
|
||||||
if ( self.menuType == "overlay" ) {
|
|
||||||
widget.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh: function( forceRebuild ){
|
|
||||||
var self = this,
|
|
||||||
select = this.element,
|
|
||||||
isMultiple = this.isMultiple,
|
|
||||||
options = this.optionElems = select.find( "option" ),
|
|
||||||
selected = this.selected(),
|
|
||||||
// return an array of all selected index's
|
|
||||||
indicies = this.selectedIndices();
|
|
||||||
|
|
||||||
if ( forceRebuild || select[0].options.length != self.list.find( "li" ).length ) {
|
|
||||||
self._buildList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.setButtonText();
|
event.preventDefault();
|
||||||
self.setButtonCount();
|
})
|
||||||
|
.keydown(function( event ) { //keyboard events for menu items
|
||||||
|
var target = $( event.target ),
|
||||||
|
li = target.closest( "li" ),
|
||||||
|
prev, next;
|
||||||
|
|
||||||
self.list.find( "li:not(.ui-li-divider)" )
|
// switch logic based on which key was pressed
|
||||||
.removeClass( $.mobile.activeBtnClass )
|
switch ( event.keyCode ) {
|
||||||
.attr( "aria-selected", false )
|
// up or left arrow keys
|
||||||
.each(function( i ) {
|
case 38:
|
||||||
|
prev = li.prev();
|
||||||
|
|
||||||
if ( $.inArray( i, indicies ) > -1 ) {
|
// if there's a previous option, focus it
|
||||||
var item = $( this ).addClass( $.mobile.activeBtnClass );
|
if ( prev.length ) {
|
||||||
|
target
|
||||||
|
.blur()
|
||||||
|
.attr( "tabindex", "-1" );
|
||||||
|
|
||||||
// Aria selected attr
|
prev.find( "a" ).first().focus();
|
||||||
item.find( "a" ).attr( "aria-selected", true );
|
|
||||||
|
|
||||||
// Multiple selects: add the "on" checkbox state to the icon
|
|
||||||
if ( self.isMultiple ) {
|
|
||||||
item.find( ".ui-icon" ).removeClass( "ui-icon-checkbox-off" ).addClass( "ui-icon-checkbox-on" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
close: function() {
|
|
||||||
if ( this.options.disabled || !this.isOpen ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
if ( self.menuType == "page" ) {
|
|
||||||
// rebind the page remove that was unbound in the open function
|
|
||||||
// to allow for the parent page removal from actions other than the use
|
|
||||||
// of a dialog sized custom select
|
|
||||||
self.thisPage.bind( "pagehide.remove", function() {
|
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
// doesn't solve the possible issue with calling change page
|
|
||||||
// where the objects don't define data urls which prevents dialog key
|
|
||||||
// stripping - changePage has incoming refactor
|
|
||||||
window.history.back();
|
|
||||||
} else {
|
|
||||||
self.screen.addClass( "ui-screen-hidden" );
|
|
||||||
self.listbox.addClass( "ui-selectmenu-hidden" ).removeAttr( "style" ).removeClass( "in" );
|
|
||||||
self.list.appendTo( self.listbox );
|
|
||||||
self._focusButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow the dialog to be closed again
|
|
||||||
this.isOpen = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
open: function() {
|
|
||||||
if ( this.options.disabled ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var self = this,
|
|
||||||
menuHeight = self.list.parent().outerHeight(),
|
|
||||||
menuWidth = self.list.parent().outerWidth(),
|
|
||||||
scrollTop = $( window ).scrollTop(),
|
|
||||||
btnOffset = self.button.offset().top,
|
|
||||||
screenHeight = window.innerHeight,
|
|
||||||
screenWidth = window.innerWidth;
|
|
||||||
|
|
||||||
//add active class to button
|
|
||||||
self.button.addClass( $.mobile.activeBtnClass );
|
|
||||||
|
|
||||||
//remove after delay
|
|
||||||
setTimeout( function() {
|
|
||||||
self.button.removeClass( $.mobile.activeBtnClass );
|
|
||||||
}, 300);
|
|
||||||
|
|
||||||
function focusMenuItem() {
|
|
||||||
self.list.find( ".ui-btn-active" ).focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( menuHeight > screenHeight - 80 || !$.support.scrollTop ) {
|
|
||||||
// prevent the parent page from being removed from the DOM,
|
|
||||||
// otherwise the results of selecting a list item in the dialog
|
|
||||||
// fall into a black hole
|
|
||||||
self.thisPage.unbind( "pagehide.remove" );
|
|
||||||
|
|
||||||
//for webos (set lastscroll using button offset)
|
|
||||||
if ( scrollTop == 0 && btnOffset > screenHeight ) {
|
|
||||||
self.thisPage.one( "pagehide", function() {
|
|
||||||
$( this ).jqmData( "lastScroll", btnOffset );
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.menuPage.one( "pageshow", function() {
|
return false;
|
||||||
// silentScroll() is called whenever a page is shown to restore
|
break;
|
||||||
// any previous scroll position the page may have had. We need to
|
|
||||||
// wait for the "silentscroll" event before setting focus to avoid
|
|
||||||
// the browser"s "feature" which offsets rendering to make sure
|
|
||||||
// whatever has focus is in view.
|
|
||||||
$( window ).one( "silentscroll", function() {
|
|
||||||
focusMenuItem();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.isOpen = true;
|
// down or right arrow keys
|
||||||
});
|
case 40:
|
||||||
|
next = li.next();
|
||||||
|
|
||||||
self.menuType = "page";
|
// if there's a next option, focus it
|
||||||
self.menuPageContent.append( self.list );
|
if ( next.length ) {
|
||||||
$.mobile.changePage( self.menuPage, {
|
target
|
||||||
transition: $.mobile.defaultDialogTransition
|
.blur()
|
||||||
});
|
.attr( "tabindex", "-1" );
|
||||||
} else {
|
|
||||||
self.menuType = "overlay";
|
|
||||||
|
|
||||||
self.screen.height( $(document).height() )
|
next.find( "a" ).first().focus();
|
||||||
.removeClass( "ui-screen-hidden" );
|
|
||||||
|
|
||||||
// Try and center the overlay over the button
|
|
||||||
var roomtop = btnOffset - scrollTop,
|
|
||||||
roombot = scrollTop + screenHeight - btnOffset,
|
|
||||||
halfheight = menuHeight / 2,
|
|
||||||
maxwidth = parseFloat( self.list.parent().css( "max-width" ) ),
|
|
||||||
newtop, newleft;
|
|
||||||
|
|
||||||
if ( roomtop > menuHeight / 2 && roombot > menuHeight / 2 ) {
|
|
||||||
newtop = btnOffset + ( self.button.outerHeight() / 2 ) - halfheight;
|
|
||||||
} else {
|
|
||||||
// 30px tolerance off the edges
|
|
||||||
newtop = roomtop > roombot ? scrollTop + screenHeight - menuHeight - 30 : scrollTop + 30;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the menuwidth is smaller than the screen center is
|
return false;
|
||||||
if ( menuWidth < maxwidth ) {
|
break;
|
||||||
newleft = ( screenWidth - menuWidth ) / 2;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
//otherwise insure a >= 30px offset from the left
|
// If enter or space is pressed, trigger click
|
||||||
newleft = self.button.offset().left + self.button.outerWidth() / 2 - menuWidth / 2;
|
case 13:
|
||||||
|
case 32:
|
||||||
|
target.trigger( "vclick" );
|
||||||
|
|
||||||
// 30px tolerance off the edges
|
return false;
|
||||||
if ( newleft < 30 ) {
|
break;
|
||||||
newleft = 30;
|
|
||||||
} else if ( (newleft + menuWidth) > screenWidth ) {
|
|
||||||
newleft = screenWidth - menuWidth - 30;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.listbox.append( self.list )
|
|
||||||
.removeClass( "ui-selectmenu-hidden" )
|
|
||||||
.css({
|
|
||||||
top: newtop,
|
|
||||||
left: newleft
|
|
||||||
})
|
|
||||||
.addClass( "in" );
|
|
||||||
|
|
||||||
focusMenuItem();
|
|
||||||
|
|
||||||
// duplicate with value set in page show for dialog sized selects
|
|
||||||
self.isOpen = true;
|
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
|
|
||||||
_buildList: function() {
|
// button refocus ensures proper height calculation
|
||||||
var self = this,
|
// by removing the inline style and ensuring page inclusion
|
||||||
o = this.options,
|
self.menuPage.bind( "pagehide", function() {
|
||||||
placeholder = this.placeholder,
|
self.list.appendTo( self.listbox );
|
||||||
optgroups = [],
|
self._focusButton();
|
||||||
lis = [],
|
});
|
||||||
dataIcon = self.isMultiple ? "checkbox-off" : "false";
|
|
||||||
|
|
||||||
self.list.empty().filter( ".ui-listview" ).listview( "destroy" );
|
// Events on "screen" overlay
|
||||||
|
self.screen.bind( "vclick", function( event ) {
|
||||||
|
widget.close();
|
||||||
|
});
|
||||||
|
|
||||||
// Populate menu with options from select element
|
// Close button on small overlays
|
||||||
self.select.find( "option" ).each( function( i ) {
|
self.headerClose.click( function() {
|
||||||
var $this = $( this ),
|
if ( self.menuType == "overlay" ) {
|
||||||
$parent = $this.parent(),
|
widget.close();
|
||||||
text = $this.text(),
|
return false;
|
||||||
anchor = "<a href='#'>"+ text +"</a>",
|
|
||||||
classes = [],
|
|
||||||
extraAttrs = [];
|
|
||||||
|
|
||||||
// Are we inside an optgroup?
|
|
||||||
if ( $parent.is( "optgroup" ) ) {
|
|
||||||
var optLabel = $parent.attr( "label" );
|
|
||||||
|
|
||||||
// has this optgroup already been built yet?
|
|
||||||
if ( $.inArray( optLabel, optgroups ) === -1 ) {
|
|
||||||
lis.push( "<li data-" + $.mobile.ns + "role='list-divider'>"+ optLabel +"</li>" );
|
|
||||||
optgroups.push( optLabel );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find placeholder text
|
|
||||||
// TODO: Are you sure you want to use getAttribute? ^RW
|
|
||||||
if ( !this.getAttribute( "value" ) || text.length == 0 || $this.jqmData( "placeholder" ) ) {
|
|
||||||
if ( o.hidePlaceholderMenuItems ) {
|
|
||||||
classes.push( "ui-selectmenu-placeholder" );
|
|
||||||
}
|
|
||||||
placeholder = self.placeholder = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// support disabled option tags
|
|
||||||
if ( this.disabled ) {
|
|
||||||
classes.push( "ui-disabled" );
|
|
||||||
extraAttrs.push( "aria-disabled='true'" );
|
|
||||||
}
|
|
||||||
|
|
||||||
lis.push( "<li data-" + $.mobile.ns + "option-index='" + i + "' data-" + $.mobile.ns + "icon='"+ dataIcon +"' class='"+ classes.join(" ") + "' " + extraAttrs.join(" ") +">"+ anchor +"</li>" );
|
|
||||||
});
|
|
||||||
|
|
||||||
self.list.html( lis.join(" ") );
|
|
||||||
|
|
||||||
self.list.find( "li" )
|
|
||||||
.attr({ "role": "option", "tabindex": "-1" })
|
|
||||||
.first().attr( "tabindex", "0" );
|
|
||||||
|
|
||||||
// Hide header close link for single selects
|
|
||||||
if ( !this.isMultiple ) {
|
|
||||||
this.headerClose.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide header if it's not a multiselect and there's no placeholder
|
|
||||||
if ( !this.isMultiple && !placeholder.length ) {
|
|
||||||
this.header.hide();
|
|
||||||
} else {
|
|
||||||
this.headerTitle.text( this.placeholder );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now populated, create listview
|
|
||||||
self.list.listview();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_create: function() {
|
refresh: function( forceRebuild ){
|
||||||
var self = this,
|
var self = this,
|
||||||
|
select = this.element,
|
||||||
|
isMultiple = this.isMultiple,
|
||||||
|
options = this.optionElems = select.find( "option" ),
|
||||||
|
selected = this.selected(),
|
||||||
|
// return an array of all selected index's
|
||||||
|
indicies = this.selectedIndices();
|
||||||
|
|
||||||
o = this.options,
|
if ( forceRebuild || select[0].options.length != self.list.find( "li" ).length ) {
|
||||||
|
self._buildList();
|
||||||
|
}
|
||||||
|
|
||||||
menu = this._custom();
|
self.setButtonText();
|
||||||
|
self.setButtonCount();
|
||||||
|
|
||||||
// Expose to other methods
|
self.list.find( "li:not(.ui-li-divider)" )
|
||||||
$.extend( self, menu );
|
.removeClass( $.mobile.activeBtnClass )
|
||||||
|
.attr( "aria-selected", false )
|
||||||
|
.each(function( i ) {
|
||||||
|
|
||||||
|
if ( $.inArray( i, indicies ) > -1 ) {
|
||||||
|
var item = $( this ).addClass( $.mobile.activeBtnClass );
|
||||||
|
|
||||||
|
// Aria selected attr
|
||||||
|
item.find( "a" ).attr( "aria-selected", true );
|
||||||
|
|
||||||
|
// Multiple selects: add the "on" checkbox state to the icon
|
||||||
|
if ( self.isMultiple ) {
|
||||||
|
item.find( ".ui-icon" ).removeClass( "ui-icon-checkbox-off" ).addClass( "ui-icon-checkbox-on" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
close: function() {
|
||||||
|
if ( this.options.disabled || !this.isOpen ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if ( self.menuType == "page" ) {
|
||||||
|
// rebind the page remove that was unbound in the open function
|
||||||
|
// to allow for the parent page removal from actions other than the use
|
||||||
|
// of a dialog sized custom select
|
||||||
|
self.thisPage.bind( "pagehide.remove", function() {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// doesn't solve the possible issue with calling change page
|
||||||
|
// where the objects don't define data urls which prevents dialog key
|
||||||
|
// stripping - changePage has incoming refactor
|
||||||
|
window.history.back();
|
||||||
|
} else {
|
||||||
|
self.screen.addClass( "ui-screen-hidden" );
|
||||||
|
self.listbox.addClass( "ui-selectmenu-hidden" ).removeAttr( "style" ).removeClass( "in" );
|
||||||
|
self.list.appendTo( self.listbox );
|
||||||
|
self._focusButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow the dialog to be closed again
|
||||||
|
this.isOpen = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
open: function() {
|
||||||
|
if ( this.options.disabled ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this,
|
||||||
|
menuHeight = self.list.parent().outerHeight(),
|
||||||
|
menuWidth = self.list.parent().outerWidth(),
|
||||||
|
scrollTop = $( window ).scrollTop(),
|
||||||
|
btnOffset = self.button.offset().top,
|
||||||
|
screenHeight = window.innerHeight,
|
||||||
|
screenWidth = window.innerWidth;
|
||||||
|
|
||||||
|
//add active class to button
|
||||||
|
self.button.addClass( $.mobile.activeBtnClass );
|
||||||
|
|
||||||
|
//remove after delay
|
||||||
|
setTimeout( function() {
|
||||||
|
self.button.removeClass( $.mobile.activeBtnClass );
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
function focusMenuItem() {
|
||||||
|
self.list.find( ".ui-btn-active" ).focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( menuHeight > screenHeight - 80 || !$.support.scrollTop ) {
|
||||||
|
// prevent the parent page from being removed from the DOM,
|
||||||
|
// otherwise the results of selecting a list item in the dialog
|
||||||
|
// fall into a black hole
|
||||||
|
self.thisPage.unbind( "pagehide.remove" );
|
||||||
|
|
||||||
|
//for webos (set lastscroll using button offset)
|
||||||
|
if ( scrollTop == 0 && btnOffset > screenHeight ) {
|
||||||
|
self.thisPage.one( "pagehide", function() {
|
||||||
|
$( this ).jqmData( "lastScroll", btnOffset );
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.menuPage.one( "pageshow", function() {
|
||||||
|
// silentScroll() is called whenever a page is shown to restore
|
||||||
|
// any previous scroll position the page may have had. We need to
|
||||||
|
// wait for the "silentscroll" event before setting focus to avoid
|
||||||
|
// the browser"s "feature" which offsets rendering to make sure
|
||||||
|
// whatever has focus is in view.
|
||||||
|
$( window ).one( "silentscroll", function() {
|
||||||
|
focusMenuItem();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.isOpen = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
self.menuType = "page";
|
||||||
|
self.menuPageContent.append( self.list );
|
||||||
|
$.mobile.changePage( self.menuPage, {
|
||||||
|
transition: $.mobile.defaultDialogTransition
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.menuType = "overlay";
|
||||||
|
|
||||||
|
self.screen.height( $(document).height() )
|
||||||
|
.removeClass( "ui-screen-hidden" );
|
||||||
|
|
||||||
|
// Try and center the overlay over the button
|
||||||
|
var roomtop = btnOffset - scrollTop,
|
||||||
|
roombot = scrollTop + screenHeight - btnOffset,
|
||||||
|
halfheight = menuHeight / 2,
|
||||||
|
maxwidth = parseFloat( self.list.parent().css( "max-width" ) ),
|
||||||
|
newtop, newleft;
|
||||||
|
|
||||||
|
if ( roomtop > menuHeight / 2 && roombot > menuHeight / 2 ) {
|
||||||
|
newtop = btnOffset + ( self.button.outerHeight() / 2 ) - halfheight;
|
||||||
|
} else {
|
||||||
|
// 30px tolerance off the edges
|
||||||
|
newtop = roomtop > roombot ? scrollTop + screenHeight - menuHeight - 30 : scrollTop + 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the menuwidth is smaller than the screen center is
|
||||||
|
if ( menuWidth < maxwidth ) {
|
||||||
|
newleft = ( screenWidth - menuWidth ) / 2;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
//otherwise insure a >= 30px offset from the left
|
||||||
|
newleft = self.button.offset().left + self.button.outerWidth() / 2 - menuWidth / 2;
|
||||||
|
|
||||||
|
// 30px tolerance off the edges
|
||||||
|
if ( newleft < 30 ) {
|
||||||
|
newleft = 30;
|
||||||
|
} else if ( (newleft + menuWidth) > screenWidth ) {
|
||||||
|
newleft = screenWidth - menuWidth - 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.listbox.append( self.list )
|
||||||
|
.removeClass( "ui-selectmenu-hidden" )
|
||||||
|
.css({
|
||||||
|
top: newtop,
|
||||||
|
left: newleft
|
||||||
|
})
|
||||||
|
.addClass( "in" );
|
||||||
|
|
||||||
|
focusMenuItem();
|
||||||
|
|
||||||
|
// duplicate with value set in page show for dialog sized selects
|
||||||
|
self.isOpen = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_buildList: function() {
|
||||||
|
var self = this,
|
||||||
|
o = this.options,
|
||||||
|
placeholder = this.placeholder,
|
||||||
|
optgroups = [],
|
||||||
|
lis = [],
|
||||||
|
dataIcon = self.isMultiple ? "checkbox-off" : "false";
|
||||||
|
|
||||||
|
self.list.empty().filter( ".ui-listview" ).listview( "destroy" );
|
||||||
|
|
||||||
|
// Populate menu with options from select element
|
||||||
|
self.select.find( "option" ).each( function( i ) {
|
||||||
|
var $this = $( this ),
|
||||||
|
$parent = $this.parent(),
|
||||||
|
text = $this.text(),
|
||||||
|
anchor = "<a href='#'>"+ text +"</a>",
|
||||||
|
classes = [],
|
||||||
|
extraAttrs = [];
|
||||||
|
|
||||||
|
// Are we inside an optgroup?
|
||||||
|
if ( $parent.is( "optgroup" ) ) {
|
||||||
|
var optLabel = $parent.attr( "label" );
|
||||||
|
|
||||||
|
// has this optgroup already been built yet?
|
||||||
|
if ( $.inArray( optLabel, optgroups ) === -1 ) {
|
||||||
|
lis.push( "<li data-" + $.mobile.ns + "role='list-divider'>"+ optLabel +"</li>" );
|
||||||
|
optgroups.push( optLabel );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find placeholder text
|
||||||
|
// TODO: Are you sure you want to use getAttribute? ^RW
|
||||||
|
if ( !this.getAttribute( "value" ) || text.length == 0 || $this.jqmData( "placeholder" ) ) {
|
||||||
|
if ( o.hidePlaceholderMenuItems ) {
|
||||||
|
classes.push( "ui-selectmenu-placeholder" );
|
||||||
|
}
|
||||||
|
placeholder = self.placeholder = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// support disabled option tags
|
||||||
|
if ( this.disabled ) {
|
||||||
|
classes.push( "ui-disabled" );
|
||||||
|
extraAttrs.push( "aria-disabled='true'" );
|
||||||
|
}
|
||||||
|
|
||||||
|
lis.push( "<li data-" + $.mobile.ns + "option-index='" + i + "' data-" + $.mobile.ns + "icon='"+ dataIcon +"' class='"+ classes.join(" ") + "' " + extraAttrs.join(" ") +">"+ anchor +"</li>" );
|
||||||
|
});
|
||||||
|
|
||||||
|
self.list.html( lis.join(" ") );
|
||||||
|
|
||||||
|
self.list.find( "li" )
|
||||||
|
.attr({ "role": "option", "tabindex": "-1" })
|
||||||
|
.first().attr( "tabindex", "0" );
|
||||||
|
|
||||||
|
// Hide header close link for single selects
|
||||||
|
if ( !this.isMultiple ) {
|
||||||
|
this.headerClose.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide header if it's not a multiselect and there's no placeholder
|
||||||
|
if ( !this.isMultiple && !placeholder.length ) {
|
||||||
|
this.header.hide();
|
||||||
|
} else {
|
||||||
|
this.headerTitle.text( this.placeholder );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now populated, create listview
|
||||||
|
self.list.listview();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})( jQuery );
|
})( jQuery );
|
||||||
|
|
|
||||||
|
|
@ -98,5 +98,4 @@ $( document ).bind( "pagecreate create", function( e ){
|
||||||
.not( ":jqmData(role='none'), :jqmData(role='nojs')" )
|
.not( ":jqmData(role='none'), :jqmData(role='nojs')" )
|
||||||
.selectmenu();
|
.selectmenu();
|
||||||
});
|
});
|
||||||
|
|
||||||
})( jQuery );
|
})( jQuery );
|
||||||
|
|
|
||||||
|
|
@ -27,58 +27,47 @@ $.widget( "mobile.nativeselect", $.mobile.widget, {
|
||||||
|
|
||||||
_shared: $.mobile.selectShared,
|
_shared: $.mobile.selectShared,
|
||||||
|
|
||||||
_native: function() {
|
_create: function() {
|
||||||
var widget = this;
|
var widget = this;
|
||||||
|
|
||||||
return $.extend( this._shared(), {
|
$.extend( widget, widget._shared(), {
|
||||||
typgeName: 'native',
|
typgeName: 'native',
|
||||||
|
|
||||||
button: $( "<div/>" ),
|
button: $( "<div/>" )
|
||||||
|
|
||||||
build: function() {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.select
|
|
||||||
.appendTo( self.button )
|
|
||||||
.bind( "vmousedown", function() {
|
|
||||||
// Add active class to button
|
|
||||||
self.button.addClass( $.mobile.activeBtnClass );
|
|
||||||
})
|
|
||||||
.bind( "focus vmouseover", function() {
|
|
||||||
self.button.trigger( "vmouseover" );
|
|
||||||
})
|
|
||||||
.bind( "vmousemove", function() {
|
|
||||||
// Remove active class on scroll/touchmove
|
|
||||||
self.button.removeClass( $.mobile.activeBtnClass );
|
|
||||||
})
|
|
||||||
.bind( "change blur vmouseout", function() {
|
|
||||||
self.button.trigger( "vmouseout" )
|
|
||||||
.removeClass( $.mobile.activeBtnClass );
|
|
||||||
})
|
|
||||||
.bind( "change blur", function() {
|
|
||||||
self.button.removeClass( "ui-btn-down-" + widget.options.theme );
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh: function() {
|
|
||||||
var self = this,
|
|
||||||
selected = this.selected();
|
|
||||||
|
|
||||||
self.setButtonText();
|
|
||||||
self.setButtonCount();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_create: function() {
|
build: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.select
|
||||||
|
.appendTo( self.button )
|
||||||
|
.bind( "vmousedown", function() {
|
||||||
|
// Add active class to button
|
||||||
|
self.button.addClass( $.mobile.activeBtnClass );
|
||||||
|
})
|
||||||
|
.bind( "focus vmouseover", function() {
|
||||||
|
self.button.trigger( "vmouseover" );
|
||||||
|
})
|
||||||
|
.bind( "vmousemove", function() {
|
||||||
|
// Remove active class on scroll/touchmove
|
||||||
|
self.button.removeClass( $.mobile.activeBtnClass );
|
||||||
|
})
|
||||||
|
.bind( "change blur vmouseout", function() {
|
||||||
|
self.button.trigger( "vmouseout" )
|
||||||
|
.removeClass( $.mobile.activeBtnClass );
|
||||||
|
})
|
||||||
|
.bind( "change blur", function() {
|
||||||
|
self.button.removeClass( "ui-btn-down-" + self.options.theme );
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
refresh: function() {
|
||||||
var self = this,
|
var self = this,
|
||||||
|
selected = this.selected();
|
||||||
|
|
||||||
o = this.options,
|
self.setButtonText();
|
||||||
|
self.setButtonCount();
|
||||||
menu = this._native();
|
|
||||||
|
|
||||||
// Expose to other methods
|
|
||||||
$.extend( self, menu );
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})( jQuery );
|
})( jQuery );
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue