--- /dev/null
+<!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-menu.Menu-method-constructor'><span id='Ext-menu.Menu'>/**
+</span></span> * @class Ext.menu.Menu
+ * @extends Ext.panel.Panel
+ *
+ * A menu object. This is the container to which you may add {@link Ext.menu.Item menu items}.
+ *
+ * Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Components}.
+ * Menus may also contain {@link Ext.panel.AbstractPanel#dockedItems docked items} because it extends {@link Ext.panel.Panel}.
+ *
+ * To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items},
+ * specify `{@link Ext.menu.Item#iconCls iconCls}: 'no-icon'` _or_ `{@link Ext.menu.Item#indent indent}: true`.
+ * This reserves a space for an icon, and indents the Component in line with the other menu items.
+ * See {@link Ext.form.field.ComboBox}.{@link Ext.form.field.ComboBox#getListParent getListParent} for an example.
+
+ * By default, Menus are absolutely positioned, floating Components. By configuring a Menu with `{@link #floating}:false`,
+ * a Menu may be used as a child of a {@link Ext.container.Container Container}.
+ * {@img Ext.menu.Item/Ext.menu.Item.png Ext.menu.Item component}
+__Example Usage__
+ Ext.create('Ext.menu.Menu', {
+ width: 100,
+ height: 100,
+ margin: '0 0 10 0',
+ floating: false, // usually you want this set to True (default)
+ renderTo: Ext.getBody(), // usually rendered by it's containing component
+ items: [{
+ text: 'regular item 1'
+ },{
+ text: 'regular item 2'
+ },{
+ text: 'regular item 3'
+ }]
+ });
+
+ Ext.create('Ext.menu.Menu', {
+ width: 100,
+ height: 100,
+ plain: true,
+ floating: false, // usually you want this set to True (default)
+ renderTo: Ext.getBody(), // usually rendered by it's containing component
+ items: [{
+ text: 'plain item 1'
+ },{
+ text: 'plain item 2'
+ },{
+ text: 'plain item 3'
+ }]
+ });
+ * @xtype menu
+ * @markdown
+ * @constructor
+ * @param {Object} config The config object
+ */
+Ext.define('Ext.menu.Menu', {
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.menu',
+ requires: [
+ 'Ext.layout.container.Fit',
+ 'Ext.layout.container.VBox',
+ 'Ext.menu.CheckItem',
+ 'Ext.menu.Item',
+ 'Ext.menu.KeyNav',
+ 'Ext.menu.Manager',
+ 'Ext.menu.Separator'
+ ],
+
+<span id='Ext-menu.Menu-cfg-allowOtherMenus'> /**
+</span> * @cfg {Boolean} allowOtherMenus
+ * True to allow multiple menus to be displayed at the same time. Defaults to `false`.
+ * @markdown
+ */
+ allowOtherMenus: false,
+
+<span id='Ext-menu.Menu-cfg-ariaRole'> /**
+</span> * @cfg {String} ariaRole @hide
+ */
+ ariaRole: 'menu',
+
+<span id='Ext-menu.Menu-cfg-autoRender'> /**
+</span> * @cfg {Boolean} autoRender @hide
+ * floating is true, so autoRender always happens
+ */
+
+<span id='Ext-menu.Menu-cfg-defaultAlign'> /**
+</span> * @cfg {String} defaultAlign
+ * The default {@link Ext.core.Element#getAlignToXY Ext.core.Element#getAlignToXY} anchor position value for this menu
+ * relative to its element of origin. Defaults to `'tl-bl?'`.
+ * @markdown
+ */
+ defaultAlign: 'tl-bl?',
+
+<span id='Ext-menu.Menu-cfg-floating'> /**
+</span> * @cfg {Boolean} floating
+ * A Menu configured as `floating: true` (the default) will be rendered as an absolutely positioned,
+ * {@link Ext.Component#floating floating} {@link Ext.Component Component}. If configured as `floating: false`, the Menu may be
+ * used as a child item of another {@link Ext.container.Container Container}.
+ * @markdown
+ */
+ floating: true,
+
+<span id='Ext-menu.Menu-cfg-constrain'> /**
+</span> * @cfg {Boolean} @hide
+ * Menu performs its own size changing constraining, so ensure Component's constraining is not applied
+ */
+ constrain: false,
+
+<span id='Ext-menu.Menu-cfg-hidden'> /**
+</span> * @cfg {Boolean} hidden
+ * True to initially render the Menu as hidden, requiring to be shown manually.
+ * Defaults to `true` when `floating: true`, and defaults to `false` when `floating: false`.
+ * @markdown
+ */
+ hidden: true,
+
+<span id='Ext-menu.Menu-cfg-ignoreParentClicks'> /**
+</span> * @cfg {Boolean} ignoreParentClicks
+ * True to ignore clicks on any item in this menu that is a parent item (displays a submenu)
+ * so that the submenu is not dismissed when clicking the parent item. Defaults to `false`.
+ * @markdown
+ */
+ ignoreParentClicks: false,
+
+ isMenu: true,
+
+<span id='Ext-menu.Menu-cfg-layout'> /**
+</span> * @cfg {String/Object} layout @hide
+ */
+
+<span id='Ext-menu.Menu-cfg-showSeparator'> /**
+</span> * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).
+ */
+ showSeparator : true,
+
+<span id='Ext-menu.Menu-cfg-minWidth'> /**
+</span> * @cfg {Number} minWidth
+ * The minimum width of the Menu. Defaults to `120`.
+ * @markdown
+ */
+ minWidth: 120,
+
+<span id='Ext-menu.Menu-cfg-plain'> /**
+</span> * @cfg {Boolean} plain
+ * True to remove the incised line down the left side of the menu and to not
+ * indent general Component items. Defaults to `false`.
+ * @markdown
+ */
+
+ initComponent: function() {
+ var me = this,
+ prefix = Ext.baseCSSPrefix;
+
+ me.addEvents(
+<span id='Ext-menu.Menu-event-click'> /**
+</span> * @event click
+ * Fires when this menu is clicked
+ * @param {Ext.menu.Menu} menu The menu which has been clicked
+ * @param {Ext.Component} item The menu item that was clicked. `undefined` if not applicable.
+ * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}.
+ * @markdown
+ */
+ 'click',
+
+<span id='Ext-menu.Menu-event-mouseenter'> /**
+</span> * @event mouseenter
+ * Fires when the mouse enters this menu
+ * @param {Ext.menu.Menu} menu The menu
+ * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
+ * @markdown
+ */
+ 'mouseenter',
+
+<span id='Ext-menu.Menu-event-mouseleave'> /**
+</span> * @event mouseleave
+ * Fires when the mouse leaves this menu
+ * @param {Ext.menu.Menu} menu The menu
+ * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
+ * @markdown
+ */
+ 'mouseleave',
+
+<span id='Ext-menu.Menu-event-mouseover'> /**
+</span> * @event mouseover
+ * Fires when the mouse is hovering over this menu
+ * @param {Ext.menu.Menu} menu The menu
+ * @param {Ext.Component} item The menu item that the mouse is over. `undefined` if not applicable.
+ * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
+ */
+ 'mouseover'
+ );
+
+ Ext.menu.Manager.register(me);
+
+ // Menu classes
+ var cls = [prefix + 'menu'];
+ if (me.plain) {
+ cls.push(prefix + 'menu-plain');
+ }
+ me.cls = cls.join(' ');
+
+ // Menu body classes
+ var bodyCls = me.bodyCls ? [me.bodyCls] : [];
+ bodyCls.unshift(prefix + 'menu-body');
+ me.bodyCls = bodyCls.join(' ');
+
+ // Internal vbox layout, with scrolling overflow
+ // Placed in initComponent (rather than prototype) in order to support dynamic layout/scroller
+ // options if we wish to allow for such configurations on the Menu.
+ // e.g., scrolling speed, vbox align stretch, etc.
+ me.layout = {
+ type: 'vbox',
+ align: 'stretchmax',
+ autoSize: true,
+ clearInnerCtOnLayout: true,
+ overflowHandler: 'Scroller'
+ };
+
+ // hidden defaults to false if floating is configured as false
+ if (me.floating === false && me.initialConfig.hidden !== true) {
+ me.hidden = false;
+ }
+
+ me.callParent(arguments);
+
+ me.on('beforeshow', function() {
+ var hasItems = !!me.items.length;
+ // FIXME: When a menu has its show cancelled because of no items, it
+ // gets a visibility: hidden applied to it (instead of the default display: none)
+ // Not sure why, but we remove this style when we want to show again.
+ if (hasItems && me.rendered) {
+ me.el.setStyle('visibility', null);
+ }
+ return hasItems;
+ });
+ },
+
+ afterRender: function(ct) {
+ var me = this,
+ prefix = Ext.baseCSSPrefix,
+ space = '&#160;';
+
+ me.callParent(arguments);
+
+ // TODO: Move this to a subTemplate When we support them in the future
+ if (me.showSeparator) {
+ me.iconSepEl = me.layout.getRenderTarget().insertFirst({
+ cls: prefix + 'menu-icon-separator',
+ html: space
+ });
+ }
+
+ me.focusEl = me.el.createChild({
+ cls: prefix + 'menu-focus',
+ tabIndex: '-1',
+ html: space
+ });
+
+ me.mon(me.el, {
+ click: me.onClick,
+ mouseover: me.onMouseOver,
+ scope: me
+ });
+ me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me);
+
+ if (me.showSeparator && ((!Ext.isStrict && Ext.isIE) || Ext.isIE6)) {
+ me.iconSepEl.setHeight(me.el.getHeight());
+ }
+
+ me.keyNav = Ext.create('Ext.menu.KeyNav', me);
+ },
+
+ afterLayout: function() {
+ var me = this;
+ me.callParent(arguments);
+
+ // For IE6 & IE quirks, we have to resize the el and body since position: absolute
+ // floating elements inherit their parent's width, making them the width of
+ // document.body instead of the width of their contents.
+ // This includes left/right dock items.
+ if ((!Ext.iStrict && Ext.isIE) || Ext.isIE6) {
+ var innerCt = me.layout.getRenderTarget(),
+ innerCtWidth = 0,
+ dis = me.dockedItems,
+ l = dis.length,
+ i = 0,
+ di, clone, newWidth;
+
+ innerCtWidth = innerCt.getWidth();
+
+ newWidth = innerCtWidth + me.body.getBorderWidth('lr') + me.body.getPadding('lr');
+
+ // First set the body to the new width
+ me.body.setWidth(newWidth);
+
+ // Now we calculate additional width (docked items) and set the el's width
+ for (; i < l, di = dis.getAt(i); i++) {
+ if (di.dock == 'left' || di.dock == 'right') {
+ newWidth += di.getWidth();
+ }
+ }
+ me.el.setWidth(newWidth);
+ }
+ },
+
+<span id='Ext-menu.Menu-method-canActivateItem'> /**
+</span> * Returns whether a menu item can be activated or not.
+ * @return {Boolean}
+ */
+ canActivateItem: function(item) {
+ return item && !item.isDisabled() && item.isVisible() && (item.canActivate || item.getXTypes().indexOf('menuitem') < 0);
+ },
+
+<span id='Ext-menu.Menu-method-deactivateActiveItem'> /**
+</span> * Deactivates the current active item on the menu, if one exists.
+ */
+ deactivateActiveItem: function() {
+ var me = this;
+
+ if (me.activeItem) {
+ me.activeItem.deactivate();
+ if (!me.activeItem.activated) {
+ delete me.activeItem;
+ }
+ }
+ if (me.focusedItem) {
+ me.focusedItem.blur();
+ if (!me.focusedItem.$focused) {
+ delete me.focusedItem;
+ }
+ }
+ },
+
+ // inherit docs
+ getFocusEl: function() {
+ return this.focusEl;
+ },
+
+ // inherit docs
+ hide: function() {
+ this.deactivateActiveItem();
+ this.callParent(arguments);
+ },
+
+ // private
+ getItemFromEvent: function(e) {
+ return this.getChildByElement(e.getTarget());
+ },
+
+ lookupComponent: function(cmp) {
+ var me = this;
+
+ if (Ext.isString(cmp)) {
+ cmp = me.lookupItemFromString(cmp);
+ } else if (Ext.isObject(cmp)) {
+ cmp = me.lookupItemFromObject(cmp);
+ }
+
+ // Apply our minWidth to all of our child components so it's accounted
+ // for in our VBox layout
+ cmp.minWidth = cmp.minWidth || me.minWidth;
+
+ return cmp;
+ },
+
+ // private
+ lookupItemFromObject: function(cmp) {
+ var me = this,
+ prefix = Ext.baseCSSPrefix;
+
+ if (!cmp.isComponent) {
+ if (!cmp.xtype) {
+ cmp = Ext.create('Ext.menu.' + (Ext.isBoolean(cmp.checked) ? 'Check': '') + 'Item', cmp);
+ } else {
+ cmp = Ext.ComponentManager.create(cmp, cmp.xtype);
+ }
+ }
+
+ if (cmp.isMenuItem) {
+ cmp.parentMenu = me;
+ }
+
+ if (!cmp.isMenuItem && !cmp.dock) {
+ var cls = [
+ prefix + 'menu-item',
+ prefix + 'menu-item-cmp'
+ ],
+ intercept = Ext.Function.createInterceptor;
+
+ // Wrap focus/blur to control component focus
+ cmp.focus = intercept(cmp.focus, function() {
+ this.$focused = true;
+ }, cmp);
+ cmp.blur = intercept(cmp.blur, function() {
+ this.$focused = false;
+ }, cmp);
+
+ if (!me.plain && (cmp.indent === true || cmp.iconCls === 'no-icon')) {
+ cls.push(prefix + 'menu-item-indent');
+ }
+
+ if (cmp.rendered) {
+ cmp.el.addCls(cls);
+ } else {
+ cmp.cls = (cmp.cls ? cmp.cls : '') + ' ' + cls.join(' ');
+ }
+ cmp.isMenuItem = true;
+ }
+ return cmp;
+ },
+
+ // private
+ lookupItemFromString: function(cmp) {
+ return (cmp == 'separator' || cmp == '-') ?
+ Ext.createWidget('menuseparator')
+ : Ext.createWidget('menuitem', {
+ canActivate: false,
+ hideOnClick: false,
+ plain: true,
+ text: cmp
+ });
+ },
+
+ onClick: function(e) {
+ var me = this,
+ item;
+
+ if (me.disabled) {
+ e.stopEvent();
+ return;
+ }
+
+ if ((e.getTarget() == me.focusEl.dom) || e.within(me.layout.getRenderTarget())) {
+ item = me.getItemFromEvent(e) || me.activeItem;
+
+ if (item) {
+ if (item.getXTypes().indexOf('menuitem') >= 0) {
+ if (!item.menu || !me.ignoreParentClicks) {
+ item.onClick(e);
+ } else {
+ e.stopEvent();
+ }
+ }
+ }
+ me.fireEvent('click', me, item, e);
+ }
+ },
+
+ onDestroy: function() {
+ var me = this;
+
+ Ext.menu.Manager.unregister(me);
+ if (me.rendered) {
+ me.el.un(me.mouseMonitor);
+ me.keyNav.destroy();
+ delete me.keyNav;
+ }
+ me.callParent(arguments);
+ },
+
+ onMouseLeave: function(e) {
+ var me = this;
+
+ me.deactivateActiveItem();
+
+ if (me.disabled) {
+ return;
+ }
+
+ me.fireEvent('mouseleave', me, e);
+ },
+
+ onMouseOver: function(e) {
+ var me = this,
+ fromEl = e.getRelatedTarget(),
+ mouseEnter = !me.el.contains(fromEl),
+ item = me.getItemFromEvent(e);
+
+ if (mouseEnter && me.parentMenu) {
+ me.parentMenu.setActiveItem(me.parentItem);
+ me.parentMenu.mouseMonitor.mouseenter();
+ }
+
+ if (me.disabled) {
+ return;
+ }
+
+ if (item) {
+ me.setActiveItem(item);
+ if (item.activated && item.expandMenu) {
+ item.expandMenu();
+ }
+ }
+ if (mouseEnter) {
+ me.fireEvent('mouseenter', me, e);
+ }
+ me.fireEvent('mouseover', me, item, e);
+ },
+
+ setActiveItem: function(item) {
+ var me = this;
+
+ if (item && (item != me.activeItem && item != me.focusedItem)) {
+ me.deactivateActiveItem();
+ if (me.canActivateItem(item)) {
+ if (item.activate) {
+ item.activate();
+ if (item.activated) {
+ me.activeItem = item;
+ me.focusedItem = item;
+ me.focus();
+ }
+ } else {
+ item.focus();
+ me.focusedItem = item;
+ }
+ }
+ item.el.scrollIntoView(me.layout.getRenderTarget());
+ }
+ },
+
+<span id='Ext-menu.Menu-method-showBy'> /**
+</span> * Shows the floating menu by the specified {@link Ext.Component Component} or {@link Ext.core.Element Element}.
+ * @param {Mixed component} The {@link Ext.Component} or {@link Ext.core.Element} to show the menu by.
+ * @param {String} position (optional) Alignment position as used by {@link Ext.core.Element#getAlignToXY Ext.core.Element.getAlignToXY}. Defaults to `{@link #defaultAlign}`.
+ * @param {Array} offsets (optional) Alignment offsets as used by {@link Ext.core.Element#getAlignToXY Ext.core.Element.getAlignToXY}. Defaults to `undefined`.
+ * @return {Menu} This Menu.
+ * @markdown
+ */
+ showBy: function(cmp, pos, off) {
+ var me = this;
+
+ if (me.floating && cmp) {
+ me.layout.autoSize = true;
+ me.show();
+
+ // Component or Element
+ cmp = cmp.el || cmp;
+
+ // Convert absolute to floatParent-relative coordinates if necessary.
+ var xy = me.el.getAlignToXY(cmp, pos || me.defaultAlign, off);
+ if (me.floatParent) {
+ var r = me.floatParent.getTargetEl().getViewRegion();
+ xy[0] -= r.x;
+ xy[1] -= r.y;
+ }
+ me.showAt(xy);
+ me.doConstrain();
+ }
+ return me;
+ },
+
+ doConstrain : function() {
+ var me = this,
+ y = this.el.getY(),
+ max, full,
+ returnY = y, normalY, parentEl, scrollTop, viewHeight;
+
+ delete me.height;
+ me.setSize();
+ full = me.getHeight();
+ if (me.floating) {
+ parentEl = Ext.fly(me.el.dom.parentNode);
+ scrollTop = parentEl.getScroll().top;
+ viewHeight = parentEl.getViewSize().height;
+ //Normalize y by the scroll position for the parent element. Need to move it into the coordinate space
+ //of the view.
+ normalY = y - scrollTop;
+ max = me.maxHeight ? me.maxHeight : viewHeight - normalY;
+ if (full > viewHeight) {
+ max = viewHeight;
+ //Set returnY equal to (0,0) in view space by reducing y by the value of normalY
+ returnY = y - normalY;
+ } else if (max < full) {
+ returnY = y - (full - max);
+ max = full;
+ }
+ }else{
+ max = me.getHeight();
+ }
+ // Always respect maxHeight
+ if (me.maxHeight){
+ max = Math.min(me.maxHeight, max);
+ }
+ if (full > max && max > 0){
+ me.layout.autoSize = false;
+ me.setHeight(max);
+ if (me.showSeparator){
+ me.iconSepEl.setHeight(me.layout.getRenderTarget().dom.scrollHeight);
+ }
+ }
+ me.el.setY(returnY);
+ }
+});</pre></pre></body></html>
\ No newline at end of file