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