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