source: [view]
define("dijit/Menu", ["dojo", "dijit", "text!dijit/templates/Menu.html", "dojo/window", "dijit/_Widget", "dijit/_KeyNavContainer", "dijit/_Templated", "dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"], function(dojo, dijit) {
// "dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator" for Back-compat (TODO: remove in 2.0)
dojo.declare("dijit._MenuBase",
[dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
{
// summary:
// Base class for Menu and MenuBar
// parentMenu: [readonly] Widget
// pointer to menu that displayed me
parentMenu: null,
// popupDelay: Integer
// number of milliseconds before hovering (without clicking) causes the popup to automatically open.
popupDelay: 500,
startup: function(){
if(this._started){ return; }
dojo.forEach(this.getChildren(), function(child){ child.startup(); });
this.startupKeyNavChildren();
this.inherited(arguments);
},
onExecute: function(){
// summary:
// Attach point for notification about when a menu item has been executed.
// This is an internal mechanism used for Menus to signal to their parent to
// close them, because they are about to execute the onClick handler. In
// general developers should not attach to or override this method.
// tags:
// protected
},
onCancel: function(/*Boolean*/ closeAll){
// summary:
// Attach point for notification about when the user cancels the current menu
// This is an internal mechanism used for Menus to signal to their parent to
// close them. In general developers should not attach to or override this method.
// tags:
// protected
},
_moveToPopup: function(/*Event*/ evt){
// summary:
// This handles the right arrow key (left arrow key on RTL systems),
// which will either open a submenu, or move to the next item in the
// ancestor MenuBar
// tags:
// private
if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
this.focusedChild._onClick(evt);
}else{
var topMenu = this._getTopMenu();
if(topMenu && topMenu._isMenuBar){
topMenu.focusNext();
}
}
},
_onPopupHover: function(/*Event*/ evt){
// summary:
// This handler is called when the mouse moves over the popup.
// tags:
// private
// if the mouse hovers over a menu popup that is in pending-close state,
// then stop the close operation.
// This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
if(this.currentPopup && this.currentPopup._pendingClose_timer){
var parentMenu = this.currentPopup.parentMenu;
// highlight the parent menu item pointing to this popup
if(parentMenu.focusedChild){
parentMenu.focusedChild._setSelected(false);
}
parentMenu.focusedChild = this.currentPopup.from_item;
parentMenu.focusedChild._setSelected(true);
// cancel the pending close
this._stopPendingCloseTimer(this.currentPopup);
}
},
onItemHover: function(/*MenuItem*/ item){
// summary:
// Called when cursor is over a MenuItem.
// tags:
// protected
// Don't do anything unless user has "activated" the menu by:
// 1) clicking it
// 2) opening it from a parent menu (which automatically focuses it)
if(this.isActive){
this.focusChild(item);
if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
this.hover_timer = setTimeout(dojo.hitch(this, "_openPopup"), this.popupDelay);
}
}
// if the user is mixing mouse and keyboard navigation,
// then the menu may not be active but a menu item has focus,
// but it's not the item that the mouse just hovered over.
// To avoid both keyboard and mouse selections, use the latest.
if(this.focusedChild){
this.focusChild(item);
}
this._hoveredChild = item;
},
_onChildBlur: function(item){
// summary:
// Called when a child MenuItem becomes inactive because focus
// has been removed from the MenuItem *and* it's descendant menus.
// tags:
// private
this._stopPopupTimer();
item._setSelected(false);
// Close all popups that are open and descendants of this menu
var itemPopup = item.popup;
if(itemPopup){
this._stopPendingCloseTimer(itemPopup);
itemPopup._pendingClose_timer = setTimeout(function(){
itemPopup._pendingClose_timer = null;
if(itemPopup.parentMenu){
itemPopup.parentMenu.currentPopup = null;
}
dijit.popup.close(itemPopup); // this calls onClose
}, this.popupDelay);
}
},
onItemUnhover: function(/*MenuItem*/ item){
// summary:
// Callback fires when mouse exits a MenuItem
// tags:
// protected
if(this.isActive){
this._stopPopupTimer();
}
if(this._hoveredChild == item){ this._hoveredChild = null; }
},
_stopPopupTimer: function(){
// summary:
// Cancels the popup timer because the user has stop hovering
// on the MenuItem, etc.
// tags:
// private
if(this.hover_timer){
clearTimeout(this.hover_timer);
this.hover_timer = null;
}
},
_stopPendingCloseTimer: function(/*dijit._Widget*/ popup){
// summary:
// Cancels the pending-close timer because the close has been preempted
// tags:
// private
if(popup._pendingClose_timer){
clearTimeout(popup._pendingClose_timer);
popup._pendingClose_timer = null;
}
},
_stopFocusTimer: function(){
// summary:
// Cancels the pending-focus timer because the menu was closed before focus occured
// tags:
// private
if(this._focus_timer){
clearTimeout(this._focus_timer);
this._focus_timer = null;
}
},
_getTopMenu: function(){
// summary:
// Returns the top menu in this chain of Menus
// tags:
// private
for(var top=this; top.parentMenu; top=top.parentMenu);
return top;
},
onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
// summary:
// Handle clicks on an item.
// tags:
// private
// this can't be done in _onFocus since the _onFocus events occurs asynchronously
if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
this._markActive();
}
this.focusChild(item);
if(item.disabled){ return false; }
if(item.popup){
this._openPopup();
}else{
// before calling user defined handler, close hierarchy of menus
// and restore focus to place it was when menu was opened
this.onExecute();
// user defined handler for click
item.onClick(evt);
}