]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/_MenuBase.js.uncompressed.js
1 define("dijit/_MenuBase", [
7 "dojo/_base/declare", // declare
8 "dojo/dom", // dom.isDescendant domClass.replace
10 "dojo/dom-class", // domClass.replace
11 "dojo/_base/lang", // lang.hitch
12 "dojo/_base/array" // array.indexOf
13 ], function(pm
, winUtils
, _Widget
, _KeyNavContainer
, _TemplatedMixin
,
14 declare
, dom
, domAttr
, domClass
, lang
, array
){
17 var _Widget = dijit._Widget;
18 var _TemplatedMixin = dijit._TemplatedMixin;
19 var _KeyNavContainer = dijit._KeyNavContainer;
25 // Base class for Menu and MenuBar
27 return declare("dijit._MenuBase",
28 [_Widget
, _TemplatedMixin
, _KeyNavContainer
],
31 // Base class for Menu and MenuBar
33 // parentMenu: [readonly] Widget
34 // pointer to menu that displayed me
37 // popupDelay: Integer
38 // number of milliseconds before hovering (without clicking) causes the popup to automatically open.
41 onExecute: function(){
43 // Attach point for notification about when a menu item has been executed.
44 // This is an internal mechanism used for Menus to signal to their parent to
45 // close them, because they are about to execute the onClick handler. In
46 // general developers should not attach to or override this method.
51 onCancel: function(/*Boolean*/ /*===== closeAll =====*/){
53 // Attach point for notification about when the user cancels the current menu
54 // This is an internal mechanism used for Menus to signal to their parent to
55 // close them. In general developers should not attach to or override this method.
60 _moveToPopup: function(/*Event*/ evt
){
62 // This handles the right arrow key (left arrow key on RTL systems),
63 // which will either open a submenu, or move to the next item in the
68 if(this.focusedChild
&& this.focusedChild
.popup
&& !this.focusedChild
.disabled
){
69 this.focusedChild
._onClick(evt
);
71 var topMenu
= this._getTopMenu();
72 if(topMenu
&& topMenu
._isMenuBar
){
78 _onPopupHover: function(/*Event*/ /*===== evt =====*/){
80 // This handler is called when the mouse moves over the popup.
84 // if the mouse hovers over a menu popup that is in pending-close state,
85 // then stop the close operation.
86 // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
87 if(this.currentPopup
&& this.currentPopup
._pendingClose_timer
){
88 var parentMenu
= this.currentPopup
.parentMenu
;
89 // highlight the parent menu item pointing to this popup
90 if(parentMenu
.focusedChild
){
91 parentMenu
.focusedChild
._setSelected(false);
93 parentMenu
.focusedChild
= this.currentPopup
.from_item
;
94 parentMenu
.focusedChild
._setSelected(true);
95 // cancel the pending close
96 this._stopPendingCloseTimer(this.currentPopup
);
100 onItemHover: function(/*MenuItem*/ item
){
102 // Called when cursor is over a MenuItem.
106 // Don't do anything unless user has "activated" the menu by:
108 // 2) opening it from a parent menu (which automatically focuses it)
110 this.focusChild(item
);
111 if(this.focusedChild
.popup
&& !this.focusedChild
.disabled
&& !this.hover_timer
){
112 this.hover_timer
= setTimeout(lang
.hitch(this, "_openPopup"), this.popupDelay
);
115 // if the user is mixing mouse and keyboard navigation,
116 // then the menu may not be active but a menu item has focus,
117 // but it's not the item that the mouse just hovered over.
118 // To avoid both keyboard and mouse selections, use the latest.
119 if(this.focusedChild
){
120 this.focusChild(item
);
122 this._hoveredChild
= item
;
125 _onChildBlur: function(item
){
127 // Called when a child MenuItem becomes inactive because focus
128 // has been removed from the MenuItem *and* it's descendant menus.
131 this._stopPopupTimer();
132 item
._setSelected(false);
133 // Close all popups that are open and descendants of this menu
134 var itemPopup
= item
.popup
;
136 this._stopPendingCloseTimer(itemPopup
);
137 itemPopup
._pendingClose_timer
= setTimeout(function(){
138 itemPopup
._pendingClose_timer
= null;
139 if(itemPopup
.parentMenu
){
140 itemPopup
.parentMenu
.currentPopup
= null;
142 pm
.close(itemPopup
); // this calls onClose
147 onItemUnhover: function(/*MenuItem*/ item
){
149 // Callback fires when mouse exits a MenuItem
154 this._stopPopupTimer();
156 if(this._hoveredChild
== item
){ this._hoveredChild
= null; }
159 _stopPopupTimer: function(){
161 // Cancels the popup timer because the user has stop hovering
162 // on the MenuItem, etc.
165 if(this.hover_timer
){
166 clearTimeout(this.hover_timer
);
167 this.hover_timer
= null;
171 _stopPendingCloseTimer: function(/*dijit._Widget*/ popup
){
173 // Cancels the pending-close timer because the close has been preempted
176 if(popup
._pendingClose_timer
){
177 clearTimeout(popup
._pendingClose_timer
);
178 popup
._pendingClose_timer
= null;
182 _stopFocusTimer: function(){
184 // Cancels the pending-focus timer because the menu was closed before focus occured
187 if(this._focus_timer
){
188 clearTimeout(this._focus_timer
);
189 this._focus_timer
= null;
193 _getTopMenu: function(){
195 // Returns the top menu in this chain of Menus
198 for(var top
=this; top
.parentMenu
; top
=top
.parentMenu
);
202 onItemClick: function(/*dijit._Widget*/ item
, /*Event*/ evt
){
204 // Handle clicks on an item.
208 // this can't be done in _onFocus since the _onFocus events occurs asynchronously
209 if(typeof this.isShowingNow
== 'undefined'){ // non-popup menu
213 this.focusChild(item
);
215 if(item
.disabled
){ return false; }
220 // before calling user defined handler, close hierarchy of menus
221 // and restore focus to place it was when menu was opened
224 // user defined handler for click
229 _openPopup: function(){
231 // Open the popup to the side of/underneath the current menu item
235 this._stopPopupTimer();
236 var from_item
= this.focusedChild
;
237 if(!from_item
){ return; } // the focused child lost focus since the timer was started
238 var popup
= from_item
.popup
;
239 if(popup
.isShowingNow
){ return; }
240 if(this.currentPopup
){
241 this._stopPendingCloseTimer(this.currentPopup
);
242 pm
.close(this.currentPopup
);
244 popup
.parentMenu
= this;
245 popup
.from_item
= from_item
; // helps finding the parent item that should be focused for this popup
250 around
: from_item
.domNode
,
251 orient
: this._orient
|| ["after", "before"],
252 onCancel: function(){ // called when the child menu is canceled
253 // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
254 // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
255 self
.focusChild(from_item
); // put focus back on my node
256 self
._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
257 from_item
._setSelected(true); // oops, _cleanUp() deselected the item
258 self
.focusedChild
= from_item
; // and unset focusedChild
260 onExecute
: lang
.hitch(this, "_cleanUp")
263 this.currentPopup
= popup
;
264 // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
265 popup
.connect(popup
.domNode
, "onmouseenter", lang
.hitch(self
, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
268 // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
269 // if the cursor happens to collide with the popup, it will generate an onmouseover event
270 // even though the mouse wasn't moved. Use a setTimeout() to call popup.focus so that
271 // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
272 popup
._focus_timer
= setTimeout(lang
.hitch(popup
, function(){
273 this._focus_timer
= null;
279 _markActive: function(){
281 // Mark this menu's state as active.
282 // Called when this Menu gets focus from:
283 // 1) clicking it (mouse or via space/arrow key)
284 // 2) being opened by a parent menu.
285 // This is not called just from mouse hover.
286 // Focusing a menu via TAB does NOT automatically set isActive
287 // since TAB is a navigation operation and not a selection one.
288 // For Windows apps, pressing the ALT key focuses the menubar
289 // menus (similar to TAB navigation) but the menu is not active
290 // (ie no dropdown) until an item is clicked.
291 this.isActive
= true;
292 domClass
.replace(this.domNode
, "dijitMenuActive", "dijitMenuPassive");
295 onOpen: function(/*Event*/ /*===== e =====*/){
297 // Callback when this menu is opened.
298 // This is called by the popup manager as notification that the menu
303 this.isShowingNow
= true;
307 _markInactive: function(){
309 // Mark this menu's state as inactive.
310 this.isActive
= false; // don't do this in _onBlur since the state is pending-close until we get here
311 domClass
.replace(this.domNode
, "dijitMenuPassive", "dijitMenuActive");
316 // Callback when this menu is closed.
317 // This is called by the popup manager as notification that the menu
322 this._stopFocusTimer();
323 this._markInactive();
324 this.isShowingNow
= false;
325 this.parentMenu
= null;
328 _closeChild: function(){
330 // Called when submenu is clicked or focus is lost. Close hierarchy of menus.
333 this._stopPopupTimer();
335 if(this.currentPopup
){
336 // If focus is on a descendant MenuItem then move focus to me,
337 // because IE doesn't like it when you display:none a node with focus,
338 // and also so keyboard users don't lose control.
339 // Likely, immediately after a user defined onClick handler will move focus somewhere
340 // else, like a Dialog.
341 if(array
.indexOf(this._focusManager
.activeStack
, this.id
) >= 0){
342 domAttr
.set(this.focusedChild
.focusNode
, "tabIndex", this.tabIndex
);
343 this.focusedChild
.focusNode
.focus();
345 // Close all popups that are open and descendants of this menu
346 pm
.close(this.currentPopup
);
347 this.currentPopup
= null;
350 if(this.focusedChild
){ // unhighlight the focused item
351 this.focusedChild
._setSelected(false);
352 this.focusedChild
._onUnhover();
353 this.focusedChild
= null;
357 _onItemFocus: function(/*MenuItem*/ item
){
359 // Called when child of this Menu gets focus from:
361 // 2) tabbing into it
362 // 3) being opened by a parent menu.
363 // This is not called just from mouse hover.
364 if(this._hoveredChild
&& this._hoveredChild
!= item
){
365 this._hoveredChild
._onUnhover(); // any previous mouse movement is trumped by focus selection
371 // Called when focus is moved away from this Menu and it's submenus.
375 this.inherited(arguments
);
378 _cleanUp: function(){
380 // Called when the user is done with this menu. Closes hierarchy of menus.
384 this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
385 if(typeof this.isShowingNow
== 'undefined'){ // non-popup menu doesn't call onClose
386 this._markInactive();