123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608 |
- <!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}.
- *
- * To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items},
- * specify `{@link Ext.menu.Item#plain plain}: true`. This reserves a space for an icon, and indents the Component
- * in line with the other menu items.
- *
- * 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-defaultAlign'> /**
- </span> * @cfg {String} [defaultAlign="tl-bl?"]
- * The default {@link Ext.Element#getAlignToXY Ext.Element#getAlignToXY} anchor position value for this menu
- * relative to its element of origin.
- */
- defaultAlign: 'tl-bl?',
- <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,
- hideMode: 'visibility',
- <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 {String/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,
-
- defaultMinWidth: 120,
- <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.
- */
- 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.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.
- if (!me.layout) {
- me.layout = {
- type: 'vbox',
- align: 'stretchmax',
- overflowHandler: 'Scroller'
- };
- }
-
- // only apply the minWidth when we're floating & one hasn't already been set
- if (isFloating && me.minWidth === undefined) {
- me.minWidth = me.defaultMinWidth;
- }
- // hidden defaults to false if floating is configured as false
- if (!isFloating && 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;
- });
- },
- beforeRender: function() {
- 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';
- }
- },
- onBoxReady: function() {
- var me = this,
- separatorSpec;
- me.callParent(arguments);
- // TODO: Move this to a subTemplate When we support them in the future
- if (me.showSeparator) {
- separatorSpec = {
- cls: Ext.baseCSSPrefix + 'menu-icon-separator',
- html: '&#160;'
- };
- if ((!Ext.isStrict && Ext.isIE) || Ext.isIE6) {
- separatorSpec.style = 'height:' + me.el.getHeight() + 'px';
- }
- me.iconSepEl = me.layout.getElementTarget().insertFirst(separatorSpec);
- }
- me.mon(me.el, {
- click: me.onClick,
- mouseover: me.onMouseOver,
- scope: me
- });
- me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me);
- if (me.enableKeyNav) {
- me.keyNav = new Ext.menu.KeyNav(me);
- }
- },
- getBubbleTarget: function() {
- // 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;
- }
- },
- // inherit docs
- getFocusEl: function() {
- return this.focusedItem || this.el;
- },
- // inherit docs
- hide: function() {
- this.deactivateActiveItem(true);
- this.callParent(arguments);
- },
- // private
- getItemFromEvent: function(e) {
- return this.getChildByElement(e.getTarget());
- },
- lookupComponent: function(cmp) {
- 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;
- },
- // private
- 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', prefix + 'menu-item-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(' ');
- }
- }
- return cmp;
- },
- // private
- lookupItemFromString: function(cmp) {
- return (cmp == 'separator' || cmp == '-') ?
- new Ext.menu.Separator()
- : new Ext.menu.Item({
- canActivate: false,
- hideOnClick: false,
- plain: true,
- text: cmp
- });
- },
- onClick: function(e) {
- 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);
- },
- onDestroy: function() {
- var me = this;
- Ext.menu.Manager.unregister(me);
- delete me.parentMenu;
- delete me.ownerButton;
- if (me.rendered) {
- me.el.un(me.mouseMonitor);
- Ext.destroy(me.keyNav);
- 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),
- 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);
- },
- setActiveItem: function(item) {
- 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'> /**
- </span> * Shows the floating menu by the specified {@link Ext.Component Component} or {@link Ext.Element Element}.
- * @param {Ext.Component/Ext.Element} component The {@link Ext.Component} or {@link Ext.Element} to show the menu by.
- * @param {String} [position] Alignment position as used by {@link Ext.Element#getAlignToXY}.
- * Defaults to `{@link #defaultAlign}`.
- * @param {Number[]} [offsets] Alignment offsets as used by {@link Ext.Element#getAlignToXY}.
- * @return {Ext.menu.Menu} This Menu.
- */
- showBy: function(cmp, pos, off) {
- var me = this;
- if (me.floating && cmp) {
- me.show();
- // Align to Component or Element using setPagePosition because normal show
- // methods are container-relative, and we must align to the requested element
- // or Component:
- me.setPagePosition(me.el.getAlignToXY(cmp.el || cmp, pos || me.defaultAlign, off));
- me.setVerticalPosition();
- }
- return me;
- },
- show: function() {
- var me = this,
- parentEl, viewHeight, result,
- maxWas = me.maxHeight;
- // we need to get scope parent for height constraint
- if (!me.rendered){
- me.doAutoRender();
- }
- // constrain the height to the curren viewable area
- if (me.floating) {
- //if our reset css is scoped, there will be a x-reset wrapper on this menu which we need to skip
- parentEl = Ext.fly(me.el.getScopeParent());
- viewHeight = parentEl.getViewSize().height;
- me.maxHeight = Math.min(maxWas || viewHeight, viewHeight);
- }
- result = me.callParent(arguments);
- me.maxHeight = maxWas;
- return result;
- },
- afterComponentLayout: function(width, height, oldWidth, oldHeight){
- var me = this;
- me.callParent(arguments);
- // fixup the separator
- if (me.showSeparator){
- me.iconSepEl.setHeight(me.componentLayout.lastComponentSize.contentHeight);
- }
- },
- // private
- // 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.el.getY(),
- returnY = y,
- height = me.getHeight(),
- viewportHeight = Ext.Element.getViewportHeight().height,
- parentEl = Ext.fly(me.el.getScopeParent()),
- 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.el.setY(returnY);
- }
- });</pre>
- </body>
- </html>
|