Upgrade to ExtJS 3.1.1 - Released 02/08/2010
[extjs.git] / docs / source / Menu.html
1 <html>\r
2 <head>\r
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    \r
4   <title>The source code</title>\r
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />\r
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>\r
7 </head>\r
8 <body  onload="prettyPrint();">\r
9     <pre class="prettyprint lang-js"><div id="cls-Ext.menu.Menu"></div>/**
10  * @class Ext.menu.Menu
11  * @extends Ext.Container
12  * <p>A menu object.  This is the container to which you may add menu items.  Menu can also serve as a base class
13  * when you want a specialized menu based off of another component (like {@link Ext.menu.DateMenu} for example).</p>
14  * <p>Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Component}s.</p>
15  * <p>To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items}
16  * specify <tt>iconCls: 'no-icon'</tt>.  This reserves a space for an icon, and indents the Component in line
17  * with the other menu items.  See {@link Ext.form.ComboBox}.{@link Ext.form.ComboBox#getListParent getListParent}
18  * for an example.</p>
19  * <p>By default, Menus are absolutely positioned, floating Components. By configuring a Menu with
20  * <b><tt>{@link #floating}:false</tt></b>, a Menu may be used as child of a Container.</p>
21  *
22  * @xtype menu
23  */
24 Ext.menu.Menu = Ext.extend(Ext.Container, {
25     <div id="cfg-Ext.menu.Menu-defaults"></div>/**
26      * @cfg {Object} defaults
27      * A config object that will be applied to all items added to this container either via the {@link #items}
28      * config or via the {@link #add} method.  The defaults config can contain any number of
29      * name/value property pairs to be added to each item, and should be valid for the types of items
30      * being added to the menu.
31      */
32     <div id="cfg-Ext.menu.Menu-items"></div>/**
33      * @cfg {Mixed} items
34      * An array of items to be added to this menu. Menus may contain either {@link Ext.menu.Item menu items},
35      * or general {@link Ext.Component Component}s.
36      */
37     <div id="cfg-Ext.menu.Menu-minWidth"></div>/**
38      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
39      */
40     minWidth : 120,
41     <div id="cfg-Ext.menu.Menu-shadow"></div>/**
42      * @cfg {Boolean/String} shadow True or 'sides' for the default effect, 'frame' for 4-way shadow, and 'drop'
43      * for bottom-right shadow (defaults to 'sides')
44      */
45     shadow : 'sides',
46     <div id="cfg-Ext.menu.Menu-subMenuAlign"></div>/**
47      * @cfg {String} subMenuAlign The {@link Ext.Element#alignTo} anchor position value to use for submenus of
48      * this menu (defaults to 'tl-tr?')
49      */
50     subMenuAlign : 'tl-tr?',
51     <div id="cfg-Ext.menu.Menu-defaultAlign"></div>/**
52      * @cfg {String} defaultAlign The default {@link Ext.Element#alignTo} anchor position value for this menu
53      * relative to its element of origin (defaults to 'tl-bl?')
54      */
55     defaultAlign : 'tl-bl?',
56     <div id="cfg-Ext.menu.Menu-allowOtherMenus"></div>/**
57      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
58      */
59     allowOtherMenus : false,
60     <div id="cfg-Ext.menu.Menu-ignoreParentClicks"></div>/**
61      * @cfg {Boolean} ignoreParentClicks True to ignore clicks on any item in this menu that is a parent item (displays
62      * a submenu) so that the submenu is not dismissed when clicking the parent item (defaults to false).
63      */
64     ignoreParentClicks : false,
65     <div id="cfg-Ext.menu.Menu-enableScrolling"></div>/**
66      * @cfg {Boolean} enableScrolling True to allow the menu container to have scroller controls if the menu is too long (defaults to true).
67      */
68     enableScrolling : true,
69     <div id="cfg-Ext.menu.Menu-maxHeight"></div>/**
70      * @cfg {Number} maxHeight The maximum height of the menu. Only applies when enableScrolling is set to True (defaults to null).
71      */
72     maxHeight : null,
73     <div id="cfg-Ext.menu.Menu-scrollIncrement"></div>/**
74      * @cfg {Number} scrollIncrement The amount to scroll the menu. Only applies when enableScrolling is set to True (defaults to 24).
75      */
76     scrollIncrement : 24,
77     <div id="cfg-Ext.menu.Menu-showSeparator"></div>/**
78      * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).
79      */
80     showSeparator : true,
81     <div id="cfg-Ext.menu.Menu-defaultOffsets"></div>/**
82      * @cfg {Array} defaultOffsets An array specifying the [x, y] offset in pixels by which to
83      * change the default Menu popup position after aligning according to the {@link #defaultAlign}
84      * configuration. Defaults to <tt>[0, 0]</tt>.
85      */
86     defaultOffsets : [0, 0],
87
88     <div id="cfg-Ext.menu.Menu-plain"></div>/**
89      * @cfg {Boolean} plain
90      * True to remove the incised line down the left side of the menu. Defaults to <tt>false</tt>.
91      */
92     plain : false,
93
94     <div id="cfg-Ext.menu.Menu-floating"></div>/**
95      * @cfg {Boolean} floating
96      * <p>By default, a Menu configured as <b><code>floating:true</code></b>
97      * will be rendered as an {@link Ext.Layer} (an absolutely positioned,
98      * floating Component with zindex=15000).
99      * If configured as <b><code>floating:false</code></b>, the Menu may be
100      * used as child item of another Container instead of a free-floating
101      * {@link Ext.Layer Layer}.
102      */
103     floating : true,
104
105
106     <div id="cfg-Ext.menu.Menu-zIndex"></div>/**
107      * @cfg {Number} zIndex
108      * zIndex to use when the menu is floating.
109      */
110     zIndex: 15000,
111
112     // private
113     hidden : true,
114
115     <div id="cfg-Ext.menu.Menu-layout"></div>/**
116      * @cfg {String/Object} layout
117      * This class assigns a default layout (<code>layout:'<b>menu</b>'</code>).
118      * Developers <i>may</i> override this configuration option if another layout is required.
119      * See {@link Ext.Container#layout} for additional information.
120      */
121     layout : 'menu',
122     hideMode : 'offsets',    // Important for laying out Components
123     scrollerHeight : 8,
124     autoLayout : true,       // Provided for backwards compat
125     defaultType : 'menuitem',
126     bufferResize : false,
127
128     initComponent : function(){
129         if(Ext.isArray(this.initialConfig)){
130             Ext.apply(this, {items:this.initialConfig});
131         }
132         this.addEvents(
133             <div id="event-Ext.menu.Menu-click"></div>/**
134              * @event click
135              * Fires when this menu is clicked (or when the enter key is pressed while it is active)
136              * @param {Ext.menu.Menu} this
137             * @param {Ext.menu.Item} menuItem The menu item that was clicked
138              * @param {Ext.EventObject} e
139              */
140             'click',
141             <div id="event-Ext.menu.Menu-mouseover"></div>/**
142              * @event mouseover
143              * Fires when the mouse is hovering over this menu
144              * @param {Ext.menu.Menu} this
145              * @param {Ext.EventObject} e
146              * @param {Ext.menu.Item} menuItem The menu item that was clicked
147              */
148             'mouseover',
149             <div id="event-Ext.menu.Menu-mouseout"></div>/**
150              * @event mouseout
151              * Fires when the mouse exits this menu
152              * @param {Ext.menu.Menu} this
153              * @param {Ext.EventObject} e
154              * @param {Ext.menu.Item} menuItem The menu item that was clicked
155              */
156             'mouseout',
157             <div id="event-Ext.menu.Menu-itemclick"></div>/**
158              * @event itemclick
159              * Fires when a menu item contained in this menu is clicked
160              * @param {Ext.menu.BaseItem} baseItem The BaseItem that was clicked
161              * @param {Ext.EventObject} e
162              */
163             'itemclick'
164         );
165         Ext.menu.MenuMgr.register(this);
166         if(this.floating){
167             Ext.EventManager.onWindowResize(this.hide, this);
168         }else{
169             if(this.initialConfig.hidden !== false){
170                 this.hidden = false;
171             }
172             this.internalDefaults = {hideOnClick: false};
173         }
174         Ext.menu.Menu.superclass.initComponent.call(this);
175         if(this.autoLayout){
176             var fn = this.doLayout.createDelegate(this, []);
177             this.on({
178                 add: fn,
179                 remove: fn
180             });
181         }
182     },
183
184     //private
185     getLayoutTarget : function() {
186         return this.ul;
187     },
188
189     // private
190     onRender : function(ct, position){
191         if(!ct){
192             ct = Ext.getBody();
193         }
194
195         var dh = {
196             id: this.getId(),
197             cls: 'x-menu ' + ((this.floating) ? 'x-menu-floating x-layer ' : '') + (this.cls || '') + (this.plain ? ' x-menu-plain' : '') + (this.showSeparator ? '' : ' x-menu-nosep'),
198             style: this.style,
199             cn: [
200                 {tag: 'a', cls: 'x-menu-focus', href: '#', onclick: 'return false;', tabIndex: '-1'},
201                 {tag: 'ul', cls: 'x-menu-list'}
202             ]
203         };
204         if(this.floating){
205             this.el = new Ext.Layer({
206                 shadow: this.shadow,
207                 dh: dh,
208                 constrain: false,
209                 parentEl: ct,
210                 zindex: this.zIndex
211             });
212         }else{
213             this.el = ct.createChild(dh);
214         }
215         Ext.menu.Menu.superclass.onRender.call(this, ct, position);
216
217         if(!this.keyNav){
218             this.keyNav = new Ext.menu.MenuNav(this);
219         }
220         // generic focus element
221         this.focusEl = this.el.child('a.x-menu-focus');
222         this.ul = this.el.child('ul.x-menu-list');
223         this.mon(this.ul, {
224             scope: this,
225             click: this.onClick,
226             mouseover: this.onMouseOver,
227             mouseout: this.onMouseOut
228         });
229         if(this.enableScrolling){
230             this.mon(this.el, {
231                 scope: this,
232                 delegate: '.x-menu-scroller',
233                 click: this.onScroll,
234                 mouseover: this.deactivateActive
235             });
236         }
237     },
238
239     // private
240     findTargetItem : function(e){
241         var t = e.getTarget('.x-menu-list-item', this.ul, true);
242         if(t && t.menuItemId){
243             return this.items.get(t.menuItemId);
244         }
245     },
246
247     // private
248     onClick : function(e){
249         var t = this.findTargetItem(e);
250         if(t){
251             if(t.isFormField){
252                 this.setActiveItem(t);
253             }else if(t instanceof Ext.menu.BaseItem){
254                 if(t.menu && this.ignoreParentClicks){
255                     t.expandMenu();
256                     e.preventDefault();
257                 }else if(t.onClick){
258                     t.onClick(e);
259                     this.fireEvent('click', this, t, e);
260                 }
261             }
262         }
263     },
264
265     // private
266     setActiveItem : function(item, autoExpand){
267         if(item != this.activeItem){
268             this.deactivateActive();
269             if((this.activeItem = item).isFormField){
270                 item.focus();
271             }else{
272                 item.activate(autoExpand);
273             }
274         }else if(autoExpand){
275             item.expandMenu();
276         }
277     },
278
279     deactivateActive : function(){
280         var a = this.activeItem;
281         if(a){
282             if(a.isFormField){
283                 //Fields cannot deactivate, but Combos must collapse
284                 if(a.collapse){
285                     a.collapse();
286                 }
287             }else{
288                 a.deactivate();
289             }
290             delete this.activeItem;
291         }
292     },
293
294     // private
295     tryActivate : function(start, step){
296         var items = this.items;
297         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
298             var item = items.get(i);
299             if(!item.disabled && (item.canActivate || item.isFormField)){
300                 this.setActiveItem(item, false);
301                 return item;
302             }
303         }
304         return false;
305     },
306
307     // private
308     onMouseOver : function(e){
309         var t = this.findTargetItem(e);
310         if(t){
311             if(t.canActivate && !t.disabled){
312                 this.setActiveItem(t, true);
313             }
314         }
315         this.over = true;
316         this.fireEvent('mouseover', this, e, t);
317     },
318
319     // private
320     onMouseOut : function(e){
321         var t = this.findTargetItem(e);
322         if(t){
323             if(t == this.activeItem && t.shouldDeactivate && t.shouldDeactivate(e)){
324                 this.activeItem.deactivate();
325                 delete this.activeItem;
326             }
327         }
328         this.over = false;
329         this.fireEvent('mouseout', this, e, t);
330     },
331
332     // private
333     onScroll : function(e, t){
334         if(e){
335             e.stopEvent();
336         }
337         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
338         ul.scrollTop += this.scrollIncrement * (top ? -1 : 1);
339         if(top ? ul.scrollTop <= 0 : ul.scrollTop + this.activeMax >= ul.scrollHeight){
340            this.onScrollerOut(null, t);
341         }
342     },
343
344     // private
345     onScrollerIn : function(e, t){
346         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
347         if(top ? ul.scrollTop > 0 : ul.scrollTop + this.activeMax < ul.scrollHeight){
348             Ext.fly(t).addClass(['x-menu-item-active', 'x-menu-scroller-active']);
349         }
350     },
351
352     // private
353     onScrollerOut : function(e, t){
354         Ext.fly(t).removeClass(['x-menu-item-active', 'x-menu-scroller-active']);
355     },
356
357     <div id="method-Ext.menu.Menu-show"></div>/**
358      * If <code>{@link #floating}=true</code>, shows this menu relative to
359      * another element using {@link #showat}, otherwise uses {@link Ext.Component#show}.
360      * @param {Mixed} element The element to align to
361      * @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to
362      * the element (defaults to this.defaultAlign)
363      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
364      */
365     show : function(el, pos, parentMenu){
366         if(this.floating){
367             this.parentMenu = parentMenu;
368             if(!this.el){
369                 this.render();
370                 this.doLayout(false, true);
371             }
372             this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign, this.defaultOffsets), parentMenu);
373         }else{
374             Ext.menu.Menu.superclass.show.call(this);
375         }
376     },
377
378     <div id="method-Ext.menu.Menu-showAt"></div>/**
379      * Displays this menu at a specific xy position and fires the 'show' event if a
380      * handler for the 'beforeshow' event does not return false cancelling the operation.
381      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
382      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
383      */
384     showAt : function(xy, parentMenu){
385         if(this.fireEvent('beforeshow', this) !== false){
386             this.parentMenu = parentMenu;
387             if(!this.el){
388                 this.render();
389             }
390             if(this.enableScrolling){
391                 // set the position so we can figure out the constrain value.
392                 this.el.setXY(xy);
393                 //constrain the value, keep the y coordinate the same
394                 xy[1] = this.constrainScroll(xy[1]);
395                 xy = [this.el.adjustForConstraints(xy)[0], xy[1]];
396             }else{
397                 //constrain to the viewport.
398                 xy = this.el.adjustForConstraints(xy);
399             }
400             this.el.setXY(xy);
401             this.el.show();
402             Ext.menu.Menu.superclass.onShow.call(this);
403             if(Ext.isIE){
404                 // internal event, used so we don't couple the layout to the menu
405                 this.fireEvent('autosize', this);
406                 if(!Ext.isIE8){
407                     this.el.repaint();
408                 }
409             }
410             this.hidden = false;
411             this.focus();
412             this.fireEvent('show', this);
413         }
414     },
415
416     constrainScroll : function(y){
417         var max, full = this.ul.setHeight('auto').getHeight(),
418             returnY = y, normalY, parentEl, scrollTop, viewHeight;
419         if(this.floating){
420             parentEl = Ext.fly(this.el.dom.parentNode);
421             scrollTop = parentEl.getScroll().top;
422             viewHeight = parentEl.getViewSize().height;
423             //Normalize y by the scroll position for the parent element.  Need to move it into the coordinate space
424             //of the view.
425             normalY = y - scrollTop;
426             max = this.maxHeight ? this.maxHeight : viewHeight - normalY;
427             if(full > viewHeight) {
428                 max = viewHeight;
429                 //Set returnY equal to (0,0) in view space by reducing y by the value of normalY
430                 returnY = y - normalY;
431             } else if(max < full) {
432                 returnY = y - (full - max);
433                 max = full;
434             }
435         }else{
436             max = this.getHeight();
437         }
438         if(full > max && max > 0){
439             this.activeMax = max - this.scrollerHeight * 2 - this.el.getFrameWidth('tb') - Ext.num(this.el.shadowOffset, 0);
440             this.ul.setHeight(this.activeMax);
441             this.createScrollers();
442             this.el.select('.x-menu-scroller').setDisplayed('');
443         }else{
444             this.ul.setHeight(full);
445             this.el.select('.x-menu-scroller').setDisplayed('none');
446         }
447         this.ul.dom.scrollTop = 0;
448         return returnY;
449     },
450
451     createScrollers : function(){
452         if(!this.scroller){
453             this.scroller = {
454                 pos: 0,
455                 top: this.el.insertFirst({
456                     tag: 'div',
457                     cls: 'x-menu-scroller x-menu-scroller-top',
458                     html: '&#160;'
459                 }),
460                 bottom: this.el.createChild({
461                     tag: 'div',
462                     cls: 'x-menu-scroller x-menu-scroller-bottom',
463                     html: '&#160;'
464                 })
465             };
466             this.scroller.top.hover(this.onScrollerIn, this.onScrollerOut, this);
467             this.scroller.topRepeater = new Ext.util.ClickRepeater(this.scroller.top, {
468                 listeners: {
469                     click: this.onScroll.createDelegate(this, [null, this.scroller.top], false)
470                 }
471             });
472             this.scroller.bottom.hover(this.onScrollerIn, this.onScrollerOut, this);
473             this.scroller.bottomRepeater = new Ext.util.ClickRepeater(this.scroller.bottom, {
474                 listeners: {
475                     click: this.onScroll.createDelegate(this, [null, this.scroller.bottom], false)
476                 }
477             });
478         }
479     },
480
481     onLayout : function(){
482         if(this.isVisible()){
483             if(this.enableScrolling){
484                 this.constrainScroll(this.el.getTop());
485             }
486             if(this.floating){
487                 this.el.sync();
488             }
489         }
490     },
491
492     focus : function(){
493         if(!this.hidden){
494             this.doFocus.defer(50, this);
495         }
496     },
497
498     doFocus : function(){
499         if(!this.hidden){
500             this.focusEl.focus();
501         }
502     },
503
504     <div id="method-Ext.menu.Menu-hide"></div>/**
505      * Hides this menu and optionally all parent menus
506      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
507      */
508     hide : function(deep){
509         if (!this.isDestroyed) {
510             this.deepHide = deep;
511             Ext.menu.Menu.superclass.hide.call(this);
512             delete this.deepHide;
513         }
514     },
515
516     // private
517     onHide : function(){
518         Ext.menu.Menu.superclass.onHide.call(this);
519         this.deactivateActive();
520         if(this.el && this.floating){
521             this.el.hide();
522         }
523         var pm = this.parentMenu;
524         if(this.deepHide === true && pm){
525             if(pm.floating){
526                 pm.hide(true);
527             }else{
528                 pm.deactivateActive();
529             }
530         }
531     },
532
533     // private
534     lookupComponent : function(c){
535          if(Ext.isString(c)){
536             c = (c == 'separator' || c == '-') ? new Ext.menu.Separator() : new Ext.menu.TextItem(c);
537              this.applyDefaults(c);
538          }else{
539             if(Ext.isObject(c)){
540                 c = this.getMenuItem(c);
541             }else if(c.tagName || c.el){ // element. Wrap it.
542                 c = new Ext.BoxComponent({
543                     el: c
544                 });
545             }
546          }
547          return c;
548     },
549
550     applyDefaults : function(c){
551         if(!Ext.isString(c)){
552             c = Ext.menu.Menu.superclass.applyDefaults.call(this, c);
553             var d = this.internalDefaults;
554             if(d){
555                 if(c.events){
556                     Ext.applyIf(c.initialConfig, d);
557                     Ext.apply(c, d);
558                 }else{
559                     Ext.applyIf(c, d);
560                 }
561             }
562         }
563         return c;
564     },
565
566     // private
567     getMenuItem : function(config){
568        if(!config.isXType){
569             if(!config.xtype && Ext.isBoolean(config.checked)){
570                 return new Ext.menu.CheckItem(config)
571             }
572             return Ext.create(config, this.defaultType);
573         }
574         return config;
575     },
576
577     <div id="method-Ext.menu.Menu-addSeparator"></div>/**
578      * Adds a separator bar to the menu
579      * @return {Ext.menu.Item} The menu item that was added
580      */
581     addSeparator : function(){
582         return this.add(new Ext.menu.Separator());
583     },
584
585     <div id="method-Ext.menu.Menu-addElement"></div>/**
586      * Adds an {@link Ext.Element} object to the menu
587      * @param {Mixed} el The element or DOM node to add, or its id
588      * @return {Ext.menu.Item} The menu item that was added
589      */
590     addElement : function(el){
591         return this.add(new Ext.menu.BaseItem({
592             el: el
593         }));
594     },
595
596     <div id="method-Ext.menu.Menu-addItem"></div>/**
597      * Adds an existing object based on {@link Ext.menu.BaseItem} to the menu
598      * @param {Ext.menu.Item} item The menu item to add
599      * @return {Ext.menu.Item} The menu item that was added
600      */
601     addItem : function(item){
602         return this.add(item);
603     },
604
605     <div id="method-Ext.menu.Menu-addMenuItem"></div>/**
606      * Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu
607      * @param {Object} config A MenuItem config object
608      * @return {Ext.menu.Item} The menu item that was added
609      */
610     addMenuItem : function(config){
611         return this.add(this.getMenuItem(config));
612     },
613
614     <div id="method-Ext.menu.Menu-addText"></div>/**
615      * Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu
616      * @param {String} text The text to display in the menu item
617      * @return {Ext.menu.Item} The menu item that was added
618      */
619     addText : function(text){
620         return this.add(new Ext.menu.TextItem(text));
621     },
622
623     //private
624     onDestroy : function(){
625         Ext.EventManager.removeResizeListener(this.hide, this);
626         var pm = this.parentMenu;
627         if(pm && pm.activeChild == this){
628             delete pm.activeChild;
629         }
630         delete this.parentMenu;
631         Ext.menu.Menu.superclass.onDestroy.call(this);
632         Ext.menu.MenuMgr.unregister(this);
633         if(this.keyNav) {
634             this.keyNav.disable();
635         }
636         var s = this.scroller;
637         if(s){
638             Ext.destroy(s.topRepeater, s.bottomRepeater, s.top, s.bottom);
639         }
640         Ext.destroy(
641             this.el,
642             this.focusEl,
643             this.ul
644         );
645     }
646 });
647
648 Ext.reg('menu', Ext.menu.Menu);
649
650 // MenuNav is a private utility class used internally by the Menu
651 Ext.menu.MenuNav = Ext.extend(Ext.KeyNav, function(){
652     function up(e, m){
653         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
654             m.tryActivate(m.items.length-1, -1);
655         }
656     }
657     function down(e, m){
658         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
659             m.tryActivate(0, 1);
660         }
661     }
662     return {
663         constructor : function(menu){
664             Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);
665             this.scope = this.menu = menu;
666         },
667
668         doRelay : function(e, h){
669             var k = e.getKey();
670 //          Keystrokes within a form Field (e.g.: down in a Combo) do not navigate. Allow only TAB
671             if (this.menu.activeItem && this.menu.activeItem.isFormField && k != e.TAB) {
672                 return false;
673             }
674             if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
675                 this.menu.tryActivate(0, 1);
676                 return false;
677             }
678             return h.call(this.scope || this, e, this.menu);
679         },
680
681         tab: function(e, m) {
682             e.stopEvent();
683             if (e.shiftKey) {
684                 up(e, m);
685             } else {
686                 down(e, m);
687             }
688         },
689
690         up : up,
691
692         down : down,
693
694         right : function(e, m){
695             if(m.activeItem){
696                 m.activeItem.expandMenu(true);
697             }
698         },
699
700         left : function(e, m){
701             m.hide();
702             if(m.parentMenu && m.parentMenu.activeItem){
703                 m.parentMenu.activeItem.activate();
704             }
705         },
706
707         enter : function(e, m){
708             if(m.activeItem){
709                 e.stopPropagation();
710                 m.activeItem.onClick(e);
711                 m.fireEvent('click', this, m.activeItem);
712                 return true;
713             }
714         }
715     };
716 }());
717 </pre>    \r
718 </body>\r
719 </html>