Menu.js
上传用户:dawnssy
上传日期:2022-08-06
资源大小:9345k
文件大小:26k
源码类别:

JavaScript

开发平台:

JavaScript

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