* jquery.ui.core.js
* jquery.ui.widget.js
*/
-(function($) {
+(function( $, undefined ) {
var currentEventTarget = null;
focus: null,
select: null
},
+
_create: function() {
this.activeMenu = this.element;
this.element
var target = $( event.target );
if ( target[0] !== currentEventTarget ) {
currentEventTarget = target[0];
- target.one( "click.menu", function( event ) {
+ // TODO: What are we trying to accomplish with this check?
+ // Clicking a menu item twice results in a select event with
+ // an empty ui.item.
+ target.one( "click" + this.eventNamespace, function( event ) {
currentEventTarget = null;
});
// Don't select disabled menu items
mouseleave: "collapseAll",
"mouseleave .ui-menu": "collapseAll",
focus: function( event ) {
- var menu = this.element,
- firstItem = menu.children( ".ui-menu-item" ).eq( 0 );
- if ( this._hasScroll() && !this.active ) {
+ var menuTop,
+ menu = this.element,
+ // Default to focusing the first item
+ item = menu.children( ".ui-menu-item" ).eq( 0 );
+
+ // If there's already an active item, keep it active
+ if ( this.active ) {
+ item = this.active;
+ // If there's no active item and the menu is scrolled,
+ // then find the first visible item
+ } else if ( this._hasScroll() ) {
+ menuTop = menu.offset().top;
menu.children().each(function() {
var currentItem = $( this );
- if ( currentItem.offset().top - menu.offset().top >= 0 ) {
- firstItem = currentItem;
+ if ( currentItem.offset().top - menuTop >= 0 ) {
+ item = currentItem;
return false;
}
});
- } else if ( this.active ) {
- firstItem = this.active;
}
- this.focus( event, firstItem );
+ this.focus( event, item );
},
blur: function( event ) {
this._delay(function() {
this.refresh();
- // TODO: We probably shouldn't bind to document for each menu.
+ // Clicks outside of a menu collapse any open menus
this._on( this.document, {
click: function( event ) {
if ( !$( event.target ).closest( ".ui-menu" ).length ) {
},
_destroy: function() {
- // destroy (sub)menus
+ // Destroy (sub)menus
this.element
.removeAttr( "aria-activedescendant" )
.find( ".ui-menu" ).andSelf()
.removeUniqueId()
.show();
- // destroy menu items
+ // Destroy menu items
this.element.find( ".ui-menu-item" )
.removeClass( "ui-menu-item" )
.removeAttr( "role" )
}
});
- // destroy menu dividers
+ // Destroy menu dividers
this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
- // unbind currentEventTarget click event handler
+ // Unbind currentEventTarget click event handler
this._off( $( currentEventTarget ), "click" );
},
}
break;
case $.ui.keyCode.ENTER:
- this._activate( event );
- break;
case $.ui.keyCode.SPACE:
this._activate( event );
break;
if ( !match.length ) {
character = String.fromCharCode( event.keyCode );
match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
- return new RegExp( "^" + escape(character), "i" )
+ return new RegExp( "^" + escape( character ), "i" )
.test( $( this ).children( "a" ).text() );
});
}
},
refresh: function() {
- // initialize nested menus
+ // Initialize nested menus
var menus,
submenus = this.element.find( this.options.menus + ":not(.ui-menu)" )
.addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
"aria-expanded": "false"
});
- // don't refresh list items that are already adapted
+ // Don't refresh list items that are already adapted
menus = submenus.add( this.element );
menus.children( ":not( .ui-menu-item ):has( a )" )
role: this._itemRole()
});
- // initialize unlinked menu-items containing spaces and/or dashes only as dividers
- menus.children( ":not(.ui-menu-item)" ).each( function() {
+ // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
+ menus.children( ":not(.ui-menu-item)" ).each(function() {
var item = $( this );
// hyphen, em dash, en dash
if ( !/[^\-—–\s]/.test( item.text() ) ) {
}
});
- // add aria-disabled attribute to any disabled menu item
+ // Add aria-disabled attribute to any disabled menu item
menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
submenus.each(function() {
var menu = $( this ),
item = menu.prev( "a" ),
- submenuCarat = $( '<span class="ui-menu-icon ui-icon ui-icon-carat-1-e"></span>' ).data( "ui-menu-submenu-carat", true );
+ submenuCarat = $( "<span>" )
+ .addClass( "ui-menu-icon ui-icon ui-icon-carat-1-e" )
+ .data( "ui-menu-submenu-carat", true );
item
.attr( "aria-haspopup", "true" )
this.active = item.first();
focused = this.active.children( "a" ).addClass( "ui-state-focus" );
- // only update aria-activedescendant if there's a role
+ // Only update aria-activedescendant if there's a role
// otherwise we assume focus is managed elsewhere
if ( this.options.role ) {
this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
}
- // highlight active parent menu item, if any
+ // Highlight active parent menu item, if any
this.active
.parent()
.closest( ".ui-menu-item" )
}, this.delay );
}
- nested = $( "> .ui-menu", item );
+ nested = item.children( ".ui-menu" );
if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
this._startOpening(nested);
}
);
clearTimeout( this.timer );
- this.element.find( ".ui-menu" ).not( submenu.parents() )
+ this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
.hide()
.attr( "aria-hidden", "true" );
collapseAll: function( event, all ) {
clearTimeout( this.timer );
this.timer = this._delay(function() {
- // if we were passed an event, look for the submenu that contains the event
+ // If we were passed an event, look for the submenu that contains the event
var currentMenu = all ? this.element :
$( event && event.target ).closest( this.element.find( ".ui-menu" ) );
- // if we found no valid submenu ancestor, use the main menu to close all sub menus anyway
+ // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
if ( !currentMenu.length ) {
currentMenu = this.element;
}
if ( newItem && newItem.length ) {
this._open( newItem.parent() );
- // timeout so Firefox will not hide activedescendant change in expanding submenu from AT
+ // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
this._delay(function() {
this.focus( event, newItem );
}, 20 );
},
nextPage: function( event ) {
+ var item, base, height;
+
if ( !this.active ) {
- this._move( "next", "first", event );
+ this.next( event );
return;
}
if ( this.isLastItem() ) {
return;
}
if ( this._hasScroll() ) {
- var base = this.active.offset().top,
- height = this.element.height(),
- result;
+ base = this.active.offset().top;
+ height = this.element.height();
this.active.nextAll( ".ui-menu-item" ).each(function() {
- result = $( this );
- return $( this ).offset().top - base - height < 0;
+ item = $( this );
+ return item.offset().top - base - height < 0;
});
- this.focus( event, result );
+ this.focus( event, item );
} else {
this.focus( event, this.activeMenu.children( ".ui-menu-item" )
[ !this.active ? "first" : "last" ]() );
},
previousPage: function( event ) {
+ var item, base, height;
if ( !this.active ) {
- this._move( "next", "first", event );
+ this.next( event );
return;
}
if ( this.isFirstItem() ) {
return;
}
if ( this._hasScroll() ) {
- var base = this.active.offset().top,
- height = this.element.height(),
- result;
+ base = this.active.offset().top;
+ height = this.element.height();
this.active.prevAll( ".ui-menu-item" ).each(function() {
- result = $( this );
- return $(this).offset().top - base + height > 0;
+ item = $( this );
+ return item.offset().top - base + height > 0;
});
- this.focus( event, result );
+ this.focus( event, item );
} else {
this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() );
}
},
select: function( event ) {
- // save active reference before collapseAll triggers blur
+ // Save active reference before collapseAll triggers blur
var ui = {
item: this.active
};