]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/jquery/jquery.superfish.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / jquery / jquery.superfish.js
1 /*
2 * Superfish v1.4.8 - jQuery menu widget
3 * Copyright (c) 2008 Joel Birch
4 *
5 * Dual licensed under the MIT and GPL licenses:
6 * http://www.opensource.org/licenses/mit-license.php
7 * http://www.gnu.org/licenses/gpl.html
8 *
9 * CHANGELOG: http://users.tpg.com.au/j_birch/plugins/superfish/changelog.txt
10 */
11
12 ; (function($) {
13     $.fn.superfish = function(op) {
14
15         var sf = $.fn.superfish,
16         c = sf.c,
17         menuActive = false,
18         $arrow = $(['<span class="', c.arrowClass, '"> &#187;</span>'].join('')),
19         click = function(evt) {
20                 $(".subnav.ddopen").hide();
21             var $$ = $(this),
22             menu = getMenu($$),
23             o = sf.op;
24             if (o.firstOnClick && !menuActive && $$.parent()[0] == menu)
25             {
26                 menuActive = true;
27                 clearTimeout(menu.sfTimer);
28
29                 $$.showSuperfishUl().siblings().hideSuperfishUl();
30                 // prevent redirect to anchor target href
31                 evt.preventDefault();
32             }
33         },
34         over = function() {
35             var $$ = $(this),
36             menu = getMenu($$),
37             o = sf.op;
38             //Bug#52225: Activate submenu while hs-activated
39             if($$.parent().hasClass("hs-active")) {
40                 $$.addClass("iefix");
41             } else {
42                 $$.removeClass("iefix");
43             }
44
45             if (!o.firstOnClick || menuActive || $$.parent()[0] != menu)
46             {
47                 clearTimeout(menu.sfTimer);
48                 $$.showSuperfishUl().siblings().hideSuperfishUl();
49                 if($$.parent().hasClass('megamenuSiblings')) {
50                         $$.parent().siblings().children('li').hideSuperfishUl();
51                 }
52             }
53         },
54         out = function() {
55             var $$ = $(this),
56                 menu = getMenu($$),
57                 o = sf.op,
58                 $menu = $(menu);
59             //Bug#52225: Activate submenu while hs-activated
60             if($$.parent().hasClass("hs-active")) {
61                 $$.addClass("iefix");
62                 setTimeout(function() {
63                     if($menu.hasClass(sf.defaults['retainClass']) === false)
64                         $$.hideSuperfishUl();
65                 }, o.delay);
66             } else {
67                 $$.removeClass("iefix");
68             }
69
70             clearTimeout(menu.sfTimer);
71             menu.sfTimer = $menu.hasClass(sf.defaults['retainClass']) ? null : setTimeout(function() {
72                 if($menu.hasClass(sf.defaults['retainClass']) === false) {
73                     o.retainPath = ($.inArray($$[0], o.$path) > -1);
74                     $$.hideSuperfishUl();
75                     if (o.$path.length && $$.parents(['li.', o.hoverClass].join('')).length < 1)
76                     {
77                         over.call(o.$path);
78                     }
79                     else
80                     {
81                         menuActive = false;
82                     }
83                 }
84
85             },
86             o.delay);
87         },
88         getMenu = function($menu) {
89             var menu = $menu.hasClass(sf.menuClass) ? $menu[0] : $menu.parents(['ul.', c.menuClass, ':first'].join(''))[0];
90             if(!menu)
91                 return $menu[0];
92             sf.op = sf.o[menu.serial];
93             return menu;
94         },
95         addArrow = function($a) {
96             $a.addClass(c.anchorClass).append($arrow.clone());
97         };
98         sf.getMenu = getMenu;
99         return this.each(function() {
100             var s = this.serial = sf.o.length;
101             var o = $.extend({},
102             sf.defaults, op);
103             o.$path =
104             $('li.' + o.pathClass, this).slice(0, o.pathLevels).each
105             (function() {
106
107                 $(this).addClass([o.hoverClass, c.bcClass].join(' '))
108
109                 .filter('li:has(ul)').removeClass(o.pathClass);
110             });
111             sf.o[s] = sf.op = o;
112
113                         if(o.firstOnClick){
114                                 $('li:has(ul)', this).not('li:has( > .' + sf.ignoreClass + ')')['click'](click);
115                         } else {
116                                 $('li:has(ul)', this).not('li:has( > .' + sf.ignoreClass + ')')[($.fn.hoverIntent && !o.disableHI) ? 'hoverIntent' : 'hover'](over, out);
117                         }
118             
119             $('li:has(ul)', this)
120             .click(click)
121             .each(function() {
122                 if (o.autoArrows) addArrow(
123                 $('>a:first-child', this));
124             })
125             .not('.' + c.bcClass)
126             .hideSuperfishUl();
127
128             var $a = $('a', this);
129             $a.each(function(i) {
130                 var $li = $a.eq(i).parents('li');
131
132                 $a.eq(i).attr("tabindex",-1).focus(function() {
133                     over.call($li);
134                 }).blur(function()
135                 {
136                     out.call($li);
137                 });
138                 
139                 
140                 if(o.firstOnClick) {
141                         $a.eq(i).click(function(event)
142                         {
143                                           event.preventDefault();
144                                           if ( !$a.eq(i).hasClass("sf-with-ul") || $li.children('ul').size() == 0) {
145                                             SUGAR.ajaxUI.loadContent(this.href);
146                                           }
147                                         });
148                                         
149                                         
150                                         $a.eq(i).dblclick(function(event)
151                         {
152                             SUGAR.ajaxUI.loadContent(this.href);
153                                         });
154                 }
155                                 
156             });
157             o.onInit.call(this);
158
159         }).each(function() {
160             var menuClasses = [c.menuClass];
161             if (sf.op.dropShadows && !($.browser.msie &&
162             $.browser.version <
163             7)) menuClasses.push(c.shadowClass);
164             $(this).addClass(menuClasses.join(' '));
165         });
166     };
167
168     var sf = $.fn.superfish;
169     sf.o = [];
170     sf.op = {};
171     sf.counter = 0;
172     sf.IE7fix = function() {
173         var o = sf.op;
174         if ($.browser.msie && $.browser.version > 6 && o.dropShadows &&
175         o.animation.opacity != undefined)
176         this.toggleClass(sf.c.shadowClass + '-off');
177     };
178     
179      sf.positionMenu = function($ul) {
180          //reset css generating by positionMenu
181         this.removeClass("rtl ltr");
182
183         if(this.offset() && this.parent().parent().hasClass('sf-menu') != true) {
184             //reset position to origin
185             var is_rtl_theme = sf.op['rtl'] || sf.defaults['rtl'];
186             if(is_rtl_theme) {
187                 this.css({
188                     'left' : 'auto',
189                     'right' : this.attr("right") ? this.attr("right") : this.css("right"),
190                     'top' : 0,
191                     'bottom' : 'auto'
192                 });
193             } else {
194                 this.css({
195                     'left' : this.attr("left") ? this.attr("left") : this.css("left"),
196                     'right' : 'auto',
197                     'top' : 0,
198                     'bottom' : 'auto'
199                 });
200             }
201             var viewPortHeight = $(window).height(),
202                 viewPortWidth = $(window).width(),
203                 submenuHeight = this.outerHeight(),
204                 megamenuWidth = (is_rtl_theme) ? this.parent().outerWidth() : sf.cssValue.call(this, 'left'),
205                 submenuTop = this.offset().top - $(document).scrollTop(),
206                 submenuLeft = this.offset().left - $(document).scrollLeft(),
207                 megamenuLeft = this.parent().offset().left - $(document).scrollLeft(),
208                 viewPortRSpace = (is_rtl_theme) ? viewPortWidth - megamenuLeft - megamenuWidth : viewPortWidth - submenuLeft,
209                 viewPortLSpace = (is_rtl_theme) ? megamenuLeft : submenuLeft - megamenuWidth;
210
211             //Followings are required to optimize calculation in IE
212             if(megamenuWidth == 0) {
213                 megamenuWidth = this.parent().outerWidth();
214                 viewPortRSpace -= megamenuWidth;
215             }
216                 if(submenuTop + submenuHeight > viewPortHeight) {
217                         this.css('top',viewPortHeight - submenuTop - submenuHeight);
218                 }
219             if(is_rtl_theme === false && viewPortRSpace < viewPortLSpace && viewPortRSpace < megamenuWidth) {
220                 var _left = this.css('left');
221                 this.attr("left", _left).css({
222                     'left': 'auto',
223                     'right': _left
224                 }).addClass("rtl");
225             } else if(is_rtl_theme && viewPortRSpace > viewPortLSpace && viewPortLSpace < megamenuWidth) {
226                 var _right = this.css('right');
227                 this.attr("right", _right).css({
228                     'left': _right,
229                     'right': 'auto'
230                 }).addClass("ltr");
231             }
232         }
233     }
234     /**
235      * Return css property variale which contains numerical data.
236      * i.e. width, border, padding-left, etc.
237      * @param this - element that is trying to retrive
238      * @param $css - css properity which contains numerical data
239      * @return int - value of the size
240      */
241     sf.cssValue = function($css) {
242         if(this.length == 0)
243             return 0;
244         var _val = parseInt(this.css($css).replace("px", ""));
245         return (_val) ? _val : 0;
246     };
247     /**
248      * To support IE fixed size rendering,
249      * parse out dom elements out of the fixed element
250      *
251      * Prepare ===
252      * <div style=position:fixed>
253      *     ...
254      *     <li jquery-attached>
255      *         <ul style=position:absoulte>
256      *             ...
257      *         </ul>
258      *     </li>
259      * </div>
260      *
261      * Application ===
262      * <div style=position:fixed>
263      *     <li ul-child-id='auto-evaluted-id'>
264      *     ...
265      *     </li>
266      * </div>
267      *
268      * <ul id='auto-evaluted-id' style=position:fix;left/right/top-positioning:auto-calculated>
269      *     ...
270      * </ul>
271      * @param this - element container which is inside the fixed box model
272      * @param $ul - dropdown box model which needs to render out of the fixed box range
273      *              if $ul is not given, it will restore back to the original structure
274      */
275     sf.IEfix = function($ul) {
276         if ( ($.browser.msie && $.browser.version > 6) || $(this).hasClass("iefix") ) {
277             if($ul) {
278                 //Take out the element out of the fixed box model,
279                 //and then append it into the end of body container
280                 this.each(function(){
281                     var $$ = $(this),
282                         o = sf.op,
283                         is_rtl_theme = sf.op['rtl'] || sf.defaults['rtl'],
284                         _id = $$.attr("ul-child-id") ? $$.attr("ul-child-id") : ($ul.attr('id')) ? $ul.attr('id') : o.megamenuID ? o.megamenuID + ++sf.counter : 'megamenu' + ++sf.counter,
285                         _top = $$.position().top + $$.parent().offset().top - $(document).scrollTop(),
286                         _rtl_adjustment = $$.width() - $ul.width(),
287                         _left = $$.offset().left - sf.cssValue.call($ul, "border-left-width") - $(document).scrollLeft(),
288                         _right = $(window).width() - _left - $$.width() - sf.cssValue.call($ul, "border-right-width"),
289                         $menu = $('ul.' + sf.c.menuClass + ':visible');
290
291                     //handling sub-sliding menu
292                     if($$.css('position') == 'static' || $$.parent().hasClass('megamenuSiblings')) {
293
294                         //When the submenu is positioned to the left-hand side, the absolute position should be adjusted as expected
295                         if(is_rtl_theme) {
296                             _right = $ul.hasClass("ltr") ?_right - $ul.outerWidth()
297                                 : _right + $$.outerWidth();
298                         } else {
299                             _left = $ul.hasClass("rtl") ? _left - $ul.outerWidth()
300                                 : _left + $$.outerWidth();
301                         }
302                         _top += sf.cssValue.call($ul, "top");
303                         $ul.addClass('sf-sub-modulelist').on('mouseover', function(){
304                                 $$.addClass(sf.defaults['retainClass']);
305                             }).on('mouseout', function(){
306                                 $$.removeClass(sf.defaults['retainClass']);
307                                 $('ul.' + sf.c.menuClass + ':visible').removeClass(sf.defaults['retainClass'])[0].sfTimer = setTimeout(function(){
308                                     $$.hideSuperfishUl();
309                                     $('ul.' + sf.c.menuClass + ':visible > li').hideSuperfishUl();
310                                 }, o.delay);
311                             });
312                     } else {
313                         _top += $$.outerHeight();
314                     }
315
316                     //append the item into the body element, and then save the id to restore back later
317                     $('body').append($ul.attr("id", _id).css({
318                         top: _top,
319                         left: (is_rtl_theme) ? '' : _left,
320                         right: (is_rtl_theme) ? _right : '',
321                         position: 'fixed'
322                         }).on('mouseover',function(){
323                             //maintaining the dropdown container
324                             var menu = sf.getMenu($menu),
325                                 o = sf.op;
326                             clearTimeout(menu.sfTimer);
327                             if( $(menu).hasClass(sf.defaults['retainClass']) === false )
328                                 $(menu).addClass(sf.defaults['retainClass']);
329                         }).on('mouseout', function(){
330                             //clear out the dropdown menu
331                             var menu = sf.getMenu($menu),
332                                 o = sf.op;
333                             clearTimeout(menu.sfTimer);
334
335                             menu.sfTimer = setTimeout(function() {
336                                 $$.hideSuperfishUl();
337                                 $(menu).removeClass(sf.defaults['retainClass']);
338                                 $(menu).hideSuperfishUl();
339                             }, o.delay)
340                         })
341                     );
342                     $$.attr("ul-child-id", _id);
343                 });
344
345             } else {
346                 //restore back the element to the original structure
347                 this.each(function(){
348                     var _id = $(this).attr("ul-child-id"),
349                         _elem = $("#"+_id);
350                     $(this).append(_elem.off('mouseover mouseout').css({
351                         'left' : '',
352                         'right' : '',
353                         'top' : '',
354                         'bottom' : '',
355                         'position': ''
356                     }));
357                 });
358             }
359         }
360     };
361     sf.c = {
362         bcClass: 'sf-breadcrumb',
363         menuClass: 'sf-js-enabled',
364         anchorClass: 'sf-with-ul',
365         arrowClass: 'sf-sub-indicator',
366         shadowClass: 'sf-shadow'
367     };
368     sf.defaults = {
369         hoverClass: 'sfHover',
370         retainClass: 'retainThisItem',
371         ignoreClass: 'none',
372         pathClass: 'overideThisToUse',
373         pathLevels: 8,
374         delay: 800,
375         animation: {
376             opacity: 'show'
377         },
378         speed: 'normal',
379         autoArrows: true,
380         dropShadows: true,
381         disableHI: false,
382         // true disables hoverIntent detection
383         onInit: function() {},
384         // callback functions
385         onBeforeShow: function() {},
386         onShow: function() {},
387         onHide: function() {},
388         firstOnClick: false,
389         // true - open first level on click (like classic application menu),
390         rtl: false
391         // true - if it is RTL theme
392
393     };
394     $.fn.extend({
395         hideSuperfishUl: function() {
396             var o = sf.op,
397             not = (o.retainPath === true) ? o.$path: '';
398             o.retainPath = false;
399             sf.IEfix.call(this);
400             var $ul = $(['li.', o.hoverClass].join(''), this).add(this).not
401                 (not).removeClass(o.hoverClass).find('>ul').hide().css('visibility', 'hidden');
402             o.onHide.call($ul);
403             return this;
404         },
405         showSuperfishUl: function() {
406             var o = sf.op,
407             sh = sf.c.shadowClass + '-off',
408             $ul = this.addClass(o.hoverClass).find('>ul:hidden').show().css('visibility', 'visible');
409             sf.positionMenu.call($ul);
410             sf.IE7fix.call($ul);
411             o.onBeforeShow.call($ul);
412             sf.IEfix.call(this, $ul);
413             $ul.animate(o.animation, o.speed,
414             function() {
415                 sf.IE7fix.call($ul);
416                 o.onShow.call($ul);
417             });
418             return this;
419         }
420     });
421
422 })(jQuery);