3
* Copyright(c) 2006-2008, Ext JS, LLC.
6
* http://extjs.com/license
10
* @class Ext.menu.Menu
11
* @extends Ext.util.Observable
12
* A menu object. This is the container to which you add all other menu items. Menu can also serve as a base class
13
* when you want a specialized menu based off of another component (like {@link Ext.menu.DateMenu} for example).
16
* @param {Object} config Configuration options
18
Ext.menu.Menu = function(config){
19
if(Ext.isArray(config)){
20
config = {items:config};
22
Ext.apply(this, config);
23
this.id = this.id || Ext.id();
27
* Fires before this menu is displayed
28
* @param {Ext.menu.Menu} this
33
* Fires before this menu is hidden
34
* @param {Ext.menu.Menu} this
39
* Fires after this menu is displayed
40
* @param {Ext.menu.Menu} this
45
* Fires after this menu is hidden
46
* @param {Ext.menu.Menu} this
51
* Fires when this menu is clicked (or when the enter key is pressed while it is active)
52
* @param {Ext.menu.Menu} this
53
* @param {Ext.menu.Item} menuItem The menu item that was clicked
54
* @param {Ext.EventObject} e
59
* Fires when the mouse is hovering over this menu
60
* @param {Ext.menu.Menu} this
61
* @param {Ext.EventObject} e
62
* @param {Ext.menu.Item} menuItem The menu item that was clicked
67
* Fires when the mouse exits this menu
68
* @param {Ext.menu.Menu} this
69
* @param {Ext.EventObject} e
70
* @param {Ext.menu.Item} menuItem The menu item that was clicked
75
* Fires when a menu item contained in this menu is clicked
76
* @param {Ext.menu.BaseItem} baseItem The BaseItem that was clicked
77
* @param {Ext.EventObject} e
81
Ext.menu.MenuMgr.register(this);
82
Ext.menu.Menu.superclass.constructor.call(this);
85
* A MixedCollection of this Menu's items
87
* @type Ext.util.MixedCollection
90
this.items = new Ext.util.MixedCollection();
92
this.add.apply(this, mis);
96
Ext.extend(Ext.menu.Menu, Ext.util.Observable, {
98
* @cfg {Object} defaults
99
* A config object that will be applied to all items added to this container either via the {@link #items}
100
* config or via the {@link #add} method. The defaults config can contain any number of
101
* name/value property pairs to be added to each item, and should be valid for the types of items
102
* being added to the menu.
106
* An array of items to be added to this menu. See {@link #add} for a list of valid item types.
109
* @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
113
* @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
114
* for bottom-right shadow (defaults to "sides")
118
* @cfg {String} subMenuAlign The {@link Ext.Element#alignTo} anchor position value to use for submenus of
119
* this menu (defaults to "tl-tr?")
121
subMenuAlign : "tl-tr?",
123
* @cfg {String} defaultAlign The default {@link Ext.Element#alignTo} anchor position value for this menu
124
* relative to its element of origin (defaults to "tl-bl?")
126
defaultAlign : "tl-bl?",
128
* @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
130
allowOtherMenus : false,
132
* @cfg {Boolean} ignoreParentClicks True to ignore clicks on any item in this menu that is a parent item (displays
133
* a submenu) so that the submenu is not dismissed when clicking the parent item (defaults to false).
135
ignoreParentClicks : false,
141
createEl : function(){
142
return new Ext.Layer({
146
parentEl: this.parentEl || document.body,
156
var el = this.el = this.createEl();
159
this.keyNav = new Ext.menu.MenuNav(this);
162
el.addClass("x-menu-plain");
165
el.addClass(this.cls);
167
// generic focus element
168
this.focusEl = el.createChild({
169
tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
171
var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
172
ul.on("click", this.onClick, this);
173
ul.on("mouseover", this.onMouseOver, this);
174
ul.on("mouseout", this.onMouseOut, this);
175
this.items.each(function(item){
176
var li = document.createElement("li");
177
li.className = "x-menu-list-item";
178
ul.dom.appendChild(li);
179
item.render(li, this);
186
autoWidth : function(){
187
var el = this.el, ul = this.ul;
195
el.setWidth(this.minWidth);
196
var t = el.dom.offsetWidth; // force recalc
197
el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
202
delayAutoWidth : function(){
205
this.awTask = new Ext.util.DelayedTask(this.autoWidth, this);
207
this.awTask.delay(20);
212
findTargetItem : function(e){
213
var t = e.getTarget(".x-menu-list-item", this.ul, true);
214
if(t && t.menuItemId){
215
return this.items.get(t.menuItemId);
220
onClick : function(e){
222
if(t = this.findTargetItem(e)){
223
if(t.menu && this.ignoreParentClicks){
227
this.fireEvent("click", this, t, e);
233
setActiveItem : function(item, autoExpand){
234
if(item != this.activeItem){
236
this.activeItem.deactivate();
238
this.activeItem = item;
239
item.activate(autoExpand);
240
}else if(autoExpand){
246
tryActivate : function(start, step){
247
var items = this.items;
248
for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
249
var item = items.get(i);
250
if(!item.disabled && item.canActivate){
251
this.setActiveItem(item, false);
259
onMouseOver : function(e){
261
if(t = this.findTargetItem(e)){
262
if(t.canActivate && !t.disabled){
263
this.setActiveItem(t, true);
267
this.fireEvent("mouseover", this, e, t);
271
onMouseOut : function(e){
273
if(t = this.findTargetItem(e)){
274
if(t == this.activeItem && t.shouldDeactivate(e)){
275
this.activeItem.deactivate();
276
delete this.activeItem;
280
this.fireEvent("mouseout", this, e, t);
284
* Read-only. Returns true if the menu is currently displayed, else false.
287
isVisible : function(){
288
return this.el && !this.hidden;
292
* Displays this menu relative to another element
293
* @param {Mixed} element The element to align to
294
* @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to
295
* the element (defaults to this.defaultAlign)
296
* @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
298
show : function(el, pos, parentMenu){
299
this.parentMenu = parentMenu;
303
this.fireEvent("beforeshow", this);
304
this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
308
* Displays this menu at a specific xy position
309
* @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
310
* @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
312
showAt : function(xy, parentMenu, /* private: */_e){
313
this.parentMenu = parentMenu;
318
this.fireEvent("beforeshow", this);
319
xy = this.el.adjustForConstraints(xy);
325
this.fireEvent("show", this);
332
this.doFocus.defer(50, this);
336
doFocus : function(){
338
this.focusEl.focus();
343
* Hides this menu and optionally all parent menus
344
* @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
346
hide : function(deep){
347
if(this.el && this.isVisible()){
348
this.fireEvent("beforehide", this);
350
this.activeItem.deactivate();
351
this.activeItem = null;
355
this.fireEvent("hide", this);
357
if(deep === true && this.parentMenu){
358
this.parentMenu.hide(true);
363
* Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
364
* Any of the following are valid:
366
* <li>Any menu item object based on {@link Ext.menu.BaseItem}</li>
367
* <li>An HTMLElement object which will be converted to a menu item</li>
368
* <li>A menu item config object that will be created as a new menu item</li>
369
* <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
370
* it will be converted into a {@link Ext.menu.TextItem} and added</li>
375
var menu = new Ext.menu.Menu();
377
// Create a menu item to add by reference
378
var menuItem = new Ext.menu.Item({ text: 'New Item!' });
380
// Add a bunch of items at once using different methods.
381
// Only the last item added will be returned.
383
menuItem, // add existing item by ref
384
'Dynamic Item', // new TextItem
385
'-', // new separator
386
{ text: 'Config Item' } // new item by config
389
* @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
390
* @return {Ext.menu.Item} The menu item that was added, or the last one if multiple items were added
393
var a = arguments, l = a.length, item;
394
for(var i = 0; i < l; i++){
396
if(el.render){ // some kind of Item
397
item = this.addItem(el);
398
}else if(typeof el == "string"){ // string
399
if(el == "separator" || el == "-"){
400
item = this.addSeparator();
402
item = this.addText(el);
404
}else if(el.tagName || el.el){ // element
405
item = this.addElement(el);
406
}else if(typeof el == "object"){ // must be menu item config?
407
Ext.applyIf(el, this.defaults);
408
item = this.addMenuItem(el);
415
* Returns this menu's underlying {@link Ext.Element} object
416
* @return {Ext.Element} The element
426
* Adds a separator bar to the menu
427
* @return {Ext.menu.Item} The menu item that was added
429
addSeparator : function(){
430
return this.addItem(new Ext.menu.Separator());
434
* Adds an {@link Ext.Element} object to the menu
435
* @param {Mixed} el The element or DOM node to add, or its id
436
* @return {Ext.menu.Item} The menu item that was added
438
addElement : function(el){
439
return this.addItem(new Ext.menu.BaseItem(el));
443
* Adds an existing object based on {@link Ext.menu.BaseItem} to the menu
444
* @param {Ext.menu.Item} item The menu item to add
445
* @return {Ext.menu.Item} The menu item that was added
447
addItem : function(item){
448
this.items.add(item);
450
var li = document.createElement("li");
451
li.className = "x-menu-list-item";
452
this.ul.dom.appendChild(li);
453
item.render(li, this);
454
this.delayAutoWidth();
460
* Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu
461
* @param {Object} config A MenuItem config object
462
* @return {Ext.menu.Item} The menu item that was added
464
addMenuItem : function(config){
465
if(!(config instanceof Ext.menu.Item)){
466
if(typeof config.checked == "boolean"){ // must be check menu item config?
467
config = new Ext.menu.CheckItem(config);
469
config = new Ext.menu.Item(config);
472
return this.addItem(config);
476
* Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu
477
* @param {String} text The text to display in the menu item
478
* @return {Ext.menu.Item} The menu item that was added
480
addText : function(text){
481
return this.addItem(new Ext.menu.TextItem(text));
485
* Inserts an existing object based on {@link Ext.menu.BaseItem} to the menu at a specified index
486
* @param {Number} index The index in the menu's list of current items where the new item should be inserted
487
* @param {Ext.menu.Item} item The menu item to add
488
* @return {Ext.menu.Item} The menu item that was added
490
insert : function(index, item){
491
this.items.insert(index, item);
493
var li = document.createElement("li");
494
li.className = "x-menu-list-item";
495
this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
496
item.render(li, this);
497
this.delayAutoWidth();
503
* Removes an {@link Ext.menu.Item} from the menu and destroys the object
504
* @param {Ext.menu.Item} item The menu item to remove
506
remove : function(item){
507
this.items.removeKey(item.id);
512
* Removes and destroys all items in the menu
514
removeAll : function(){
517
while(f = this.items.first()){
524
* Destroys the menu by unregistering it from {@link Ext.menu.MenuMgr}, purging event listeners,
525
* removing all of the menus items, then destroying the underlying {@link Ext.Element}
527
destroy : function(){
528
this.beforeDestroy();
529
Ext.menu.MenuMgr.unregister(this);
531
this.keyNav.disable();
535
this.ul.removeAllListeners();
543
beforeDestroy : Ext.emptyFn
547
// MenuNav is a private utility class used internally by the Menu
548
Ext.menu.MenuNav = function(menu){
549
Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);
550
this.scope = this.menu = menu;
553
Ext.extend(Ext.menu.MenuNav, Ext.KeyNav, {
554
doRelay : function(e, h){
556
if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
557
this.menu.tryActivate(0, 1);
560
return h.call(this.scope || this, e, this.menu);
564
if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
565
m.tryActivate(m.items.length-1, -1);
569
down : function(e, m){
570
if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
575
right : function(e, m){
577
m.activeItem.expandMenu(true);
581
left : function(e, m){
583
if(m.parentMenu && m.parentMenu.activeItem){
584
m.parentMenu.activeItem.activate();
588
enter : function(e, m){
591
m.activeItem.onClick(e);
592
m.fireEvent("click", this, m.activeItem);
b'\\ No newline at end of file'