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