Upgrade to ExtJS 3.0.3 - Released 10/11/2009
[extjs.git] / pkgs / pkg-menu-debug.js
1 /*!
2  * Ext JS Library 3.0.3
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**\r
8  * @class Ext.layout.MenuLayout\r
9  * @extends Ext.layout.ContainerLayout\r
10  * <p>Layout manager used by {@link Ext.menu.Menu}. Generally this class should not need to be used directly.</p>\r
11  */\r
12  Ext.layout.MenuLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
13     monitorResize : true,\r
14 \r
15     setContainer : function(ct){\r
16         this.monitorResize = !ct.floating;\r
17         // This event is only fired by the menu in IE, used so we don't couple\r
18         // the menu with the layout.\r
19         ct.on('autosize', this.doAutoSize, this);\r
20         Ext.layout.MenuLayout.superclass.setContainer.call(this, ct);\r
21     },\r
22 \r
23     renderItem : function(c, position, target){\r
24         if (!this.itemTpl) {\r
25             this.itemTpl = Ext.layout.MenuLayout.prototype.itemTpl = new Ext.XTemplate(\r
26                 '<li id="{itemId}" class="{itemCls}">',\r
27                     '<tpl if="needsIcon">',\r
28                         '<img src="{icon}" class="{iconCls}"/>',\r
29                     '</tpl>',\r
30                 '</li>'\r
31             );\r
32         }\r
33 \r
34         if(c && !c.rendered){\r
35             if(Ext.isNumber(position)){\r
36                 position = target.dom.childNodes[position];\r
37             }\r
38             var a = this.getItemArgs(c);\r
39 \r
40 //          The Component's positionEl is the <li> it is rendered into\r
41             c.render(c.positionEl = position ?\r
42                 this.itemTpl.insertBefore(position, a, true) :\r
43                 this.itemTpl.append(target, a, true));\r
44 \r
45 //          Link the containing <li> to the item.\r
46             c.positionEl.menuItemId = c.getItemId();\r
47 \r
48 //          If rendering a regular Component, and it needs an icon,\r
49 //          move the Component rightwards.\r
50             if (!a.isMenuItem && a.needsIcon) {\r
51                 c.positionEl.addClass('x-menu-list-item-indent');\r
52             }\r
53             this.configureItem(c, position);\r
54         }else if(c && !this.isValidParent(c, target)){\r
55             if(Ext.isNumber(position)){\r
56                 position = target.dom.childNodes[position];\r
57             }\r
58             target.dom.insertBefore(c.getActionEl().dom, position || null);\r
59         }\r
60     },\r
61 \r
62     getItemArgs : function(c) {\r
63         var isMenuItem = c instanceof Ext.menu.Item;\r
64         return {\r
65             isMenuItem: isMenuItem,\r
66             needsIcon: !isMenuItem && (c.icon || c.iconCls),\r
67             icon: c.icon || Ext.BLANK_IMAGE_URL,\r
68             iconCls: 'x-menu-item-icon ' + (c.iconCls || ''),\r
69             itemId: 'x-menu-el-' + c.id,\r
70             itemCls: 'x-menu-list-item '\r
71         };\r
72     },\r
73 \r
74 //  Valid if the Component is in a <li> which is part of our target <ul>\r
75     isValidParent : function(c, target) {\r
76         return c.el.up('li.x-menu-list-item', 5).dom.parentNode === (target.dom || target);\r
77     },\r
78 \r
79     onLayout : function(ct, target){\r
80         this.renderAll(ct, target);\r
81         this.doAutoSize();\r
82     },\r
83 \r
84     doAutoSize : function(){\r
85         var ct = this.container, w = ct.width;\r
86         if(ct.floating){\r
87             if(w){\r
88                 ct.setWidth(w);\r
89             }else if(Ext.isIE){\r
90                 ct.setWidth(Ext.isStrict && (Ext.isIE7 || Ext.isIE8) ? 'auto' : ct.minWidth);\r
91                 var el = ct.getEl(), t = el.dom.offsetWidth; // force recalc\r
92                 ct.setWidth(ct.getLayoutTarget().getWidth() + el.getFrameWidth('lr'));\r
93             }\r
94         }\r
95     }\r
96 });\r
97 Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;\r
98 \r
99 /**\r
100  * @class Ext.menu.Menu\r
101  * @extends Ext.Container\r
102  * <p>A menu object.  This is the container to which you may add menu items.  Menu can also serve as a base class\r
103  * when you want a specialized menu based off of another component (like {@link Ext.menu.DateMenu} for example).</p>\r
104  * <p>Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Component}s.</p>\r
105  * <p>To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items}\r
106  * specify <tt>iconCls: 'no-icon'</tt>.  This reserves a space for an icon, and indents the Component in line\r
107  * with the other menu items.  See {@link Ext.form.ComboBox}.{@link Ext.form.ComboBox#getListParent getListParent}\r
108  * for an example.</p>\r
109  * <p>By default, Menus are absolutely positioned, floating Components. By configuring a Menu with\r
110  * <b><tt>{@link #floating}:false</tt></b>, a Menu may be used as child of a Container.</p>\r
111  *\r
112  * @xtype menu\r
113  */\r
114 Ext.menu.Menu = Ext.extend(Ext.Container, {\r
115     /**\r
116      * @cfg {Object} defaults\r
117      * A config object that will be applied to all items added to this container either via the {@link #items}\r
118      * config or via the {@link #add} method.  The defaults config can contain any number of\r
119      * name/value property pairs to be added to each item, and should be valid for the types of items\r
120      * being added to the menu.\r
121      */\r
122     /**\r
123      * @cfg {Mixed} items\r
124      * An array of items to be added to this menu. Menus may contain either {@link Ext.menu.Item menu items},\r
125      * or general {@link Ext.Component Component}s.\r
126      */\r
127     /**\r
128      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)\r
129      */\r
130     minWidth : 120,\r
131     /**\r
132      * @cfg {Boolean/String} shadow True or 'sides' for the default effect, 'frame' for 4-way shadow, and 'drop'\r
133      * for bottom-right shadow (defaults to 'sides')\r
134      */\r
135     shadow : 'sides',\r
136     /**\r
137      * @cfg {String} subMenuAlign The {@link Ext.Element#alignTo} anchor position value to use for submenus of\r
138      * this menu (defaults to 'tl-tr?')\r
139      */\r
140     subMenuAlign : 'tl-tr?',\r
141     /**\r
142      * @cfg {String} defaultAlign The default {@link Ext.Element#alignTo} anchor position value for this menu\r
143      * relative to its element of origin (defaults to 'tl-bl?')\r
144      */\r
145     defaultAlign : 'tl-bl?',\r
146     /**\r
147      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)\r
148      */\r
149     allowOtherMenus : false,\r
150     /**\r
151      * @cfg {Boolean} ignoreParentClicks True to ignore clicks on any item in this menu that is a parent item (displays\r
152      * a submenu) so that the submenu is not dismissed when clicking the parent item (defaults to false).\r
153      */\r
154     ignoreParentClicks : false,\r
155     /**\r
156      * @cfg {Boolean} enableScrolling True to allow the menu container to have scroller controls if the menu is too long (defaults to true).\r
157      */\r
158     enableScrolling : true,\r
159     /**\r
160      * @cfg {Number} maxHeight The maximum height of the menu. Only applies when enableScrolling is set to True (defaults to null).\r
161      */\r
162     maxHeight : null,\r
163     /**\r
164      * @cfg {Number} scrollIncrement The amount to scroll the menu. Only applies when enableScrolling is set to True (defaults to 24).\r
165      */\r
166     scrollIncrement : 24,\r
167     /**\r
168      * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).\r
169      */\r
170     showSeparator : true,\r
171     /**\r
172      * @cfg {Array} defaultOffsets An array specifying the [x, y] offset in pixels by which to\r
173      * change the default Menu popup position after aligning according to the {@link #defaultAlign}\r
174      * configuration. Defaults to <tt>[0, 0]</tt>.\r
175      */\r
176     defaultOffsets : [0, 0],\r
177     \r
178     /**\r
179      * @cfg {Boolean} plain\r
180      * True to remove the incised line down the left side of the menu. Defaults to <tt>false</tt>.\r
181      */\r
182     plain : false,\r
183 \r
184     /**\r
185      * @cfg {Boolean} floating\r
186      * <p>By default, a Menu configured as <b><code>floating:true</code></b>\r
187      * will be rendered as an {@link Ext.Layer} (an absolutely positioned,\r
188      * floating Component with zindex=15000).\r
189      * If configured as <b><code>floating:false</code></b>, the Menu may be\r
190      * used as child item of another Container instead of a free-floating\r
191      * {@link Ext.Layer Layer}.\r
192      */\r
193     floating : true,\r
194 \r
195     // private\r
196     hidden : true,\r
197 \r
198     /**\r
199      * @cfg {String/Object} layout\r
200      * This class assigns a default layout (<code>layout:'<b>menu</b>'</code>).\r
201      * Developers <i>may</i> override this configuration option if another layout is required.\r
202      * See {@link Ext.Container#layout} for additional information.\r
203      */\r
204     layout : 'menu',\r
205     hideMode : 'offsets',    // Important for laying out Components\r
206     scrollerHeight : 8,\r
207     autoLayout : true,       // Provided for backwards compat\r
208     defaultType : 'menuitem',\r
209 \r
210     initComponent : function(){\r
211         if(Ext.isArray(this.initialConfig)){\r
212             Ext.apply(this, {items:this.initialConfig});\r
213         }\r
214         this.addEvents(\r
215             /**\r
216              * @event click\r
217              * Fires when this menu is clicked (or when the enter key is pressed while it is active)\r
218              * @param {Ext.menu.Menu} this\r
219             * @param {Ext.menu.Item} menuItem The menu item that was clicked\r
220              * @param {Ext.EventObject} e\r
221              */\r
222             'click',\r
223             /**\r
224              * @event mouseover\r
225              * Fires when the mouse is hovering over this menu\r
226              * @param {Ext.menu.Menu} this\r
227              * @param {Ext.EventObject} e\r
228              * @param {Ext.menu.Item} menuItem The menu item that was clicked\r
229              */\r
230             'mouseover',\r
231             /**\r
232              * @event mouseout\r
233              * Fires when the mouse exits this menu\r
234              * @param {Ext.menu.Menu} this\r
235              * @param {Ext.EventObject} e\r
236              * @param {Ext.menu.Item} menuItem The menu item that was clicked\r
237              */\r
238             'mouseout',\r
239             /**\r
240              * @event itemclick\r
241              * Fires when a menu item contained in this menu is clicked\r
242              * @param {Ext.menu.BaseItem} baseItem The BaseItem that was clicked\r
243              * @param {Ext.EventObject} e\r
244              */\r
245             'itemclick'\r
246         );\r
247         Ext.menu.MenuMgr.register(this);\r
248         if(this.floating){\r
249             Ext.EventManager.onWindowResize(this.hide, this);\r
250         }else{\r
251             if(this.initialConfig.hidden !== false){\r
252                 this.hidden = false;\r
253             }\r
254             this.internalDefaults = {hideOnClick: false};\r
255         }\r
256         Ext.menu.Menu.superclass.initComponent.call(this);\r
257         if(this.autoLayout){\r
258             this.on({\r
259                 add: this.doLayout,\r
260                 remove: this.doLayout,\r
261                 scope: this\r
262             });\r
263         }\r
264     },\r
265 \r
266     //private\r
267     getLayoutTarget : function() {\r
268         return this.ul;\r
269     },\r
270 \r
271     // private\r
272     onRender : function(ct, position){\r
273         if(!ct){\r
274             ct = Ext.getBody();\r
275         }\r
276 \r
277         var dh = {\r
278             id: this.getId(),\r
279             cls: 'x-menu ' + ((this.floating) ? 'x-menu-floating x-layer ' : '') + (this.cls || '') + (this.plain ? ' x-menu-plain' : '') + (this.showSeparator ? '' : ' x-menu-nosep'),\r
280             style: this.style,\r
281             cn: [\r
282                 {tag: 'a', cls: 'x-menu-focus', href: '#', onclick: 'return false;', tabIndex: '-1'},\r
283                 {tag: 'ul', cls: 'x-menu-list'}\r
284             ]\r
285         };\r
286         if(this.floating){\r
287             this.el = new Ext.Layer({\r
288                 shadow: this.shadow,\r
289                 dh: dh,\r
290                 constrain: false,\r
291                 parentEl: ct,\r
292                 zindex:15000\r
293             });\r
294         }else{\r
295             this.el = ct.createChild(dh);\r
296         }\r
297         Ext.menu.Menu.superclass.onRender.call(this, ct, position);\r
298 \r
299         if(!this.keyNav){\r
300             this.keyNav = new Ext.menu.MenuNav(this);\r
301         }\r
302         // generic focus element\r
303         this.focusEl = this.el.child('a.x-menu-focus');\r
304         this.ul = this.el.child('ul.x-menu-list');\r
305         this.mon(this.ul, {\r
306             scope: this,\r
307             click: this.onClick,\r
308             mouseover: this.onMouseOver,\r
309             mouseout: this.onMouseOut\r
310         });\r
311         if(this.enableScrolling){\r
312             this.mon(this.el, {\r
313                 scope: this,\r
314                 delegate: '.x-menu-scroller',\r
315                 click: this.onScroll,\r
316                 mouseover: this.deactivateActive\r
317             });\r
318         }\r
319     },\r
320 \r
321     // private\r
322     findTargetItem : function(e){\r
323         var t = e.getTarget('.x-menu-list-item', this.ul, true);\r
324         if(t && t.menuItemId){\r
325             return this.items.get(t.menuItemId);\r
326         }\r
327     },\r
328 \r
329     // private\r
330     onClick : function(e){\r
331         var t = this.findTargetItem(e);\r
332         if(t){\r
333             if(t.isFormField){\r
334                 this.setActiveItem(t);\r
335             }else if(t instanceof Ext.menu.BaseItem){\r
336                 if(t.menu && this.ignoreParentClicks){\r
337                     t.expandMenu();\r
338                     e.preventDefault();\r
339                 }else if(t.onClick){\r
340                     t.onClick(e);\r
341                     this.fireEvent('click', this, t, e);\r
342                 }\r
343             }\r
344         }\r
345     },\r
346 \r
347     // private\r
348     setActiveItem : function(item, autoExpand){\r
349         if(item != this.activeItem){\r
350             this.deactivateActive();\r
351             if((this.activeItem = item).isFormField){\r
352                 item.focus();\r
353             }else{\r
354                 item.activate(autoExpand);\r
355             }\r
356         }else if(autoExpand){\r
357             item.expandMenu();\r
358         }\r
359     },\r
360 \r
361     deactivateActive : function(){\r
362         var a = this.activeItem;\r
363         if(a){\r
364             if(a.isFormField){\r
365                 //Fields cannot deactivate, but Combos must collapse\r
366                 if(a.collapse){\r
367                     a.collapse();\r
368                 }\r
369             }else{\r
370                 a.deactivate();\r
371             }\r
372             delete this.activeItem;\r
373         }\r
374     },\r
375 \r
376     // private\r
377     tryActivate : function(start, step){\r
378         var items = this.items;\r
379         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){\r
380             var item = items.get(i);\r
381             if(!item.disabled && (item.canActivate || item.isFormField)){\r
382                 this.setActiveItem(item, false);\r
383                 return item;\r
384             }\r
385         }\r
386         return false;\r
387     },\r
388 \r
389     // private\r
390     onMouseOver : function(e){\r
391         var t = this.findTargetItem(e);\r
392         if(t){\r
393             if(t.canActivate && !t.disabled){\r
394                 this.setActiveItem(t, true);\r
395             }\r
396         }\r
397         this.over = true;\r
398         this.fireEvent('mouseover', this, e, t);\r
399     },\r
400 \r
401     // private\r
402     onMouseOut : function(e){\r
403         var t = this.findTargetItem(e);\r
404         if(t){\r
405             if(t == this.activeItem && t.shouldDeactivate && t.shouldDeactivate(e)){\r
406                 this.activeItem.deactivate();\r
407                 delete this.activeItem;\r
408             }\r
409         }\r
410         this.over = false;\r
411         this.fireEvent('mouseout', this, e, t);\r
412     },\r
413 \r
414     // private\r
415     onScroll : function(e, t){\r
416         if(e){\r
417             e.stopEvent();\r
418         }\r
419         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');\r
420         ul.scrollTop += this.scrollIncrement * (top ? -1 : 1);\r
421         if(top ? ul.scrollTop <= 0 : ul.scrollTop + this.activeMax >= ul.scrollHeight){\r
422            this.onScrollerOut(null, t);\r
423         }\r
424     },\r
425 \r
426     // private\r
427     onScrollerIn : function(e, t){\r
428         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');\r
429         if(top ? ul.scrollTop > 0 : ul.scrollTop + this.activeMax < ul.scrollHeight){\r
430             Ext.fly(t).addClass(['x-menu-item-active', 'x-menu-scroller-active']);\r
431         }\r
432     },\r
433 \r
434     // private\r
435     onScrollerOut : function(e, t){\r
436         Ext.fly(t).removeClass(['x-menu-item-active', 'x-menu-scroller-active']);\r
437     },\r
438 \r
439     /**\r
440      * If <code>{@link #floating}=true</code>, shows this menu relative to\r
441      * another element using {@link #showat}, otherwise uses {@link Ext.Component#show}.\r
442      * @param {Mixed} element The element to align to\r
443      * @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to\r
444      * the element (defaults to this.defaultAlign)\r
445      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)\r
446      */\r
447     show : function(el, pos, parentMenu){\r
448         if(this.floating){\r
449             this.parentMenu = parentMenu;\r
450             if(!this.el){\r
451                 this.render();\r
452                 this.doLayout(false, true);\r
453             }\r
454             this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign, this.defaultOffsets), parentMenu);\r
455         }else{\r
456             Ext.menu.Menu.superclass.show.call(this);\r
457         }\r
458     },\r
459 \r
460     /**\r
461      * Displays this menu at a specific xy position and fires the 'show' event if a\r
462      * handler for the 'beforeshow' event does not return false cancelling the operation.\r
463      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)\r
464      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)\r
465      */\r
466     showAt : function(xy, parentMenu){\r
467         if(this.fireEvent('beforeshow', this) !== false){\r
468             this.parentMenu = parentMenu;\r
469             if(!this.el){\r
470                 this.render();\r
471             }\r
472             if(this.enableScrolling){\r
473                 // set the position so we can figure out the constrain value.\r
474                 this.el.setXY(xy);\r
475                 //constrain the value, keep the y coordinate the same\r
476                 this.constrainScroll(xy[1]);\r
477                 xy = [this.el.adjustForConstraints(xy)[0], xy[1]];\r
478             }else{\r
479                 //constrain to the viewport.\r
480                 xy = this.el.adjustForConstraints(xy);\r
481             }\r
482             this.el.setXY(xy);\r
483             this.el.show();\r
484             Ext.menu.Menu.superclass.onShow.call(this);\r
485             if(Ext.isIE){\r
486                 // internal event, used so we don't couple the layout to the menu\r
487                 this.fireEvent('autosize', this);\r
488                 if(!Ext.isIE8){\r
489                     this.el.repaint();\r
490                 }\r
491             }\r
492             this.hidden = false;\r
493             this.focus();\r
494             this.fireEvent('show', this);\r
495         }\r
496     },\r
497 \r
498     constrainScroll : function(y){\r
499         var max, full = this.ul.setHeight('auto').getHeight();\r
500         if(this.floating){\r
501             max = this.maxHeight ? this.maxHeight : Ext.fly(this.el.dom.parentNode).getViewSize().height - y;\r
502         }else{\r
503             max = this.getHeight();\r
504         }\r
505         if(full > max && max > 0){\r
506             this.activeMax = max - this.scrollerHeight * 2 - this.el.getFrameWidth('tb') - Ext.num(this.el.shadowOffset, 0);\r
507             this.ul.setHeight(this.activeMax);\r
508             this.createScrollers();\r
509             this.el.select('.x-menu-scroller').setDisplayed('');\r
510         }else{\r
511             this.ul.setHeight(full);\r
512             this.el.select('.x-menu-scroller').setDisplayed('none');\r
513         }\r
514         this.ul.dom.scrollTop = 0;\r
515     },\r
516 \r
517     createScrollers : function(){\r
518         if(!this.scroller){\r
519             this.scroller = {\r
520                 pos: 0,\r
521                 top: this.el.insertFirst({\r
522                     tag: 'div',\r
523                     cls: 'x-menu-scroller x-menu-scroller-top',\r
524                     html: '&#160;'\r
525                 }),\r
526                 bottom: this.el.createChild({\r
527                     tag: 'div',\r
528                     cls: 'x-menu-scroller x-menu-scroller-bottom',\r
529                     html: '&#160;'\r
530                 })\r
531             };\r
532             this.scroller.top.hover(this.onScrollerIn, this.onScrollerOut, this);\r
533             this.scroller.topRepeater = new Ext.util.ClickRepeater(this.scroller.top, {\r
534                 listeners: {\r
535                     click: this.onScroll.createDelegate(this, [null, this.scroller.top], false)\r
536                 }\r
537             });\r
538             this.scroller.bottom.hover(this.onScrollerIn, this.onScrollerOut, this);\r
539             this.scroller.bottomRepeater = new Ext.util.ClickRepeater(this.scroller.bottom, {\r
540                 listeners: {\r
541                     click: this.onScroll.createDelegate(this, [null, this.scroller.bottom], false)\r
542                 }\r
543             });\r
544         }\r
545     },\r
546 \r
547     onLayout : function(){\r
548         if(this.isVisible()){\r
549             if(this.enableScrolling){\r
550                 this.constrainScroll(this.el.getTop());\r
551             }\r
552             if(this.floating){\r
553                 this.el.sync();\r
554             }\r
555         }\r
556     },\r
557 \r
558     focus : function(){\r
559         if(!this.hidden){\r
560             this.doFocus.defer(50, this);\r
561         }\r
562     },\r
563 \r
564     doFocus : function(){\r
565         if(!this.hidden){\r
566             this.focusEl.focus();\r
567         }\r
568     },\r
569 \r
570     /**\r
571      * Hides this menu and optionally all parent menus\r
572      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)\r
573      */\r
574     hide : function(deep){\r
575         this.deepHide = deep;\r
576         Ext.menu.Menu.superclass.hide.call(this);\r
577         delete this.deepHide;\r
578     },\r
579 \r
580     // private\r
581     onHide : function(){\r
582         Ext.menu.Menu.superclass.onHide.call(this);\r
583         this.deactivateActive();\r
584         if(this.el && this.floating){\r
585             this.el.hide();\r
586         }\r
587         var pm = this.parentMenu;\r
588         if(this.deepHide === true && pm){\r
589             if(pm.floating){\r
590                 pm.hide(true);\r
591             }else{\r
592                 pm.deactivateActive();\r
593             }\r
594         }\r
595     },\r
596 \r
597     // private\r
598     lookupComponent : function(c){\r
599          if(Ext.isString(c)){\r
600             c = (c == 'separator' || c == '-') ? new Ext.menu.Separator() : new Ext.menu.TextItem(c);\r
601              this.applyDefaults(c);\r
602          }else{\r
603             if(Ext.isObject(c)){\r
604                 c = this.getMenuItem(c);\r
605             }else if(c.tagName || c.el){ // element. Wrap it.\r
606                 c = new Ext.BoxComponent({\r
607                     el: c\r
608                 });\r
609             }\r
610          }\r
611          return c;\r
612     },\r
613 \r
614     applyDefaults : function(c){\r
615         if(!Ext.isString(c)){\r
616             c = Ext.menu.Menu.superclass.applyDefaults.call(this, c);\r
617             var d = this.internalDefaults;\r
618             if(d){\r
619                 if(c.events){\r
620                     Ext.applyIf(c.initialConfig, d);\r
621                     Ext.apply(c, d);\r
622                 }else{\r
623                     Ext.applyIf(c, d);\r
624                 }\r
625             }\r
626         }\r
627         return c;\r
628     },\r
629 \r
630     // private\r
631     getMenuItem : function(config){\r
632        if(!config.isXType){\r
633             if(!config.xtype && Ext.isBoolean(config.checked)){\r
634                 return new Ext.menu.CheckItem(config)\r
635             }\r
636             return Ext.create(config, this.defaultType);\r
637         }\r
638         return config;\r
639     },\r
640 \r
641     /**\r
642      * Adds a separator bar to the menu\r
643      * @return {Ext.menu.Item} The menu item that was added\r
644      */\r
645     addSeparator : function(){\r
646         return this.add(new Ext.menu.Separator());\r
647     },\r
648 \r
649     /**\r
650      * Adds an {@link Ext.Element} object to the menu\r
651      * @param {Mixed} el The element or DOM node to add, or its id\r
652      * @return {Ext.menu.Item} The menu item that was added\r
653      */\r
654     addElement : function(el){\r
655         return this.add(new Ext.menu.BaseItem(el));\r
656     },\r
657 \r
658     /**\r
659      * Adds an existing object based on {@link Ext.menu.BaseItem} to the menu\r
660      * @param {Ext.menu.Item} item The menu item to add\r
661      * @return {Ext.menu.Item} The menu item that was added\r
662      */\r
663     addItem : function(item){\r
664         return this.add(item);\r
665     },\r
666 \r
667     /**\r
668      * Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu\r
669      * @param {Object} config A MenuItem config object\r
670      * @return {Ext.menu.Item} The menu item that was added\r
671      */\r
672     addMenuItem : function(config){\r
673         return this.add(this.getMenuItem(config));\r
674     },\r
675 \r
676     /**\r
677      * Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu\r
678      * @param {String} text The text to display in the menu item\r
679      * @return {Ext.menu.Item} The menu item that was added\r
680      */\r
681     addText : function(text){\r
682         return this.add(new Ext.menu.TextItem(text));\r
683     },\r
684 \r
685     //private\r
686     onDestroy : function(){\r
687         Ext.menu.Menu.superclass.onDestroy.call(this);\r
688         Ext.menu.MenuMgr.unregister(this);\r
689         Ext.EventManager.removeResizeListener(this.hide, this);\r
690         if(this.keyNav) {\r
691             this.keyNav.disable();\r
692         }\r
693         var s = this.scroller;\r
694         if(s){\r
695             Ext.destroy(s.topRepeater, s.bottomRepeater, s.top, s.bottom);\r
696         }\r
697         Ext.destroy(\r
698             this.el,\r
699             this.focusEl,\r
700             this.ul\r
701         );\r
702     }\r
703 });\r
704 \r
705 Ext.reg('menu', Ext.menu.Menu);\r
706 \r
707 // MenuNav is a private utility class used internally by the Menu\r
708 Ext.menu.MenuNav = Ext.extend(Ext.KeyNav, function(){\r
709     function up(e, m){\r
710         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){\r
711             m.tryActivate(m.items.length-1, -1);\r
712         }\r
713     }\r
714     function down(e, m){\r
715         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){\r
716             m.tryActivate(0, 1);\r
717         }\r
718     }\r
719     return {\r
720         constructor : function(menu){\r
721             Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);\r
722             this.scope = this.menu = menu;\r
723         },\r
724 \r
725         doRelay : function(e, h){\r
726             var k = e.getKey();\r
727 //          Keystrokes within a form Field (e.g.: down in a Combo) do not navigate. Allow only TAB\r
728             if (this.menu.activeItem && this.menu.activeItem.isFormField && k != e.TAB) {\r
729                 return false;\r
730             }\r
731             if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){\r
732                 this.menu.tryActivate(0, 1);\r
733                 return false;\r
734             }\r
735             return h.call(this.scope || this, e, this.menu);\r
736         },\r
737 \r
738         tab: function(e, m) {\r
739             e.stopEvent();\r
740             if (e.shiftKey) {\r
741                 up(e, m);\r
742             } else {\r
743                 down(e, m);\r
744             }\r
745         },\r
746 \r
747         up : up,\r
748 \r
749         down : down,\r
750 \r
751         right : function(e, m){\r
752             if(m.activeItem){\r
753                 m.activeItem.expandMenu(true);\r
754             }\r
755         },\r
756 \r
757         left : function(e, m){\r
758             m.hide();\r
759             if(m.parentMenu && m.parentMenu.activeItem){\r
760                 m.parentMenu.activeItem.activate();\r
761             }\r
762         },\r
763 \r
764         enter : function(e, m){\r
765             if(m.activeItem){\r
766                 e.stopPropagation();\r
767                 m.activeItem.onClick(e);\r
768                 m.fireEvent('click', this, m.activeItem);\r
769                 return true;\r
770             }\r
771         }\r
772     };\r
773 }());\r
774 /**
775  * @class Ext.menu.MenuMgr
776  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
777  * @singleton
778  */
779 Ext.menu.MenuMgr = function(){
780    var menus, active, groups = {}, attached = false, lastShow = new Date();
781
782    // private - called when first menu is created
783    function init(){
784        menus = {};
785        active = new Ext.util.MixedCollection();
786        Ext.getDoc().addKeyListener(27, function(){
787            if(active.length > 0){
788                hideAll();
789            }
790        });
791    }
792
793    // private
794    function hideAll(){
795        if(active && active.length > 0){
796            var c = active.clone();
797            c.each(function(m){
798                m.hide();
799            });
800        }
801    }
802
803    // private
804    function onHide(m){
805        active.remove(m);
806        if(active.length < 1){
807            Ext.getDoc().un("mousedown", onMouseDown);
808            attached = false;
809        }
810    }
811
812    // private
813    function onShow(m){
814        var last = active.last();
815        lastShow = new Date();
816        active.add(m);
817        if(!attached){
818            Ext.getDoc().on("mousedown", onMouseDown);
819            attached = true;
820        }
821        if(m.parentMenu){
822           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
823           m.parentMenu.activeChild = m;
824        }else if(last && last.isVisible()){
825           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
826        }
827    }
828
829    // private
830    function onBeforeHide(m){
831        if(m.activeChild){
832            m.activeChild.hide();
833        }
834        if(m.autoHideTimer){
835            clearTimeout(m.autoHideTimer);
836            delete m.autoHideTimer;
837        }
838    }
839
840    // private
841    function onBeforeShow(m){
842        var pm = m.parentMenu;
843        if(!pm && !m.allowOtherMenus){
844            hideAll();
845        }else if(pm && pm.activeChild){
846            pm.activeChild.hide();
847        }
848    }
849
850    // private
851    function onMouseDown(e){
852        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
853            hideAll();
854        }
855    }
856
857    // private
858    function onBeforeCheck(mi, state){
859        if(state){
860            var g = groups[mi.group];
861            for(var i = 0, l = g.length; i < l; i++){
862                if(g[i] != mi){
863                    g[i].setChecked(false);
864                }
865            }
866        }
867    }
868
869    return {
870
871        /**
872         * Hides all menus that are currently visible
873         */
874        hideAll : function(){
875             hideAll();  
876        },
877
878        // private
879        register : function(menu){
880            if(!menus){
881                init();
882            }
883            menus[menu.id] = menu;
884            menu.on("beforehide", onBeforeHide);
885            menu.on("hide", onHide);
886            menu.on("beforeshow", onBeforeShow);
887            menu.on("show", onShow);
888            var g = menu.group;
889            if(g && menu.events["checkchange"]){
890                if(!groups[g]){
891                    groups[g] = [];
892                }
893                groups[g].push(menu);
894                menu.on("checkchange", onCheck);
895            }
896        },
897
898         /**
899          * Returns a {@link Ext.menu.Menu} object
900          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
901          * be used to generate and return a new Menu instance.
902          * @return {Ext.menu.Menu} The specified menu, or null if none are found
903          */
904        get : function(menu){
905            if(typeof menu == "string"){ // menu id
906                if(!menus){  // not initialized, no menus to return
907                    return null;
908                }
909                return menus[menu];
910            }else if(menu.events){  // menu instance
911                return menu;
912            }else if(typeof menu.length == 'number'){ // array of menu items?
913                return new Ext.menu.Menu({items:menu});
914            }else{ // otherwise, must be a config
915                return Ext.create(menu, 'menu');
916            }
917        },
918
919        // private
920        unregister : function(menu){
921            delete menus[menu.id];
922            menu.un("beforehide", onBeforeHide);
923            menu.un("hide", onHide);
924            menu.un("beforeshow", onBeforeShow);
925            menu.un("show", onShow);
926            var g = menu.group;
927            if(g && menu.events["checkchange"]){
928                groups[g].remove(menu);
929                menu.un("checkchange", onCheck);
930            }
931        },
932
933        // private
934        registerCheckable : function(menuItem){
935            var g = menuItem.group;
936            if(g){
937                if(!groups[g]){
938                    groups[g] = [];
939                }
940                groups[g].push(menuItem);
941                menuItem.on("beforecheckchange", onBeforeCheck);
942            }
943        },
944
945        // private
946        unregisterCheckable : function(menuItem){
947            var g = menuItem.group;
948            if(g){
949                groups[g].remove(menuItem);
950                menuItem.un("beforecheckchange", onBeforeCheck);
951            }
952        },
953
954        getCheckedItem : function(groupId){
955            var g = groups[groupId];
956            if(g){
957                for(var i = 0, l = g.length; i < l; i++){
958                    if(g[i].checked){
959                        return g[i];
960                    }
961                }
962            }
963            return null;
964        },
965
966        setCheckedItem : function(groupId, itemId){
967            var g = groups[groupId];
968            if(g){
969                for(var i = 0, l = g.length; i < l; i++){
970                    if(g[i].id == itemId){
971                        g[i].setChecked(true);
972                    }
973                }
974            }
975            return null;
976        }
977    };
978 }();
979 /**
980  * @class Ext.menu.BaseItem
981  * @extends Ext.Component
982  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
983  * management and base configuration options shared by all menu components.
984  * @constructor
985  * Creates a new BaseItem
986  * @param {Object} config Configuration options
987  * @xtype menubaseitem
988  */
989 Ext.menu.BaseItem = function(config){
990     Ext.menu.BaseItem.superclass.constructor.call(this, config);
991
992     this.addEvents(
993         /**
994          * @event click
995          * Fires when this item is clicked
996          * @param {Ext.menu.BaseItem} this
997          * @param {Ext.EventObject} e
998          */
999         'click',
1000         /**
1001          * @event activate
1002          * Fires when this item is activated
1003          * @param {Ext.menu.BaseItem} this
1004          */
1005         'activate',
1006         /**
1007          * @event deactivate
1008          * Fires when this item is deactivated
1009          * @param {Ext.menu.BaseItem} this
1010          */
1011         'deactivate'
1012     );
1013
1014     if(this.handler){
1015         this.on("click", this.handler, this.scope);
1016     }
1017 };
1018
1019 Ext.extend(Ext.menu.BaseItem, Ext.Component, {
1020     /**
1021      * @property parentMenu
1022      * @type Ext.menu.Menu
1023      * The parent Menu of this Item.
1024      */
1025     /**
1026      * @cfg {Function} handler
1027      * A function that will handle the click event of this menu item (optional).
1028      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
1029      * <li><code>b</code> : Item<div class="sub-desc">This menu Item.</div></li>
1030      * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
1031      * </ul></div>
1032      */
1033     /**
1034      * @cfg {Object} scope
1035      * The scope (<tt><b>this</b></tt> reference) in which the handler function will be called.
1036      */
1037     /**
1038      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
1039      */
1040     canActivate : false,
1041     /**
1042      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
1043      */
1044     activeClass : "x-menu-item-active",
1045     /**
1046      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
1047      */
1048     hideOnClick : true,
1049     /**
1050      * @cfg {Number} clickHideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
1051      */
1052     clickHideDelay : 1,
1053
1054     // private
1055     ctype : "Ext.menu.BaseItem",
1056
1057     // private
1058     actionMode : "container",
1059
1060     // private
1061     onRender : function(container, position){
1062         Ext.menu.BaseItem.superclass.onRender.apply(this, arguments);
1063         if(this.ownerCt && this.ownerCt instanceof Ext.menu.Menu){
1064             this.parentMenu = this.ownerCt;
1065         }else{
1066             this.container.addClass('x-menu-list-item');
1067             this.mon(this.el, 'click', this.onClick, this);
1068             this.mon(this.el, 'mouseenter', this.activate, this);
1069             this.mon(this.el, 'mouseleave', this.deactivate, this);
1070         }
1071     },
1072
1073     /**
1074      * Sets the function that will handle click events for this item (equivalent to passing in the {@link #handler}
1075      * config property).  If an existing handler is already registered, it will be unregistered for you.
1076      * @param {Function} handler The function that should be called on click
1077      * @param {Object} scope The scope that should be passed to the handler
1078      */
1079     setHandler : function(handler, scope){
1080         if(this.handler){
1081             this.un("click", this.handler, this.scope);
1082         }
1083         this.on("click", this.handler = handler, this.scope = scope);
1084     },
1085
1086     // private
1087     onClick : function(e){
1088         if(!this.disabled && this.fireEvent("click", this, e) !== false
1089                 && (this.parentMenu && this.parentMenu.fireEvent("itemclick", this, e) !== false)){
1090             this.handleClick(e);
1091         }else{
1092             e.stopEvent();
1093         }
1094     },
1095
1096     // private
1097     activate : function(){
1098         if(this.disabled){
1099             return false;
1100         }
1101         var li = this.container;
1102         li.addClass(this.activeClass);
1103         this.region = li.getRegion().adjust(2, 2, -2, -2);
1104         this.fireEvent("activate", this);
1105         return true;
1106     },
1107
1108     // private
1109     deactivate : function(){
1110         this.container.removeClass(this.activeClass);
1111         this.fireEvent("deactivate", this);
1112     },
1113
1114     // private
1115     shouldDeactivate : function(e){
1116         return !this.region || !this.region.contains(e.getPoint());
1117     },
1118
1119     // private
1120     handleClick : function(e){
1121         var pm = this.parentMenu;
1122         if(this.hideOnClick){
1123             if(pm.floating){
1124                 pm.hide.defer(this.clickHideDelay, pm, [true]);
1125             }else{
1126                 pm.deactivateActive();
1127             }
1128         }
1129     },
1130
1131     // private. Do nothing
1132     expandMenu : Ext.emptyFn,
1133
1134     // private. Do nothing
1135     hideMenu : Ext.emptyFn
1136 });
1137 Ext.reg('menubaseitem', Ext.menu.BaseItem);/**
1138  * @class Ext.menu.TextItem
1139  * @extends Ext.menu.BaseItem
1140  * Adds a static text string to a menu, usually used as either a heading or group separator.
1141  * @constructor
1142  * Creates a new TextItem
1143  * @param {Object/String} config If config is a string, it is used as the text to display, otherwise it
1144  * is applied as a config object (and should contain a <tt>text</tt> property).
1145  * @xtype menutextitem
1146  */
1147 Ext.menu.TextItem = function(cfg){
1148     if(typeof cfg == 'string'){
1149         cfg = {text: cfg}
1150     }
1151     Ext.menu.TextItem.superclass.constructor.call(this, cfg);
1152 };
1153
1154 Ext.extend(Ext.menu.TextItem, Ext.menu.BaseItem, {
1155     /**
1156      * @cfg {String} text The text to display for this item (defaults to '')
1157      */
1158     /**
1159      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
1160      */
1161     hideOnClick : false,
1162     /**
1163      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
1164      */
1165     itemCls : "x-menu-text",
1166
1167     // private
1168     onRender : function(){
1169         var s = document.createElement("span");
1170         s.className = this.itemCls;
1171         s.innerHTML = this.text;
1172         this.el = s;
1173         Ext.menu.TextItem.superclass.onRender.apply(this, arguments);
1174     }
1175 });
1176 Ext.reg('menutextitem', Ext.menu.TextItem);/**
1177  * @class Ext.menu.Separator
1178  * @extends Ext.menu.BaseItem
1179  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
1180  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
1181  * @constructor
1182  * @param {Object} config Configuration options
1183  * @xtype menuseparator
1184  */
1185 Ext.menu.Separator = function(config){
1186     Ext.menu.Separator.superclass.constructor.call(this, config);
1187 };
1188
1189 Ext.extend(Ext.menu.Separator, Ext.menu.BaseItem, {
1190     /**
1191      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
1192      */
1193     itemCls : "x-menu-sep",
1194     /**
1195      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
1196      */
1197     hideOnClick : false,
1198     
1199     /** 
1200      * @cfg {String} activeClass
1201      * @hide 
1202      */
1203     activeClass: '',
1204
1205     // private
1206     onRender : function(li){
1207         var s = document.createElement("span");
1208         s.className = this.itemCls;
1209         s.innerHTML = "&#160;";
1210         this.el = s;
1211         li.addClass("x-menu-sep-li");
1212         Ext.menu.Separator.superclass.onRender.apply(this, arguments);
1213     }
1214 });
1215 Ext.reg('menuseparator', Ext.menu.Separator);/**\r
1216  * @class Ext.menu.Item\r
1217  * @extends Ext.menu.BaseItem\r
1218  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static\r
1219  * display items.  Item extends the base functionality of {@link Ext.menu.BaseItem} by adding menu-specific\r
1220  * activation and click handling.\r
1221  * @constructor\r
1222  * Creates a new Item\r
1223  * @param {Object} config Configuration options\r
1224  * @xtype menuitem\r
1225  */\r
1226 Ext.menu.Item = function(config){\r
1227     Ext.menu.Item.superclass.constructor.call(this, config);\r
1228     if(this.menu){\r
1229         this.menu = Ext.menu.MenuMgr.get(this.menu);\r
1230     }\r
1231 };\r
1232 Ext.extend(Ext.menu.Item, Ext.menu.BaseItem, {\r
1233     /**\r
1234      * @property menu\r
1235      * @type Ext.menu.Menu\r
1236      * The submenu associated with this Item if one was configured.\r
1237      */\r
1238     /**\r
1239      * @cfg {Mixed} menu (optional) Either an instance of {@link Ext.menu.Menu} or the config object for an\r
1240      * {@link Ext.menu.Menu} which acts as the submenu when this item is activated.\r
1241      */\r
1242     /**\r
1243      * @cfg {String} icon The path to an icon to display in this item (defaults to Ext.BLANK_IMAGE_URL).  If\r
1244      * icon is specified {@link #iconCls} should not be.\r
1245      */\r
1246     /**\r
1247      * @cfg {String} iconCls A CSS class that specifies a background image that will be used as the icon for\r
1248      * this item (defaults to '').  If iconCls is specified {@link #icon} should not be.\r
1249      */\r
1250     /**\r
1251      * @cfg {String} text The text to display in this item (defaults to '').\r
1252      */\r
1253     /**\r
1254      * @cfg {String} href The href attribute to use for the underlying anchor link (defaults to '#').\r
1255      */\r
1256     /**\r
1257      * @cfg {String} hrefTarget The target attribute to use for the underlying anchor link (defaults to '').\r
1258      */\r
1259     /**\r
1260      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to 'x-menu-item')\r
1261      */\r
1262     itemCls : 'x-menu-item',\r
1263     /**\r
1264      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)\r
1265      */\r
1266     canActivate : true,\r
1267     /**\r
1268      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)\r
1269      */\r
1270     showDelay: 200,\r
1271     // doc'd in BaseItem\r
1272     hideDelay: 200,\r
1273 \r
1274     // private\r
1275     ctype: 'Ext.menu.Item',\r
1276 \r
1277     // private\r
1278     onRender : function(container, position){\r
1279         if (!this.itemTpl) {\r
1280             this.itemTpl = Ext.menu.Item.prototype.itemTpl = new Ext.XTemplate(\r
1281                 '<a id="{id}" class="{cls}" hidefocus="true" unselectable="on" href="{href}"',\r
1282                     '<tpl if="hrefTarget">',\r
1283                         ' target="{hrefTarget}"',\r
1284                     '</tpl>',\r
1285                  '>',\r
1286                      '<img src="{icon}" class="x-menu-item-icon {iconCls}"/>',\r
1287                      '<span class="x-menu-item-text">{text}</span>',\r
1288                  '</a>'\r
1289              );\r
1290         }\r
1291         var a = this.getTemplateArgs();\r
1292         this.el = position ? this.itemTpl.insertBefore(position, a, true) : this.itemTpl.append(container, a, true);\r
1293         this.iconEl = this.el.child('img.x-menu-item-icon');\r
1294         this.textEl = this.el.child('.x-menu-item-text');\r
1295         Ext.menu.Item.superclass.onRender.call(this, container, position);\r
1296     },\r
1297 \r
1298     getTemplateArgs: function() {\r
1299         return {\r
1300             id: this.id,\r
1301             cls: this.itemCls + (this.menu ?  ' x-menu-item-arrow' : '') + (this.cls ?  ' ' + this.cls : ''),\r
1302             href: this.href || '#',\r
1303             hrefTarget: this.hrefTarget,\r
1304             icon: this.icon || Ext.BLANK_IMAGE_URL,\r
1305             iconCls: this.iconCls || '',\r
1306             text: this.itemText||this.text||'&#160;'\r
1307         };\r
1308     },\r
1309 \r
1310     /**\r
1311      * Sets the text to display in this menu item\r
1312      * @param {String} text The text to display\r
1313      */\r
1314     setText : function(text){\r
1315         this.text = text||'&#160;';\r
1316         if(this.rendered){\r
1317             this.textEl.update(this.text);\r
1318             this.parentMenu.layout.doAutoSize();\r
1319         }\r
1320     },\r
1321 \r
1322     /**\r
1323      * Sets the CSS class to apply to the item's icon element\r
1324      * @param {String} cls The CSS class to apply\r
1325      */\r
1326     setIconClass : function(cls){\r
1327         var oldCls = this.iconCls;\r
1328         this.iconCls = cls;\r
1329         if(this.rendered){\r
1330             this.iconEl.replaceClass(oldCls, this.iconCls);\r
1331         }\r
1332     },\r
1333     \r
1334     //private\r
1335     beforeDestroy: function(){\r
1336         if (this.menu){\r
1337             this.menu.destroy();\r
1338         }\r
1339         Ext.menu.Item.superclass.beforeDestroy.call(this);\r
1340     },\r
1341 \r
1342     // private\r
1343     handleClick : function(e){\r
1344         if(!this.href){ // if no link defined, stop the event automatically\r
1345             e.stopEvent();\r
1346         }\r
1347         Ext.menu.Item.superclass.handleClick.apply(this, arguments);\r
1348     },\r
1349 \r
1350     // private\r
1351     activate : function(autoExpand){\r
1352         if(Ext.menu.Item.superclass.activate.apply(this, arguments)){\r
1353             this.focus();\r
1354             if(autoExpand){\r
1355                 this.expandMenu();\r
1356             }\r
1357         }\r
1358         return true;\r
1359     },\r
1360 \r
1361     // private\r
1362     shouldDeactivate : function(e){\r
1363         if(Ext.menu.Item.superclass.shouldDeactivate.call(this, e)){\r
1364             if(this.menu && this.menu.isVisible()){\r
1365                 return !this.menu.getEl().getRegion().contains(e.getPoint());\r
1366             }\r
1367             return true;\r
1368         }\r
1369         return false;\r
1370     },\r
1371 \r
1372     // private\r
1373     deactivate : function(){\r
1374         Ext.menu.Item.superclass.deactivate.apply(this, arguments);\r
1375         this.hideMenu();\r
1376     },\r
1377 \r
1378     // private\r
1379     expandMenu : function(autoActivate){\r
1380         if(!this.disabled && this.menu){\r
1381             clearTimeout(this.hideTimer);\r
1382             delete this.hideTimer;\r
1383             if(!this.menu.isVisible() && !this.showTimer){\r
1384                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);\r
1385             }else if (this.menu.isVisible() && autoActivate){\r
1386                 this.menu.tryActivate(0, 1);\r
1387             }\r
1388         }\r
1389     },\r
1390 \r
1391     // private\r
1392     deferExpand : function(autoActivate){\r
1393         delete this.showTimer;\r
1394         this.menu.show(this.container, this.parentMenu.subMenuAlign || 'tl-tr?', this.parentMenu);\r
1395         if(autoActivate){\r
1396             this.menu.tryActivate(0, 1);\r
1397         }\r
1398     },\r
1399 \r
1400     // private\r
1401     hideMenu : function(){\r
1402         clearTimeout(this.showTimer);\r
1403         delete this.showTimer;\r
1404         if(!this.hideTimer && this.menu && this.menu.isVisible()){\r
1405             this.hideTimer = this.deferHide.defer(this.hideDelay, this);\r
1406         }\r
1407     },\r
1408 \r
1409     // private\r
1410     deferHide : function(){\r
1411         delete this.hideTimer;\r
1412         if(this.menu.over){\r
1413             this.parentMenu.setActiveItem(this, false);\r
1414         }else{\r
1415             this.menu.hide();\r
1416         }\r
1417     }\r
1418 });\r
1419 Ext.reg('menuitem', Ext.menu.Item);/**
1420  * @class Ext.menu.CheckItem
1421  * @extends Ext.menu.Item
1422  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
1423  * @constructor
1424  * Creates a new CheckItem
1425  * @param {Object} config Configuration options
1426  * @xtype menucheckitem
1427  */
1428 Ext.menu.CheckItem = function(config){
1429     Ext.menu.CheckItem.superclass.constructor.call(this, config);
1430     this.addEvents(
1431         /**
1432          * @event beforecheckchange
1433          * Fires before the checked value is set, providing an opportunity to cancel if needed
1434          * @param {Ext.menu.CheckItem} this
1435          * @param {Boolean} checked The new checked value that will be set
1436          */
1437         "beforecheckchange" ,
1438         /**
1439          * @event checkchange
1440          * Fires after the checked value has been set
1441          * @param {Ext.menu.CheckItem} this
1442          * @param {Boolean} checked The checked value that was set
1443          */
1444         "checkchange"
1445     );
1446     /**
1447      * A function that handles the checkchange event.  The function is undefined by default, but if an implementation
1448      * is provided, it will be called automatically when the checkchange event fires.
1449      * @param {Ext.menu.CheckItem} this
1450      * @param {Boolean} checked The checked value that was set
1451      * @method checkHandler
1452      */
1453     if(this.checkHandler){
1454         this.on('checkchange', this.checkHandler, this.scope);
1455     }
1456     Ext.menu.MenuMgr.registerCheckable(this);
1457 };
1458 Ext.extend(Ext.menu.CheckItem, Ext.menu.Item, {
1459     /**
1460      * @cfg {String} group
1461      * All check items with the same group name will automatically be grouped into a single-select
1462      * radio button group (defaults to '')
1463      */
1464     /**
1465      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
1466      */
1467     itemCls : "x-menu-item x-menu-check-item",
1468     /**
1469      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
1470      */
1471     groupClass : "x-menu-group-item",
1472
1473     /**
1474      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
1475      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
1476      * initialized with checked = true will be rendered as checked.
1477      */
1478     checked: false,
1479
1480     // private
1481     ctype: "Ext.menu.CheckItem",
1482
1483     // private
1484     onRender : function(c){
1485         Ext.menu.CheckItem.superclass.onRender.apply(this, arguments);
1486         if(this.group){
1487             this.el.addClass(this.groupClass);
1488         }
1489         if(this.checked){
1490             this.checked = false;
1491             this.setChecked(true, true);
1492         }
1493     },
1494
1495     // private
1496     destroy : function(){
1497         Ext.menu.MenuMgr.unregisterCheckable(this);
1498         Ext.menu.CheckItem.superclass.destroy.apply(this, arguments);
1499     },
1500
1501     /**
1502      * Set the checked state of this item
1503      * @param {Boolean} checked The new checked value
1504      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
1505      */
1506     setChecked : function(state, suppressEvent){
1507         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
1508             if(this.container){
1509                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
1510             }
1511             this.checked = state;
1512             if(suppressEvent !== true){
1513                 this.fireEvent("checkchange", this, state);
1514             }
1515         }
1516     },
1517
1518     // private
1519     handleClick : function(e){
1520        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
1521            this.setChecked(!this.checked);
1522        }
1523        Ext.menu.CheckItem.superclass.handleClick.apply(this, arguments);
1524     }
1525 });
1526 Ext.reg('menucheckitem', Ext.menu.CheckItem);/**\r
1527  * @class Ext.menu.DateMenu\r
1528  * @extends Ext.menu.Menu\r
1529  * <p>A menu containing an {@link Ext.DatePicker} Component.</p>\r
1530  * <p>Notes:</p><div class="mdetail-params"><ul>\r
1531  * <li>Although not listed here, the <b>constructor</b> for this class\r
1532  * accepts all of the configuration options of <b>{@link Ext.DatePicker}</b>.</li>\r
1533  * <li>If subclassing DateMenu, any configuration options for the DatePicker must be\r
1534  * applied to the <tt><b>initialConfig</b></tt> property of the DateMenu.\r
1535  * Applying {@link Ext.DatePicker DatePicker} configuration settings to\r
1536  * <b><tt>this</tt></b> will <b>not</b> affect the DatePicker's configuration.</li>\r
1537  * </ul></div>\r
1538  * @xtype datemenu\r
1539  */\r
1540  Ext.menu.DateMenu = Ext.extend(Ext.menu.Menu, {\r
1541     /** \r
1542      * @cfg {Boolean} enableScrolling\r
1543      * @hide \r
1544      */\r
1545     enableScrolling : false,\r
1546     /**\r
1547      * @cfg {Function} handler\r
1548      * Optional. A function that will handle the select event of this menu.\r
1549      * The handler is passed the following parameters:<div class="mdetail-params"><ul>\r
1550      * <li><code>picker</code> : DatePicker<div class="sub-desc">The Ext.DatePicker.</div></li>\r
1551      * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>\r
1552      * </ul></div>\r
1553      */\r
1554     /**\r
1555      * @cfg {Object} scope\r
1556      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>\r
1557      * function will be called.  Defaults to this DateMenu instance.\r
1558      */    \r
1559     /** \r
1560      * @cfg {Boolean} hideOnClick\r
1561      * False to continue showing the menu after a date is selected, defaults to true.\r
1562      */\r
1563     hideOnClick : true,\r
1564     \r
1565     /** \r
1566      * @cfg {String} pickerId\r
1567      * An id to assign to the underlying date picker. Defaults to <tt>null</tt>.\r
1568      */\r
1569     pickerId : null,\r
1570     \r
1571     /** \r
1572      * @cfg {Number} maxHeight\r
1573      * @hide \r
1574      */\r
1575     /** \r
1576      * @cfg {Number} scrollIncrement\r
1577      * @hide \r
1578      */\r
1579     /**\r
1580      * The {@link Ext.DatePicker} instance for this DateMenu\r
1581      * @property picker\r
1582      * @type DatePicker\r
1583      */\r
1584     cls : 'x-date-menu',\r
1585     \r
1586     /**\r
1587      * @event click\r
1588      * @hide\r
1589      */\r
1590     \r
1591     /**\r
1592      * @event itemclick\r
1593      * @hide\r
1594      */\r
1595 \r
1596     initComponent : function(){\r
1597         this.on('beforeshow', this.onBeforeShow, this);\r
1598         if(this.strict = (Ext.isIE7 && Ext.isStrict)){\r
1599             this.on('show', this.onShow, this, {single: true, delay: 20});\r
1600         }\r
1601         Ext.apply(this, {\r
1602             plain: true,\r
1603             showSeparator: false,\r
1604             items: this.picker = new Ext.DatePicker(Ext.applyIf({\r
1605                 internalRender: this.strict || !Ext.isIE,\r
1606                 ctCls: 'x-menu-date-item',\r
1607                 id: this.pickerId\r
1608             }, this.initialConfig))\r
1609         });\r
1610         this.picker.purgeListeners();\r
1611         Ext.menu.DateMenu.superclass.initComponent.call(this);\r
1612         /**\r
1613          * @event select\r
1614          * Fires when a date is selected from the {@link #picker Ext.DatePicker}\r
1615          * @param {DatePicker} picker The {@link #picker Ext.DatePicker}\r
1616          * @param {Date} date The selected date\r
1617          */\r
1618         this.relayEvents(this.picker, ['select']);\r
1619         this.on('select', this.menuHide, this);\r
1620         if(this.handler){\r
1621             this.on('select', this.handler, this.scope || this);\r
1622         }\r
1623     },\r
1624 \r
1625     menuHide : function() {\r
1626         if(this.hideOnClick){\r
1627             this.hide(true);\r
1628         }\r
1629     },\r
1630 \r
1631     onBeforeShow : function(){\r
1632         if(this.picker){\r
1633             this.picker.hideMonthPicker(true);\r
1634         }\r
1635     },\r
1636 \r
1637     onShow : function(){\r
1638         var el = this.picker.getEl();\r
1639         el.setWidth(el.getWidth()); //nasty hack for IE7 strict mode\r
1640     }\r
1641  });\r
1642  Ext.reg('datemenu', Ext.menu.DateMenu);\r
1643  /**\r
1644  * @class Ext.menu.ColorMenu\r
1645  * @extends Ext.menu.Menu\r
1646  * <p>A menu containing a {@link Ext.ColorPalette} Component.</p>\r
1647  * <p>Notes:</p><div class="mdetail-params"><ul>\r
1648  * <li>Although not listed here, the <b>constructor</b> for this class\r
1649  * accepts all of the configuration options of <b>{@link Ext.ColorPalette}</b>.</li>\r
1650  * <li>If subclassing ColorMenu, any configuration options for the ColorPalette must be\r
1651  * applied to the <tt><b>initialConfig</b></tt> property of the ColorMenu.\r
1652  * Applying {@link Ext.ColorPalette ColorPalette} configuration settings to\r
1653  * <b><tt>this</tt></b> will <b>not</b> affect the ColorPalette's configuration.</li>\r
1654  * </ul></div> * \r
1655  * @xtype colormenu\r
1656  */\r
1657  Ext.menu.ColorMenu = Ext.extend(Ext.menu.Menu, {\r
1658     /** \r
1659      * @cfg {Boolean} enableScrolling\r
1660      * @hide \r
1661      */\r
1662     enableScrolling : false,\r
1663     /**\r
1664      * @cfg {Function} handler\r
1665      * Optional. A function that will handle the select event of this menu.\r
1666      * The handler is passed the following parameters:<div class="mdetail-params"><ul>\r
1667      * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>\r
1668      * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>\r
1669      * </ul></div>\r
1670      */\r
1671     /**\r
1672      * @cfg {Object} scope\r
1673      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>\r
1674      * function will be called.  Defaults to this ColorMenu instance.\r
1675      */    \r
1676     \r
1677     /** \r
1678      * @cfg {Boolean} hideOnClick\r
1679      * False to continue showing the menu after a color is selected, defaults to true.\r
1680      */\r
1681     hideOnClick : true,\r
1682     \r
1683     cls : 'x-color-menu',\r
1684     \r
1685     /** \r
1686      * @cfg {String} paletteId\r
1687      * An id to assign to the underlying color palette. Defaults to <tt>null</tt>.\r
1688      */\r
1689     paletteId : null,\r
1690     \r
1691     /** \r
1692      * @cfg {Number} maxHeight\r
1693      * @hide \r
1694      */\r
1695     /** \r
1696      * @cfg {Number} scrollIncrement\r
1697      * @hide \r
1698      */\r
1699     /**\r
1700      * @property palette\r
1701      * @type ColorPalette\r
1702      * The {@link Ext.ColorPalette} instance for this ColorMenu\r
1703      */\r
1704     \r
1705     \r
1706     /**\r
1707      * @event click\r
1708      * @hide\r
1709      */\r
1710     \r
1711     /**\r
1712      * @event itemclick\r
1713      * @hide\r
1714      */\r
1715     \r
1716     initComponent : function(){\r
1717         Ext.apply(this, {\r
1718             plain: true,\r
1719             showSeparator: false,\r
1720             items: this.palette = new Ext.ColorPalette(Ext.applyIf({\r
1721                 id: this.paletteId\r
1722             }, this.initialConfig))\r
1723         });\r
1724         this.palette.purgeListeners();\r
1725         Ext.menu.ColorMenu.superclass.initComponent.call(this);\r
1726         /**\r
1727          * @event select\r
1728          * Fires when a color is selected from the {@link #palette Ext.ColorPalette}\r
1729          * @param {Ext.ColorPalette} palette The {@link #palette Ext.ColorPalette}\r
1730          * @param {String} color The 6-digit color hex code (without the # symbol)\r
1731          */\r
1732         this.relayEvents(this.palette, ['select']);\r
1733         this.on('select', this.menuHide, this);\r
1734         if(this.handler){\r
1735             this.on('select', this.handler, this.scope || this);\r
1736         }\r
1737     },\r
1738 \r
1739     menuHide : function(){\r
1740         if(this.hideOnClick){\r
1741             this.hide(true);\r
1742         }\r
1743     }\r
1744 });\r
1745 Ext.reg('colormenu', Ext.menu.ColorMenu);\r