Upgrade to ExtJS 3.2.0 - Released 03/30/2010
[extjs.git] / src / widgets / menu / Item.js
1 /*!
2  * Ext JS Library 3.2.0
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.menu.Item
9  * @extends Ext.menu.BaseItem
10  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
11  * display items.  Item extends the base functionality of {@link Ext.menu.BaseItem} by adding menu-specific
12  * activation and click handling.
13  * @constructor
14  * Creates a new Item
15  * @param {Object} config Configuration options
16  * @xtype menuitem
17  */
18 Ext.menu.Item = Ext.extend(Ext.menu.BaseItem, {
19     /**
20      * @property menu
21      * @type Ext.menu.Menu
22      * The submenu associated with this Item if one was configured.
23      */
24     /**
25      * @cfg {Mixed} menu (optional) Either an instance of {@link Ext.menu.Menu} or the config object for an
26      * {@link Ext.menu.Menu} which acts as the submenu when this item is activated.
27      */
28     /**
29      * @cfg {String} icon The path to an icon to display in this item (defaults to Ext.BLANK_IMAGE_URL).  If
30      * icon is specified {@link #iconCls} should not be.
31      */
32     /**
33      * @cfg {String} iconCls A CSS class that specifies a background image that will be used as the icon for
34      * this item (defaults to '').  If iconCls is specified {@link #icon} should not be.
35      */
36     /**
37      * @cfg {String} text The text to display in this item (defaults to '').
38      */
39     /**
40      * @cfg {String} href The href attribute to use for the underlying anchor link (defaults to '#').
41      */
42     /**
43      * @cfg {String} hrefTarget The target attribute to use for the underlying anchor link (defaults to '').
44      */
45     /**
46      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to 'x-menu-item')
47      */
48     itemCls : 'x-menu-item',
49     /**
50      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
51      */
52     canActivate : true,
53     /**
54      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
55      */
56     showDelay: 200,
57     // doc'd in BaseItem
58     hideDelay: 200,
59
60     // private
61     ctype: 'Ext.menu.Item',
62
63     initComponent : function(){
64         Ext.menu.Item.superclass.initComponent.call(this);
65         if(this.menu){
66             this.menu = Ext.menu.MenuMgr.get(this.menu);
67             this.menu.ownerCt = this;
68         }
69     },
70
71     // private
72     onRender : function(container, position){
73         if (!this.itemTpl) {
74             this.itemTpl = Ext.menu.Item.prototype.itemTpl = new Ext.XTemplate(
75                 '<a id="{id}" class="{cls}" hidefocus="true" unselectable="on" href="{href}"',
76                     '<tpl if="hrefTarget">',
77                         ' target="{hrefTarget}"',
78                     '</tpl>',
79                  '>',
80                      '<img src="{icon}" class="x-menu-item-icon {iconCls}"/>',
81                      '<span class="x-menu-item-text">{text}</span>',
82                  '</a>'
83              );
84         }
85         var a = this.getTemplateArgs();
86         this.el = position ? this.itemTpl.insertBefore(position, a, true) : this.itemTpl.append(container, a, true);
87         this.iconEl = this.el.child('img.x-menu-item-icon');
88         this.textEl = this.el.child('.x-menu-item-text');
89         if(!this.href) { // if no link defined, prevent the default anchor event
90             this.mon(this.el, 'click', Ext.emptyFn, null, { preventDefault: true });
91         }
92         Ext.menu.Item.superclass.onRender.call(this, container, position);
93     },
94
95     getTemplateArgs: function() {
96         return {
97             id: this.id,
98             cls: this.itemCls + (this.menu ?  ' x-menu-item-arrow' : '') + (this.cls ?  ' ' + this.cls : ''),
99             href: this.href || '#',
100             hrefTarget: this.hrefTarget,
101             icon: this.icon || Ext.BLANK_IMAGE_URL,
102             iconCls: this.iconCls || '',
103             text: this.itemText||this.text||'&#160;'
104         };
105     },
106
107     /**
108      * Sets the text to display in this menu item
109      * @param {String} text The text to display
110      */
111     setText : function(text){
112         this.text = text||'&#160;';
113         if(this.rendered){
114             this.textEl.update(this.text);
115             this.parentMenu.layout.doAutoSize();
116         }
117     },
118
119     /**
120      * Sets the CSS class to apply to the item's icon element
121      * @param {String} cls The CSS class to apply
122      */
123     setIconClass : function(cls){
124         var oldCls = this.iconCls;
125         this.iconCls = cls;
126         if(this.rendered){
127             this.iconEl.replaceClass(oldCls, this.iconCls);
128         }
129     },
130
131     //private
132     beforeDestroy: function(){
133         if (this.menu){
134             delete this.menu.ownerCt;
135             this.menu.destroy();
136         }
137         Ext.menu.Item.superclass.beforeDestroy.call(this);
138     },
139
140     // private
141     handleClick : function(e){
142         if(!this.href){ // if no link defined, stop the event automatically
143             e.stopEvent();
144         }
145         Ext.menu.Item.superclass.handleClick.apply(this, arguments);
146     },
147
148     // private
149     activate : function(autoExpand){
150         if(Ext.menu.Item.superclass.activate.apply(this, arguments)){
151             this.focus();
152             if(autoExpand){
153                 this.expandMenu();
154             }
155         }
156         return true;
157     },
158
159     // private
160     shouldDeactivate : function(e){
161         if(Ext.menu.Item.superclass.shouldDeactivate.call(this, e)){
162             if(this.menu && this.menu.isVisible()){
163                 return !this.menu.getEl().getRegion().contains(e.getPoint());
164             }
165             return true;
166         }
167         return false;
168     },
169
170     // private
171     deactivate : function(){
172         Ext.menu.Item.superclass.deactivate.apply(this, arguments);
173         this.hideMenu();
174     },
175
176     // private
177     expandMenu : function(autoActivate){
178         if(!this.disabled && this.menu){
179             clearTimeout(this.hideTimer);
180             delete this.hideTimer;
181             if(!this.menu.isVisible() && !this.showTimer){
182                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
183             }else if (this.menu.isVisible() && autoActivate){
184                 this.menu.tryActivate(0, 1);
185             }
186         }
187     },
188
189     // private
190     deferExpand : function(autoActivate){
191         delete this.showTimer;
192         this.menu.show(this.container, this.parentMenu.subMenuAlign || 'tl-tr?', this.parentMenu);
193         if(autoActivate){
194             this.menu.tryActivate(0, 1);
195         }
196     },
197
198     // private
199     hideMenu : function(){
200         clearTimeout(this.showTimer);
201         delete this.showTimer;
202         if(!this.hideTimer && this.menu && this.menu.isVisible()){
203             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
204         }
205     },
206
207     // private
208     deferHide : function(){
209         delete this.hideTimer;
210         if(this.menu.over){
211             this.parentMenu.setActiveItem(this, false);
212         }else{
213             this.menu.hide();
214         }
215     }
216 });
217 Ext.reg('menuitem', Ext.menu.Item);