<!DOCTYPE html>
|
<html>
|
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>The source code</title>
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
<style type="text/css">
|
.highlight { display: block; background-color: #ddd; }
|
</style>
|
<script type="text/javascript">
|
function highlight() {
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
}
|
</script>
|
</head>
|
<body onload="prettyPrint(); highlight();">
|
<pre class="prettyprint lang-js"><span id='Ext-menu-Menu'>/**
|
</span> * 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}.
|
*
|
* By default, non {@link Ext.menu.Item menu items} are indented so that they line up with the text of menu items. clearing
|
* the icon column. To make a contained general {@link Ext.Component Component} left aligned configure the child
|
* Component with `indent: false.
|
*
|
* 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}.
|
*
|
* @example
|
* Ext.create('Ext.menu.Menu', {
|
* width: 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,
|
* 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'
|
* }]
|
* });
|
*/
|
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-property-parentMenu'> /**
|
</span> * @property {Ext.menu.Menu} parentMenu
|
* The parent Menu of this Menu.
|
*/
|
|
<span id='Ext-menu-Menu-cfg-enableKeyNav'> /**
|
</span> * @cfg {Boolean} [enableKeyNav=true]
|
* True to enable keyboard navigation for controlling the menu.
|
* This option should generally be disabled when form fields are
|
* being used inside the menu.
|
*/
|
enableKeyNav: true,
|
|
<span id='Ext-menu-Menu-cfg-allowOtherMenus'> /**
|
</span> * @cfg {Boolean} [allowOtherMenus=false]
|
* True to allow multiple menus to be displayed at the same time.
|
*/
|
allowOtherMenus: false,
|
|
<span id='Ext-menu-Menu-cfg-ariaRole'> /**
|
</span> * @cfg {String} ariaRole
|
* @private
|
*/
|
ariaRole: 'menu',
|
|
<span id='Ext-menu-Menu-cfg-autoRender'> /**
|
</span> * @cfg {Boolean} autoRender
|
* Floating is true, so autoRender always happens.
|
* @private
|
*/
|
|
<span id='Ext-menu-Menu-cfg-floating'> /**
|
</span> * @cfg {Boolean} [floating=true]
|
* 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}.
|
*/
|
floating: true,
|
|
<span id='Ext-menu-Menu-cfg-constrain'> /**
|
</span> * @cfg {Boolean} constrain
|
* Menus are constrained to the document body by default.
|
* @private
|
*/
|
constrain: true,
|
|
<span id='Ext-menu-Menu-cfg-hidden'> /**
|
</span> * @cfg {Boolean} [hidden=undefined]
|
* 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`.
|
*/
|
hidden: true,
|
|
<span id='Ext-menu-Menu-cfg-hideMode'> hideMode: 'visibility',
|
</span>
|
<span id='Ext-menu-Menu-cfg-ignoreParentClicks'> /**
|
</span> * @cfg {Boolean} [ignoreParentClicks=false]
|
* 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.
|
*/
|
ignoreParentClicks: false,
|
|
<span id='Ext-menu-Menu-property-isMenu'> /**
|
</span> * @property {Boolean} isMenu
|
* `true` in this class to identify an object as an instantiated Menu, or subclass thereof.
|
*/
|
isMenu: true,
|
|
<span id='Ext-menu-Menu-cfg-layout'> /**
|
</span> * @cfg {Ext.enums.Layout/Object} layout
|
* @private
|
*/
|
|
<span id='Ext-menu-Menu-cfg-showSeparator'> /**
|
</span> * @cfg {Boolean} [showSeparator=true]
|
* True to show the icon separator.
|
*/
|
showSeparator : true,
|
|
<span id='Ext-menu-Menu-cfg-minWidth'> /**
|
</span> * @cfg {Number} [minWidth=120]
|
* The minimum width of the Menu. The default minWidth only applies when the {@link #floating} config is true.
|
*/
|
minWidth: undefined,
|
|
<span id='Ext-menu-Menu-property-defaultMinWidth'> defaultMinWidth: 120,
|
</span>
|
<span id='Ext-menu-Menu-cfg-plain'> /**
|
</span> * @cfg {Boolean} [plain=false]
|
* True to remove the incised line down the left side of the menu and to not indent general Component items.
|
*
|
* {@link Ext.menu.Item MenuItem}s will *always* have space at their start for an icon. With the `plain` setting,
|
* non {@link Ext.menu.Item MenuItem} child components will not be indented to line up.
|
*
|
* Basically, `plain:true` makes a Menu behave more like a regular {@link Ext.layout.container.HBox HBox layout}
|
* {@link Ext.panel.Panel Panel} which just has the same background as a Menu.
|
*
|
* See also the {@link #showSeparator} config.
|
*/
|
|
initComponent: function() {
|
var me = this,
|
prefix = Ext.baseCSSPrefix,
|
cls = [prefix + 'menu'],
|
bodyCls = me.bodyCls ? [me.bodyCls] : [],
|
isFloating = me.floating !== false;
|
|
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}.
|
*/
|
'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}
|
*/
|
'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}
|
*/
|
'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
|
if (me.plain) {
|
cls.push(prefix + 'menu-plain');
|
}
|
me.cls = cls.join(' ');
|
|
// Menu body classes
|
bodyCls.push(prefix + 'menu-body', Ext.dom.Element.unselectableCls);
|
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.
|
if (!me.layout) {
|
me.layout = {
|
type: 'vbox',
|
align: 'stretchmax',
|
overflowHandler: 'Scroller'
|
};
|
}
|
|
if (isFloating) {
|
// only apply the minWidth when we're floating & one hasn't already been set
|
if (me.minWidth === undefined) {
|
me.minWidth = me.defaultMinWidth;
|
}
|
} else {
|
// hidden defaults to false if floating is configured as false
|
me.hidden = !!me.initialConfig.hidden;
|
me.constrain = false;
|
}
|
|
me.callParent(arguments);
|
},
|
|
<span id='Ext-menu-Menu-method-registerWithOwnerCt'> // Private implementation for Menus. They are a special case.
|
</span> // They are always global floaters, never contained.
|
registerWithOwnerCt: function() {
|
if (this.floating) {
|
this.ownerCt = null;
|
Ext.WindowManager.register(this);
|
}
|
},
|
|
<span id='Ext-menu-Menu-method-initHierarchyEvents'> // Menus do not have owning containers on which they depend for visibility. They stand outside
|
</span> // any container hierarchy.
|
initHierarchyEvents: Ext.emptyFn,
|
|
<span id='Ext-menu-Menu-method-isVisible'> // Menus are never contained, and must not ascertain their visibility from the ancestor hierarchy
|
</span> isVisible: function() {
|
return this.callParent();
|
},
|
|
<span id='Ext-menu-Menu-method-getHierarchyState'> // As menus are never contained, a Menu's visibility only ever depends upon its own hidden state.
|
</span> // Ignore hiddenness from the ancestor hierarchy, override it with local hidden state.
|
getHierarchyState: function() {
|
var result = this.callParent();
|
result.hidden = this.hidden;
|
return result;
|
},
|
|
<span id='Ext-menu-Menu-method-beforeRender'> beforeRender: function() {
|
</span> this.callParent(arguments);
|
|
// Menus are usually floating: true, which means they shrink wrap their items.
|
// However, when they are contained, and not auto sized, we must stretch the items.
|
if (!this.getSizeModel().width.shrinkWrap) {
|
this.layout.align = 'stretch';
|
}
|
},
|
|
<span id='Ext-menu-Menu-method-onBoxReady'> onBoxReady: function() {
|
</span> var me = this;
|
|
me.callParent(arguments);
|
|
// TODO: Move this to a subTemplate When we support them in the future
|
if (me.showSeparator) {
|
me.iconSepEl = me.layout.getElementTarget().insertFirst({
|
cls: Ext.baseCSSPrefix + 'menu-icon-separator',
|
html: '&#160;'
|
});
|
}
|
|
me.mon(me.el, {
|
click: me.onClick,
|
mouseover: me.onMouseOver,
|
scope: me
|
});
|
me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me);
|
|
// A Menu is a Panel. The KeyNav can use the Panel's KeyMap
|
if (me.enableKeyNav) {
|
me.keyNav = new Ext.menu.KeyNav({
|
target: me,
|
keyMap: me.getKeyMap()
|
});
|
}
|
},
|
|
<span id='Ext-menu-Menu-method-getRefOwner'> getRefOwner: function() {
|
</span> // If a submenu, this will have a parentMenu property
|
// If a menu of a Button, it will have an ownerButton property
|
// Else use the default method.
|
return this.parentMenu || this.ownerButton || this.callParent(arguments);
|
},
|
|
<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(andBlurFocusedItem) {
|
var me = this,
|
activeItem = me.activeItem,
|
focusedItem = me.focusedItem;
|
|
if (activeItem) {
|
activeItem.deactivate();
|
if (!activeItem.activated) {
|
delete me.activeItem;
|
}
|
}
|
|
// Blur the focused item if we are being asked to do that too
|
// Only needed if we are being hidden - mouseout does not blur.
|
if (focusedItem && andBlurFocusedItem) {
|
focusedItem.blur();
|
delete me.focusedItem;
|
}
|
},
|
|
<span id='Ext-menu-Menu-method-getFocusEl'> // @inheritdoc
|
</span> getFocusEl: function() {
|
return this.focusedItem || this.el;
|
},
|
|
<span id='Ext-menu-Menu-method-hide'> // @inheritdoc
|
</span> hide: function() {
|
this.deactivateActiveItem(true);
|
this.callParent(arguments);
|
},
|
|
<span id='Ext-menu-Menu-method-getItemFromEvent'> // @private
|
</span> getItemFromEvent: function(e) {
|
return this.getChildByElement(e.getTarget());
|
},
|
|
<span id='Ext-menu-Menu-method-lookupComponent'> lookupComponent: function(cmp) {
|
</span> var me = this;
|
|
if (typeof cmp == 'string') {
|
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;
|
},
|
|
<span id='Ext-menu-Menu-method-lookupItemFromObject'> // @private
|
</span> lookupItemFromObject: function(cmp) {
|
var me = this,
|
prefix = Ext.baseCSSPrefix,
|
cls;
|
|
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) {
|
cls = [prefix + 'menu-item-cmp'];
|
|
// The "plain" setting means that the menu does not look so much like a menu. It's more like a grey Panel.
|
// So it has no vertical separator.
|
// Plain menus also will not indent non MenuItem components; there is nothing to indent them to the right of.
|
if (!me.plain && (cmp.indent !== false || cmp.iconCls === 'no-icon')) {
|
cls.push(prefix + 'menu-item-indent');
|
}
|
|
if (cmp.rendered) {
|
cmp.el.addCls(cls);
|
} else {
|
cmp.cls = (cmp.cls || '') + ' ' + cls.join(' ');
|
}
|
}
|
return cmp;
|
},
|
|
<span id='Ext-menu-Menu-method-lookupItemFromString'> // @private
|
</span> lookupItemFromString: function(cmp) {
|
return (cmp == 'separator' || cmp == '-') ?
|
new Ext.menu.Separator()
|
: new Ext.menu.Item({
|
canActivate: false,
|
hideOnClick: false,
|
plain: true,
|
text: cmp
|
});
|
},
|
|
<span id='Ext-menu-Menu-method-onClick'> onClick: function(e) {
|
</span> var me = this,
|
item;
|
|
if (me.disabled) {
|
e.stopEvent();
|
return;
|
}
|
|
item = (e.type === 'click') ? me.getItemFromEvent(e) : me.activeItem;
|
if (item && item.isMenuItem) {
|
if (!item.menu || !me.ignoreParentClicks) {
|
item.onClick(e);
|
} else {
|
e.stopEvent();
|
}
|
}
|
// Click event may be fired without an item, so we need a second check
|
if (!item || item.disabled) {
|
item = undefined;
|
}
|
me.fireEvent('click', me, item, e);
|
},
|
|
<span id='Ext-menu-Menu-method-onDestroy'> onDestroy: function() {
|
</span> var me = this;
|
|
Ext.menu.Manager.unregister(me);
|
me.parentMenu = me.ownerButton = null;
|
if (me.rendered) {
|
me.el.un(me.mouseMonitor);
|
Ext.destroy(me.keyNav);
|
me.keyNav = null;
|
}
|
me.callParent(arguments);
|
},
|
|
<span id='Ext-menu-Menu-method-onMouseLeave'> onMouseLeave: function(e) {
|
</span> var me = this;
|
|
me.deactivateActiveItem();
|
|
if (me.disabled) {
|
return;
|
}
|
|
me.fireEvent('mouseleave', me, e);
|
},
|
|
<span id='Ext-menu-Menu-method-onMouseOver'> onMouseOver: function(e) {
|
</span> var me = this,
|
fromEl = e.getRelatedTarget(),
|
mouseEnter = !me.el.contains(fromEl),
|
item = me.getItemFromEvent(e),
|
parentMenu = me.parentMenu,
|
parentItem = me.parentItem;
|
|
if (mouseEnter && parentMenu) {
|
parentMenu.setActiveItem(parentItem);
|
parentItem.cancelDeferHide();
|
parentMenu.mouseMonitor.mouseenter();
|
}
|
|
if (me.disabled) {
|
return;
|
}
|
|
// Do not activate the item if the mouseover was within the item, and it's already active
|
if (item && !item.activated) {
|
me.setActiveItem(item);
|
if (item.activated && item.expandMenu) {
|
item.expandMenu();
|
}
|
}
|
if (mouseEnter) {
|
me.fireEvent('mouseenter', me, e);
|
}
|
me.fireEvent('mouseover', me, item, e);
|
},
|
|
<span id='Ext-menu-Menu-method-setActiveItem'> setActiveItem: function(item) {
|
</span> var me = this;
|
|
if (item && (item != me.activeItem)) {
|
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'> showBy: function(cmp, pos, off) {
|
</span> var me = this;
|
|
me.callParent(arguments);
|
if (!me.hidden) {
|
// show may have been vetoed
|
me.setVerticalPosition();
|
}
|
return me;
|
},
|
|
<span id='Ext-menu-Menu-method-beforeShow'> beforeShow: function() {
|
</span> var me = this,
|
viewHeight;
|
|
// Constrain the height to the containing element's viewable area
|
if (me.floating) {
|
me.savedMaxHeight = me.maxHeight;
|
viewHeight = me.container.getViewSize().height;
|
me.maxHeight = Math.min(me.maxHeight || viewHeight, viewHeight);
|
}
|
|
me.callParent(arguments);
|
},
|
|
<span id='Ext-menu-Menu-method-afterShow'> afterShow: function() {
|
</span> var me = this;
|
|
me.callParent(arguments);
|
|
// Restore configured maxHeight
|
if (me.floating) {
|
me.maxHeight = me.savedMaxHeight;
|
}
|
},
|
|
<span id='Ext-menu-Menu-method-setVerticalPosition'> // @private
|
</span> // adjust the vertical position of the menu if the height of the
|
// menu is equal (or greater than) the viewport size
|
setVerticalPosition: function() {
|
var me = this,
|
max,
|
y = me.getY(),
|
returnY = y,
|
height = me.getHeight(),
|
viewportHeight = Ext.Element.getViewportHeight().height,
|
parentEl = me.el.parent(),
|
viewHeight = parentEl.getViewSize().height,
|
normalY = y - parentEl.getScroll().top; // factor in scrollTop of parent
|
|
parentEl = null;
|
|
if (me.floating) {
|
max = me.maxHeight ? me.maxHeight : viewHeight - normalY;
|
if (height > viewHeight) {
|
returnY = y - normalY;
|
} else if (max < height) {
|
returnY = y - (height - max);
|
} else if((y + height) > viewportHeight){ // keep the document from scrolling
|
returnY = viewportHeight - height;
|
}
|
}
|
me.setY(returnY);
|
}
|
});</pre>
|
</body>
|
</html>
|