Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / docs / source / menu.html
1 <html>
2 <head>
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
4   <title>The source code</title>
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 </head>
8 <body  onload="prettyPrint();">
9     <pre class="prettyprint lang-js">/*!
10  * Ext JS Library 3.3.1
11  * Copyright(c) 2006-2010 Sencha Inc.
12  * licensing@sencha.com
13  * http://www.sencha.com/license
14  */
15 /*
16  * Ext Core Library Examples 3.0 Beta
17  * http://extjs.com/
18  * Copyright(c) 2006-2009, Ext JS, LLC.
19  * 
20  * The MIT License
21  * 
22  * Permission is hereby granted, free of charge, to any person obtaining a copy
23  * of this software and associated documentation files (the "Software"), to deal
24  * in the Software without restriction, including without limitation the rights
25  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
26  * copies of the Software, and to permit persons to whom the Software is
27  * furnished to do so, subject to the following conditions:
28  * 
29  * The above copyright notice and this permission notice shall be included in
30  * all copies or substantial portions of the Software.
31  * 
32  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
38  * THE SOFTWARE.
39  * 
40  */
41
42 Ext.ns('Ext.ux');
43
44 Ext.ux.Menu = Ext.extend(Ext.util.Observable, {
45     direction: 'horizontal',
46     delay: 0.2,
47     autoWidth: true,
48     transitionType: 'fade',
49     transitionDuration: 0.3,
50     animate: true,
51     currentClass: 'current',
52
53     constructor: function(elId, config) {
54         config = config || {};
55         Ext.apply(this, config);
56
57         Ext.ux.Menu.superclass.constructor.call(this, config);
58
59         this.addEvents(
60             'show',
61             'hide',
62             'click'
63         );
64
65         this.el = Ext.get(elId);
66
67         this.initMarkup();
68         this.initEvents();
69
70         this.setCurrent();
71     },
72
73     initMarkup: function(){
74         this.container = this.el.wrap({cls: 'ux-menu-container', style: 'z-index: ' + --Ext.ux.Menu.zSeed});
75         this.items = this.el.select('li');
76
77         this.el.addClass('ux-menu ux-menu-' + this.direction);
78         this.el.select('>li').addClass('ux-menu-item-main');
79
80         this.el.select('li:has(>ul)').addClass('ux-menu-item-parent').each(function(item) {
81             item.down('a')
82                 .addClass('ux-menu-link-parent')
83                 .createChild({tag: 'span', cls: 'ux-menu-arrow'});
84         });
85         
86         this.el.select('li:first-child>a').addClass('ux-menu-link-first');
87         this.el.select('li:last-child>a').addClass('ux-menu-link-last');
88
89         // create clear fixes for the floating stuff
90         this.container.addClass('ux-menu-clearfix');
91
92         // if autoWidth make every submenu as wide as its biggest child;
93         if(this.autoWidth) {
94             this.doAutoWidth();
95         }
96
97         var subs = this.el.select('ul');
98         subs.addClass('ux-menu-sub');
99         
100         //ie6 and ie7/ie8 quirksmode need iframes behind the submenus
101         if(Ext.isBorderBox || Ext.isIE7) {
102             subs.each(function(item) {
103                 item.parent().createChild({tag: 'iframe', cls: 'ux-menu-ie-iframe'})
104                     .setWidth(item.getWidth())
105                     .setHeight(item.getHeight());
106             });
107         }
108         
109         subs.addClass('ux-menu-hidden');
110     },
111
112     initEvents: function() {
113         this.showTask = new Ext.util.DelayedTask(this.showMenu, this);
114         this.hideTask = new Ext.util.DelayedTask(function() {
115             this.showTask.cancel();
116             this.hideAll();
117             this.fireEvent('hide');
118         }, this);
119
120         this.el.hover(function() {
121             this.hideTask.cancel();
122         }, function() {
123             this.hideTask.delay(this.delay*1000);
124         }, this);
125
126         // for each item that has a submenu, create a mouseenter function that shows its submenu
127         // delay 5 to make sure enter is fired after mouseover
128         this.el.select('li.ux-menu-item-parent').on('mouseenter', this.onParentEnter, false, {me: this, delay: 5});
129
130         // listen for mouseover events on items to hide other items submenus and remove hovers
131         this.el.on('mouseover', function(ev, t) {
132             this.manageSiblings(t);
133             // if this item does not have a submenu, the showMenu task for a sibling could potentially still be fired, so cancel it
134             if(!Ext.fly(t).hasClass('ux-menu-item-parent')) {
135                 this.showTask.cancel();
136             }
137         }, this, {delegate: 'li'});
138
139         this.el.on('click', function(ev, t) {
140             return this.fireEvent('click', ev, t, this);
141         }, this, {delegate: 'a'})
142     },
143
144     onParentEnter: function(ev, link, o) {
145         var item = Ext.get(this),
146             me = o.me;
147
148         // if this item is in a submenu and contains a submenu, check if the submenu is not still animating
149         if(!item.hasClass('ux-menu-item-main') && item.parent('ul').hasActiveFx()) {
150             item.parent('ul').stopFx(true);
151         }
152
153         // if submenu is already shown dont do anything
154         if(!item.child('ul').hasClass('ux-menu-hidden')) {
155             return;
156         }
157         
158         me.showTask.delay(me.delay*1000, false, false, [item]);   
159     },
160
161     showMenu : function(item) {
162         var menu = item.child('ul'),
163             x = y = 0;
164
165         item.select('>a').addClass('ux-menu-link-hover');
166
167         // some different configurations require different positioning
168         if(this.direction == 'horizontal' && item.hasClass('ux-menu-item-main')) {
169             y = item.getHeight()+1;
170         }
171         else {
172             x = item.getWidth()+1;
173         }
174
175         // if its ie, force a repaint of the submenu
176         if(Ext.isIE) {
177             menu.select('ul').addClass('ux-menu-hidden');
178             // ie bugs...
179             if(Ext.isBorderBox || Ext.isIE7) {
180                 item.down('iframe').setStyle({left: x + 'px', top: y + 'px', display: 'block'});
181             }
182         }
183
184         menu.setStyle({left: x + 'px', top: y + 'px'}).removeClass('ux-menu-hidden');
185
186         if(this.animate) {
187             switch(this.transitionType) {
188                 case 'slide':
189                     if(this.direction == 'horizontal' && item.hasClass('ux-menu-item-main')) {
190                         menu.slideIn('t', {
191                             duration: this.transitionDuration
192                         });
193                     }
194                     else {
195                         menu.slideIn('l', {
196                             duration: this.transitionDuration
197                         });
198                     }
199                 break;
200
201                 default:
202                     menu.setOpacity(0.001).fadeIn({
203                         duration: this.transitionDuration
204                     });
205                 break
206             }
207         }
208         
209         this.fireEvent('show', item, menu, this);
210     },
211
212     manageSiblings: function(item) {
213         var item = Ext.get(item);
214         item.parent().select('li.ux-menu-item-parent').each(function(child) {
215             if(child.dom.id !== item.dom.id) {
216                 child.select('>a').removeClass('ux-menu-link-hover');
217                 child.select('ul').stopFx(false).addClass('ux-menu-hidden');
218                 if (Ext.isBorderBox || Ext.isIE7) {
219                     child.select('iframe').setStyle('display', 'none');
220                 }
221             }
222         });
223     },
224
225     hideAll: function() {
226         this.manageSiblings(this.el);
227     },
228     
229     setCurrent: function() {
230         var els = this.el.query('.' + this.currentClass);
231         if(!els.length) {
232             return;
233         }
234         var item = Ext.get(els[els.length-1]).removeClass(this.currentClass).findParent('li', null, true);
235         while(item && item.parent('.ux-menu')) {
236             item.down('a').addClass(this.currentClass);
237             item = item.parent('li');
238         }
239     },
240
241     doAutoWidth: function() {
242         var fixWidth = function(sub) {
243             var widest = 0;
244             var items = sub.select('>li');
245
246             sub.setStyle({width: 3000 + 'px'});
247             items.each(function(item) {
248                 widest = Math.max(widest, item.getWidth());
249             });
250
251             widest = Ext.isIE ? widest + 1 : widest;
252             items.setWidth(widest + 'px');
253             sub.setWidth(widest + 'px');
254         }
255
256         if(this.direction == 'vertical') {
257             this.container.select('ul').each(fixWidth);
258         }
259         else {
260             this.el.select('ul').each(fixWidth);
261         }
262         
263     }
264 });
265
266 Ext.ux.Menu.zSeed = 10000;</pre>    
267 </body>
268 </html>