]> git.wh0rd.org - tt-rss.git/blobdiff - lib/dijit/Menu.js
upgrade Dojo to 1.6.1
[tt-rss.git] / lib / dijit / Menu.js
index 83a2ddbad7b67cacca128a2e7f4fb9d4c28bd237..c5a48660e3e4287dfa9bba6f2197e2ad89de7101 100644 (file)
 /*
-       Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
+       Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
        Available via Academic Free License >= 2.1 OR the modified BSD license.
        see: http://dojotoolkit.org/license for details
 */
 
 
-if(!dojo._hasResource["dijit.Menu"]){
-dojo._hasResource["dijit.Menu"]=true;
+if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dijit.Menu"] = true;
 dojo.provide("dijit.Menu");
 dojo.require("dojo.window");
 dojo.require("dijit._Widget");
 dojo.require("dijit._KeyNavContainer");
 dojo.require("dijit._Templated");
-dojo.declare("dijit._MenuBase",[dijit._Widget,dijit._Templated,dijit._KeyNavContainer],{parentMenu:null,popupDelay:500,startup:function(){
-if(this._started){
-return;
-}
-dojo.forEach(this.getChildren(),function(_1){
-_1.startup();
-});
-this.startupKeyNavChildren();
-this.inherited(arguments);
-},onExecute:function(){
-},onCancel:function(_2){
-},_moveToPopup:function(_3){
-if(this.focusedChild&&this.focusedChild.popup&&!this.focusedChild.disabled){
-this.focusedChild._onClick(_3);
-}else{
-var _4=this._getTopMenu();
-if(_4&&_4._isMenuBar){
-_4.focusNext();
-}
-}
-},_onPopupHover:function(_5){
-if(this.currentPopup&&this.currentPopup._pendingClose_timer){
-var _6=this.currentPopup.parentMenu;
-if(_6.focusedChild){
-_6.focusedChild._setSelected(false);
-}
-_6.focusedChild=this.currentPopup.from_item;
-_6.focusedChild._setSelected(true);
-this._stopPendingCloseTimer(this.currentPopup);
-}
-},onItemHover:function(_7){
-if(this.isActive){
-this.focusChild(_7);
-if(this.focusedChild.popup&&!this.focusedChild.disabled&&!this.hover_timer){
-this.hover_timer=setTimeout(dojo.hitch(this,"_openPopup"),this.popupDelay);
-}
-}
-if(this.focusedChild){
-this.focusChild(_7);
-}
-this._hoveredChild=_7;
-},_onChildBlur:function(_8){
-this._stopPopupTimer();
-_8._setSelected(false);
-var _9=_8.popup;
-if(_9){
-this._stopPendingCloseTimer(_9);
-_9._pendingClose_timer=setTimeout(function(){
-_9._pendingClose_timer=null;
-if(_9.parentMenu){
-_9.parentMenu.currentPopup=null;
-}
-dijit.popup.close(_9);
-},this.popupDelay);
-}
-},onItemUnhover:function(_a){
-if(this.isActive){
-this._stopPopupTimer();
-}
-if(this._hoveredChild==_a){
-this._hoveredChild=null;
-}
-},_stopPopupTimer:function(){
-if(this.hover_timer){
-clearTimeout(this.hover_timer);
-this.hover_timer=null;
-}
-},_stopPendingCloseTimer:function(_b){
-if(_b._pendingClose_timer){
-clearTimeout(_b._pendingClose_timer);
-_b._pendingClose_timer=null;
-}
-},_stopFocusTimer:function(){
-if(this._focus_timer){
-clearTimeout(this._focus_timer);
-this._focus_timer=null;
-}
-},_getTopMenu:function(){
-for(var _c=this;_c.parentMenu;_c=_c.parentMenu){
-}
-return _c;
-},onItemClick:function(_d,_e){
-if(typeof this.isShowingNow=="undefined"){
-this._markActive();
-}
-this.focusChild(_d);
-if(_d.disabled){
-return false;
-}
-if(_d.popup){
-this._openPopup();
-}else{
-this.onExecute();
-_d.onClick(_e);
-}
-},_openPopup:function(){
-this._stopPopupTimer();
-var _f=this.focusedChild;
-if(!_f){
-return;
-}
-var _10=_f.popup;
-if(_10.isShowingNow){
-return;
-}
-if(this.currentPopup){
-this._stopPendingCloseTimer(this.currentPopup);
-dijit.popup.close(this.currentPopup);
-}
-_10.parentMenu=this;
-_10.from_item=_f;
-var _11=this;
-dijit.popup.open({parent:this,popup:_10,around:_f.domNode,orient:this._orient||(this.isLeftToRight()?{"TR":"TL","TL":"TR","BR":"BL","BL":"BR"}:{"TL":"TR","TR":"TL","BL":"BR","BR":"BL"}),onCancel:function(){
-_11.focusChild(_f);
-_11._cleanUp();
-_f._setSelected(true);
-_11.focusedChild=_f;
-},onExecute:dojo.hitch(this,"_cleanUp")});
-this.currentPopup=_10;
-_10.connect(_10.domNode,"onmouseenter",dojo.hitch(_11,"_onPopupHover"));
-if(_10.focus){
-_10._focus_timer=setTimeout(dojo.hitch(_10,function(){
-this._focus_timer=null;
-this.focus();
-}),0);
-}
-},_markActive:function(){
-this.isActive=true;
-dojo.addClass(this.domNode,"dijitMenuActive");
-dojo.removeClass(this.domNode,"dijitMenuPassive");
-},onOpen:function(e){
-this.isShowingNow=true;
-this._markActive();
-},_markInactive:function(){
-this.isActive=false;
-dojo.removeClass(this.domNode,"dijitMenuActive");
-dojo.addClass(this.domNode,"dijitMenuPassive");
-},onClose:function(){
-this._stopFocusTimer();
-this._markInactive();
-this.isShowingNow=false;
-this.parentMenu=null;
-},_closeChild:function(){
-this._stopPopupTimer();
-if(this.focusedChild){
-this.focusedChild._setSelected(false);
-this.focusedChild._onUnhover();
-this.focusedChild=null;
-}
-if(this.currentPopup){
-dijit.popup.close(this.currentPopup);
-this.currentPopup=null;
-}
-},_onItemFocus:function(_12){
-if(this._hoveredChild&&this._hoveredChild!=_12){
-this._hoveredChild._onUnhover();
-}
-},_onBlur:function(){
-this._cleanUp();
-this.inherited(arguments);
-},_cleanUp:function(){
-this._closeChild();
-if(typeof this.isShowingNow=="undefined"){
-this._markInactive();
-}
-}});
-dojo.declare("dijit.Menu",dijit._MenuBase,{constructor:function(){
-this._bindings=[];
-},templateString:dojo.cache("dijit","templates/Menu.html","<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" waiRole=\"menu\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress:_onKeyPress\" cellspacing=0>\n\t<tbody class=\"dijitReset\" dojoAttachPoint=\"containerNode\"></tbody>\n</table>\n"),baseClass:"dijitMenu",targetNodeIds:[],contextMenuForWindow:false,leftClickToOpen:false,refocus:true,postCreate:function(){
-if(this.contextMenuForWindow){
-this.bindDomNode(dojo.body());
-}else{
-dojo.forEach(this.targetNodeIds,this.bindDomNode,this);
-}
-var k=dojo.keys,l=this.isLeftToRight();
-this._openSubMenuKey=l?k.RIGHT_ARROW:k.LEFT_ARROW;
-this._closeSubMenuKey=l?k.LEFT_ARROW:k.RIGHT_ARROW;
-this.connectKeyNavHandlers([k.UP_ARROW],[k.DOWN_ARROW]);
-},_onKeyPress:function(evt){
-if(evt.ctrlKey||evt.altKey){
-return;
-}
-switch(evt.charOrCode){
-case this._openSubMenuKey:
-this._moveToPopup(evt);
-dojo.stopEvent(evt);
-break;
-case this._closeSubMenuKey:
-if(this.parentMenu){
-if(this.parentMenu._isMenuBar){
-this.parentMenu.focusPrev();
-}else{
-this.onCancel(false);
-}
-}else{
-dojo.stopEvent(evt);
-}
-break;
-}
-},_iframeContentWindow:function(_13){
-var win=dojo.window.get(this._iframeContentDocument(_13))||this._iframeContentDocument(_13)["__parent__"]||(_13.name&&dojo.doc.frames[_13.name])||null;
-return win;
-},_iframeContentDocument:function(_14){
-var doc=_14.contentDocument||(_14.contentWindow&&_14.contentWindow.document)||(_14.name&&dojo.doc.frames[_14.name]&&dojo.doc.frames[_14.name].document)||null;
-return doc;
-},bindDomNode:function(_15){
-_15=dojo.byId(_15);
-var cn;
-if(_15.tagName.toLowerCase()=="iframe"){
-var _16=_15,win=this._iframeContentWindow(_16);
-cn=dojo.withGlobal(win,dojo.body);
-}else{
-cn=(_15==dojo.body()?dojo.doc.documentElement:_15);
-}
-var _17={node:_15,iframe:_16};
-dojo.attr(_15,"_dijitMenu"+this.id,this._bindings.push(_17));
-var _18=dojo.hitch(this,function(cn){
-return [dojo.connect(cn,this.leftClickToOpen?"onclick":"oncontextmenu",this,function(evt){
-dojo.stopEvent(evt);
-this._scheduleOpen(evt.target,_16,{x:evt.pageX,y:evt.pageY});
-}),dojo.connect(cn,"onkeydown",this,function(evt){
-if(evt.shiftKey&&evt.keyCode==dojo.keys.F10){
-dojo.stopEvent(evt);
-this._scheduleOpen(evt.target,_16);
-}
-})];
-});
-_17.connects=cn?_18(cn):[];
-if(_16){
-_17.onloadHandler=dojo.hitch(this,function(){
-var win=this._iframeContentWindow(_16);
-cn=dojo.withGlobal(win,dojo.body);
-_17.connects=_18(cn);
-});
-if(_16.addEventListener){
-_16.addEventListener("load",_17.onloadHandler,false);
-}else{
-_16.attachEvent("onload",_17.onloadHandler);
-}
-}
-},unBindDomNode:function(_19){
-var _1a;
-try{
-_1a=dojo.byId(_19);
-}
-catch(e){
-return;
-}
-var _1b="_dijitMenu"+this.id;
-if(_1a&&dojo.hasAttr(_1a,_1b)){
-var bid=dojo.attr(_1a,_1b)-1,b=this._bindings[bid];
-dojo.forEach(b.connects,dojo.disconnect);
-var _1c=b.iframe;
-if(_1c){
-if(_1c.removeEventListener){
-_1c.removeEventListener("load",b.onloadHandler,false);
-}else{
-_1c.detachEvent("onload",b.onloadHandler);
-}
-}
-dojo.removeAttr(_1a,_1b);
-delete this._bindings[bid];
-}
-},_scheduleOpen:function(_1d,_1e,_1f){
-if(!this._openTimer){
-this._openTimer=setTimeout(dojo.hitch(this,function(){
-delete this._openTimer;
-this._openMyself({target:_1d,iframe:_1e,coords:_1f});
-}),1);
-}
-},_openMyself:function(_20){
-var _21=_20.target,_22=_20.iframe,_23=_20.coords;
-if(_23){
-if(_22){
-var od=_21.ownerDocument,ifc=dojo.position(_22,true),win=this._iframeContentWindow(_22),_24=dojo.withGlobal(win,"_docScroll",dojo);
-var cs=dojo.getComputedStyle(_22),tp=dojo._toPixelValue,_25=(dojo.isIE&&dojo.isQuirks?0:tp(_22,cs.paddingLeft))+(dojo.isIE&&dojo.isQuirks?tp(_22,cs.borderLeftWidth):0),top=(dojo.isIE&&dojo.isQuirks?0:tp(_22,cs.paddingTop))+(dojo.isIE&&dojo.isQuirks?tp(_22,cs.borderTopWidth):0);
-_23.x+=ifc.x+_25-_24.x;
-_23.y+=ifc.y+top-_24.y;
-}
-}else{
-_23=dojo.position(_21,true);
-_23.x+=10;
-_23.y+=10;
-}
-var _26=this;
-var _27=dijit.getFocus(this);
-function _28(){
-if(_26.refocus){
-dijit.focus(_27);
-}
-dijit.popup.close(_26);
-};
-dijit.popup.open({popup:this,x:_23.x,y:_23.y,onExecute:_28,onCancel:_28,orient:this.isLeftToRight()?"L":"R"});
-this.focus();
-this._onBlur=function(){
-this.inherited("_onBlur",arguments);
-dijit.popup.close(this);
-};
-},uninitialize:function(){
-dojo.forEach(this._bindings,function(b){
-if(b){
-this.unBindDomNode(b.node);
-}
-},this);
-this.inherited(arguments);
-}});
 dojo.require("dijit.MenuItem");
 dojo.require("dijit.PopupMenuItem");
 dojo.require("dijit.CheckedMenuItem");
 dojo.require("dijit.MenuSeparator");
+
+
+// "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);
+               }
+       },
+
+       _openPopup: function(){
+               // summary:
+               //              Open the popup to the side of/underneath the current menu item
+               // tags:
+               //              protected
+
+               this._stopPopupTimer();
+               var from_item = this.focusedChild;
+               if(!from_item){ return; } // the focused child lost focus since the timer was started
+               var popup = from_item.popup;
+               if(popup.isShowingNow){ return; }
+               if(this.currentPopup){
+                       this._stopPendingCloseTimer(this.currentPopup);
+                       dijit.popup.close(this.currentPopup);
+               }
+               popup.parentMenu = this;
+               popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
+               var self = this;
+               dijit.popup.open({
+                       parent: this,
+                       popup: popup,
+                       around: from_item.domNode,
+                       orient: this._orient || (this.isLeftToRight() ?
+                                                                       {'TR': 'TL', 'TL': 'TR', 'BR': 'BL', 'BL': 'BR'} :
+                                                                       {'TL': 'TR', 'TR': 'TL', 'BL': 'BR', 'BR': 'BL'}),
+                       onCancel: function(){ // called when the child menu is canceled
+                               // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
+                               // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
+                               self.focusChild(from_item);     // put focus back on my node
+                               self._cleanUp();                        // close the submenu (be sure this is done _after_ focus is moved)
+                               from_item._setSelected(true); // oops, _cleanUp() deselected the item
+                               self.focusedChild = from_item;  // and unset focusedChild
+                       },
+                       onExecute: dojo.hitch(this, "_cleanUp")
+               });
+
+               this.currentPopup = popup;
+               // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
+               popup.connect(popup.domNode, "onmouseenter", dojo.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
+
+               if(popup.focus){
+                       // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
+                       // if the cursor happens to collide with the popup, it will generate an onmouseover event
+                       // even though the mouse wasn't moved.   Use a setTimeout() to call popup.focus so that
+                       // our focus() call overrides the onmouseover event, rather than vice-versa.  (#8742)
+                       popup._focus_timer = setTimeout(dojo.hitch(popup, function(){
+                               this._focus_timer = null;
+                               this.focus();
+                       }), 0);
+               }
+       },
+
+       _markActive: function(){
+               // summary:
+               //              Mark this menu's state as active.
+               //              Called when this Menu gets focus from:
+               //                      1) clicking it (mouse or via space/arrow key)
+               //                      2) being opened by a parent menu.
+               //              This is not called just from mouse hover.
+               //              Focusing a menu via TAB does NOT automatically set isActive
+               //              since TAB is a navigation operation and not a selection one.
+               //              For Windows apps, pressing the ALT key focuses the menubar
+               //              menus (similar to TAB navigation) but the menu is not active
+               //              (ie no dropdown) until an item is clicked.
+               this.isActive = true;
+               dojo.replaceClass(this.domNode, "dijitMenuActive", "dijitMenuPassive");
+       },
+
+       onOpen: function(/*Event*/ e){
+               // summary:
+               //              Callback when this menu is opened.
+               //              This is called by the popup manager as notification that the menu
+               //              was opened.
+               // tags:
+               //              private
+
+               this.isShowingNow = true;
+               this._markActive();
+       },
+
+       _markInactive: function(){
+               // summary:
+               //              Mark this menu's state as inactive.
+               this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
+               dojo.replaceClass(this.domNode, "dijitMenuPassive", "dijitMenuActive");
+       },
+
+       onClose: function(){
+               // summary:
+               //              Callback when this menu is closed.
+               //              This is called by the popup manager as notification that the menu
+               //              was closed.
+               // tags:
+               //              private
+
+               this._stopFocusTimer();
+               this._markInactive();
+               this.isShowingNow = false;
+               this.parentMenu = null;
+       },
+
+       _closeChild: function(){
+               // summary:
+               //              Called when submenu is clicked or focus is lost.  Close hierarchy of menus.
+               // tags:
+               //              private
+               this._stopPopupTimer();
+
+               var fromItem = this.focusedChild && this.focusedChild.from_item;
+
+               if(this.currentPopup){
+                       // If focus is on my child menu then move focus to me,
+                       // because IE doesn't like it when you display:none a node with focus
+                       if(dijit._curFocus && dojo.isDescendant(dijit._curFocus, this.currentPopup.domNode)){
+                               this.focusedChild.focusNode.focus();
+                       }
+                       // Close all popups that are open and descendants of this menu
+                       dijit.popup.close(this.currentPopup);
+                       this.currentPopup = null;
+               }
+
+               if(this.focusedChild){ // unhighlight the focused item
+                       this.focusedChild._setSelected(false);
+                       this.focusedChild._onUnhover();
+                       this.focusedChild = null;
+               }
+       },
+
+       _onItemFocus: function(/*MenuItem*/ item){
+               // summary:
+               //              Called when child of this Menu gets focus from:
+               //                      1) clicking it
+               //                      2) tabbing into it
+               //                      3) being opened by a parent menu.
+               //              This is not called just from mouse hover.
+               if(this._hoveredChild && this._hoveredChild != item){
+                       this._hoveredChild._onUnhover(); // any previous mouse movement is trumped by focus selection
+               }
+       },
+
+       _onBlur: function(){
+               // summary:
+               //              Called when focus is moved away from this Menu and it's submenus.
+               // tags:
+               //              protected
+               this._cleanUp();
+               this.inherited(arguments);
+       },
+
+       _cleanUp: function(){
+               // summary:
+               //              Called when the user is done with this menu.  Closes hierarchy of menus.
+               // tags:
+               //              private
+
+               this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
+               if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
+                       this._markInactive();
+               }
+       }
+});
+
+dojo.declare("dijit.Menu",
+       dijit._MenuBase,
+       {
+       // summary
+       //              A context menu you can assign to multiple elements
+
+       // TODO: most of the code in here is just for context menu (right-click menu)
+       // support.  In retrospect that should have been a separate class (dijit.ContextMenu).
+       // Split them for 2.0
+
+       constructor: function(){
+               this._bindings = [];
+       },
+
+       templateString: dojo.cache("dijit", "templates/Menu.html", "<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" dojoAttachPoint=\"containerNode\"></tbody>\n</table>\n"),
+
+       baseClass: "dijitMenu",
+
+       // targetNodeIds: [const] String[]
+       //              Array of dom node ids of nodes to attach to.
+       //              Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
+       targetNodeIds: [],
+
+       // contextMenuForWindow: [const] Boolean
+       //              If true, right clicking anywhere on the window will cause this context menu to open.
+       //              If false, must specify targetNodeIds.
+       contextMenuForWindow: false,
+
+       // leftClickToOpen: [const] Boolean
+       //              If true, menu will open on left click instead of right click, similiar to a file menu.
+       leftClickToOpen: false,
+
+       // refocus: Boolean
+       //              When this menu closes, re-focus the element which had focus before it was opened.
+       refocus: true,
+
+       postCreate: function(){
+               if(this.contextMenuForWindow){
+                       this.bindDomNode(dojo.body());
+               }else{
+                       // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
+                       // later set('targetNodeIds', ...) call.   There's also a problem that targetNodeIds[]
+                       // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
+                       dojo.forEach(this.targetNodeIds, this.bindDomNode, this);
+               }
+               var k = dojo.keys, l = this.isLeftToRight();
+               this._openSubMenuKey = l ? k.RIGHT_ARROW : k.LEFT_ARROW;
+               this._closeSubMenuKey = l ? k.LEFT_ARROW : k.RIGHT_ARROW;
+               this.connectKeyNavHandlers([k.UP_ARROW], [k.DOWN_ARROW]);
+       },
+
+       _onKeyPress: function(/*Event*/ evt){
+               // summary:
+               //              Handle keyboard based menu navigation.
+               // tags:
+               //              protected
+
+               if(evt.ctrlKey || evt.altKey){ return; }
+
+               switch(evt.charOrCode){
+                       case this._openSubMenuKey:
+                               this._moveToPopup(evt);
+                               dojo.stopEvent(evt);
+                               break;
+                       case this._closeSubMenuKey:
+                               if(this.parentMenu){
+                                       if(this.parentMenu._isMenuBar){
+                                               this.parentMenu.focusPrev();
+                                       }else{
+                                               this.onCancel(false);
+                                       }
+                               }else{
+                                       dojo.stopEvent(evt);
+                               }
+                               break;
+               }
+       },
+
+       // thanks burstlib!
+       _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
+               // summary:
+               //              Returns the window reference of the passed iframe
+               // tags:
+               //              private
+               var win = dojo.window.get(this._iframeContentDocument(iframe_el)) ||
+                       // Moz. TODO: is this available when defaultView isn't?
+                       this._iframeContentDocument(iframe_el)['__parent__'] ||
+                       (iframe_el.name && dojo.doc.frames[iframe_el.name]) || null;
+               return win;     //      Window
+       },
+
+       _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
+               // summary:
+               //              Returns a reference to the document object inside iframe_el
+               // tags:
+               //              protected
+               var doc = iframe_el.contentDocument // W3
+                       || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
+                       || (iframe_el.name && dojo.doc.frames[iframe_el.name] && dojo.doc.frames[iframe_el.name].document)
+                       || null;
+               return doc;     //      HTMLDocument
+       },
+
+       bindDomNode: function(/*String|DomNode*/ node){
+               // summary:
+               //              Attach menu to given node
+               node = dojo.byId(node);
+
+               var cn; // Connect node
+
+               // Support context menus on iframes.   Rather than binding to the iframe itself we need
+               // to bind to the <body> node inside the iframe.
+               if(node.tagName.toLowerCase() == "iframe"){
+                       var iframe = node,
+                               win = this._iframeContentWindow(iframe);
+                       cn = dojo.withGlobal(win, dojo.body);
+               }else{
+                       
+                       // To capture these events at the top level, attach to <html>, not <body>.
+                       // Otherwise right-click context menu just doesn't work.
+                       cn = (node == dojo.body() ? dojo.doc.documentElement : node);
+               }
+
+
+               // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
+               var binding = {
+                       node: node,
+                       iframe: iframe
+               };
+
+               // Save info about binding in _bindings[], and make node itself record index(+1) into
+               // _bindings[] array.   Prefix w/_dijitMenu to avoid setting an attribute that may
+               // start with a number, which fails on FF/safari.
+               dojo.attr(node, "_dijitMenu" + this.id, this._bindings.push(binding));
+
+               // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
+               // loading yet, in which case we need to wait for the onload event first, and then connect
+               // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
+               // we need to monitor keyboard events in addition to the oncontextmenu event.
+               var doConnects = dojo.hitch(this, function(cn){
+                       return [
+                               // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
+                               // rather than shift-F10?
+                               dojo.connect(cn, this.leftClickToOpen ? "onclick" : "oncontextmenu", this, function(evt){
+                                       // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
+                                       dojo.stopEvent(evt);
+                                       this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY});
+                               }),
+                               dojo.connect(cn, "onkeydown", this, function(evt){
+                                       if(evt.shiftKey && evt.keyCode == dojo.keys.F10){
+                                               dojo.stopEvent(evt);
+                                               this._scheduleOpen(evt.target, iframe); // no coords - open near target node
+                                       }
+                               })
+                       ];
+               });
+               binding.connects = cn ? doConnects(cn) : [];
+
+               if(iframe){
+                       // Setup handler to [re]bind to the iframe when the contents are initially loaded,
+                       // and every time the contents change.
+                       // Need to do this b/c we are actually binding to the iframe's <body> node.
+                       // Note: can't use dojo.connect(), see #9609.
+
+                       binding.onloadHandler = dojo.hitch(this, function(){
+                               // want to remove old connections, but IE throws exceptions when trying to
+                               // access the <body> node because it's already gone, or at least in a state of limbo
+
+                               var win = this._iframeContentWindow(iframe);
+                                       cn = dojo.withGlobal(win, dojo.body);
+                               binding.connects = doConnects(cn);
+                       });
+                       if(iframe.addEventListener){
+                               iframe.addEventListener("load", binding.onloadHandler, false);
+                       }else{
+                               iframe.attachEvent("onload", binding.onloadHandler);
+                       }
+               }
+       },
+
+       unBindDomNode: function(/*String|DomNode*/ nodeName){
+               // summary:
+               //              Detach menu from given node
+
+               var node;
+               try{
+                       node = dojo.byId(nodeName);
+               }catch(e){
+                       // On IE the dojo.byId() call will get an exception if the attach point was
+                       // the <body> node of an <iframe> that has since been reloaded (and thus the
+                       // <body> node is in a limbo state of destruction.
+                       return;
+               }
+
+               // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
+               var attrName = "_dijitMenu" + this.id;
+               if(node && dojo.hasAttr(node, attrName)){
+                       var bid = dojo.attr(node, attrName)-1, b = this._bindings[bid];
+                       dojo.forEach(b.connects, dojo.disconnect);
+
+                       // Remove listener for iframe onload events
+                       var iframe = b.iframe;
+                       if(iframe){
+                               if(iframe.removeEventListener){
+                                       iframe.removeEventListener("load", b.onloadHandler, false);
+                               }else{
+                                       iframe.detachEvent("onload", b.onloadHandler);
+                               }
+                       }
+
+                       dojo.removeAttr(node, attrName);
+                       delete this._bindings[bid];
+               }
+       },
+
+       _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
+               // summary:
+               //              Set timer to display myself.  Using a timer rather than displaying immediately solves
+               //              two problems:
+               //
+               //              1. IE: without the delay, focus work in "open" causes the system
+               //              context menu to appear in spite of stopEvent.
+               //
+               //              2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
+               //              even after a dojo.stopEvent(e).  (Shift-F10 on windows doesn't generate the
+               //              oncontextmenu event.)
+
+               if(!this._openTimer){
+                       this._openTimer = setTimeout(dojo.hitch(this, function(){
+                               delete this._openTimer;
+                               this._openMyself({
+                                       target: target,
+                                       iframe: iframe,
+                                       coords: coords
+                               });
+                       }), 1);
+               }
+       },
+
+       _openMyself: function(args){
+               // summary:
+               //              Internal function for opening myself when the user does a right-click or something similar.
+               // args:
+               //              This is an Object containing:
+               //              * target:
+               //                      The node that is being clicked
+               //              * iframe:
+               //                      If an <iframe> is being clicked, iframe points to that iframe
+               //              * coords:
+               //                      Put menu at specified x/y position in viewport, or if iframe is
+               //                      specified, then relative to iframe.
+               //
+               //              _openMyself() formerly took the event object, and since various code references
+               //              evt.target (after connecting to _openMyself()), using an Object for parameters
+               //              (so that old code still works).
+
+               var target = args.target,
+                       iframe = args.iframe,
+                       coords = args.coords;
+
+               // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
+               // then near the node the menu is assigned to.
+               if(coords){
+                       if(iframe){
+                               // Specified coordinates are on <body> node of an <iframe>, convert to match main document
+                               var od = target.ownerDocument,
+                                       ifc = dojo.position(iframe, true),
+                                       win = this._iframeContentWindow(iframe),
+                                       scroll = dojo.withGlobal(win, "_docScroll", dojo);
+       
+                               var cs = dojo.getComputedStyle(iframe),
+                                       tp = dojo._toPixelValue,
+                                       left = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingLeft)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderLeftWidth) : 0),
+                                       top = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingTop)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderTopWidth) : 0);
+
+                               coords.x += ifc.x + left - scroll.x;
+                               coords.y += ifc.y + top - scroll.y;
+                       }
+               }else{
+                       coords = dojo.position(target, true);
+                       coords.x += 10;
+                       coords.y += 10;
+               }
+
+               var self=this;
+               var savedFocus = dijit.getFocus(this);
+               function closeAndRestoreFocus(){
+                       // user has clicked on a menu or popup
+                       if(self.refocus){
+                               dijit.focus(savedFocus);
+                       }
+                       dijit.popup.close(self);
+               }
+               dijit.popup.open({
+                       popup: this,
+                       x: coords.x,
+                       y: coords.y,
+                       onExecute: closeAndRestoreFocus,
+                       onCancel: closeAndRestoreFocus,
+                       orient: this.isLeftToRight() ? 'L' : 'R'
+               });
+               this.focus();
+
+               this._onBlur = function(){
+                       this.inherited('_onBlur', arguments);
+                       // Usually the parent closes the child widget but if this is a context
+                       // menu then there is no parent
+                       dijit.popup.close(this);
+                       // don't try to restore focus; user has clicked another part of the screen
+                       // and set focus there
+               };
+       },
+
+       uninitialize: function(){
+               dojo.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
+               this.inherited(arguments);
+       }
+}
+);
+
 }