2 * Superfish v1.4.8 - jQuery menu widget
3 * Copyright (c) 2008 Joel Birch
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
9 * CHANGELOG: http://users.tpg.com.au/j_birch/plugins/superfish/changelog.txt
13 $.fn.superfish = function(op) {
15 var sf = $.fn.superfish,
18 $arrow = $(['<span class="', c.arrowClass, '"> »</span>'].join('')),
19 click = function(evt) {
20 $(".subnav.ddopen").hide();
24 if (o.firstOnClick && !menuActive && $$.parent()[0] == menu)
27 clearTimeout(menu.sfTimer);
29 $$.showSuperfishUl().siblings().hideSuperfishUl();
30 // prevent redirect to anchor target href
38 //Bug#52225: Activate submenu while hs-activated
39 if($$.parent().hasClass("hs-active")) {
42 $$.removeClass("iefix");
45 if (!o.firstOnClick || menuActive || $$.parent()[0] != menu)
47 clearTimeout(menu.sfTimer);
48 $$.showSuperfishUl().siblings().hideSuperfishUl();
49 if($$.parent().hasClass('megamenuSiblings')) {
50 $$.parent().siblings().children('li').hideSuperfishUl();
59 //Bug#52225: Activate submenu while hs-activated
60 if($$.parent().hasClass("hs-active")) {
62 setTimeout(function() {
63 if($menu.hasClass(sf.defaults['retainClass']) === false)
67 $$.removeClass("iefix");
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);
75 if (o.$path.length && $$.parents(['li.', o.hoverClass].join('')).length < 1)
88 getMenu = function($menu) {
89 var menu = $menu.hasClass(sf.menuClass) ? $menu[0] : $menu.parents(['ul.', c.menuClass, ':first'].join(''))[0];
92 sf.op = sf.o[menu.serial];
95 addArrow = function($a) {
96 $a.addClass(c.anchorClass).append($arrow.clone());
99 return this.each(function() {
100 var s = this.serial = sf.o.length;
104 $('li.' + o.pathClass, this).slice(0, o.pathLevels).each
107 $(this).addClass([o.hoverClass, c.bcClass].join(' '))
109 .filter('li:has(ul)').removeClass(o.pathClass);
114 $('li:has(ul)', this).not('li:has( > .' + sf.ignoreClass + ')')['click'](click);
116 $('li:has(ul)', this).not('li:has( > .' + sf.ignoreClass + ')')[($.fn.hoverIntent && !o.disableHI) ? 'hoverIntent' : 'hover'](over, out);
119 $('li:has(ul)', this)
122 if (o.autoArrows) addArrow(
123 $('>a:first-child', this));
125 .not('.' + c.bcClass)
128 var $a = $('a', this);
129 $a.each(function(i) {
130 var $li = $a.eq(i).parents('li');
132 $a.eq(i).attr("tabindex",-1).focus(function() {
141 $a.eq(i).click(function(event)
143 event.preventDefault();
144 if ( !$a.eq(i).hasClass("sf-with-ul") || $li.children('ul').size() == 0) {
145 SUGAR.ajaxUI.loadContent(this.href);
150 $a.eq(i).dblclick(function(event)
152 SUGAR.ajaxUI.loadContent(this.href);
160 var menuClasses = [c.menuClass];
161 if (sf.op.dropShadows && !($.browser.msie &&
163 7)) menuClasses.push(c.shadowClass);
164 $(this).addClass(menuClasses.join(' '));
168 var sf = $.fn.superfish;
172 sf.IE7fix = function() {
174 if ($.browser.msie && $.browser.version > 6 && o.dropShadows &&
175 o.animation.opacity != undefined)
176 this.toggleClass(sf.c.shadowClass + '-off');
179 sf.positionMenu = function($ul) {
180 //reset css generating by positionMenu
181 this.removeClass("rtl ltr");
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'];
189 'right' : this.attr("right") ? this.attr("right") : this.css("right"),
195 'left' : this.attr("left") ? this.attr("left") : this.css("left"),
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;
211 //Followings are required to optimize calculation in IE
212 if(megamenuWidth == 0) {
213 megamenuWidth = this.parent().outerWidth();
214 viewPortRSpace -= megamenuWidth;
216 if(submenuTop + submenuHeight > viewPortHeight) {
217 this.css('top',viewPortHeight - submenuTop - submenuHeight);
219 if(is_rtl_theme === false && viewPortRSpace < viewPortLSpace && viewPortRSpace < megamenuWidth) {
220 var _left = this.css('left');
221 this.attr("left", _left).css({
225 } else if(is_rtl_theme && viewPortRSpace > viewPortLSpace && viewPortLSpace < megamenuWidth) {
226 var _right = this.css('right');
227 this.attr("right", _right).css({
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
241 sf.cssValue = function($css) {
244 var _val = parseInt(this.css($css).replace("px", ""));
245 return (_val) ? _val : 0;
248 * To support IE fixed size rendering,
249 * parse out dom elements out of the fixed element
252 * <div style=position:fixed>
254 * <li jquery-attached>
255 * <ul style=position:absoulte>
262 * <div style=position:fixed>
263 * <li ul-child-id='auto-evaluted-id'>
268 * <ul id='auto-evaluted-id' style=position:fix;left/right/top-positioning:auto-calculated>
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
275 sf.IEfix = function($ul) {
276 if ( ($.browser.msie && $.browser.version > 6) || $(this).hasClass("iefix") ) {
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(){
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');
291 //handling sub-sliding menu
292 if($$.css('position') == 'static' || $$.parent().hasClass('megamenuSiblings')) {
294 //When the submenu is positioned to the left-hand side, the absolute position should be adjusted as expected
296 _right = $ul.hasClass("ltr") ?_right - $ul.outerWidth()
297 : _right + $$.outerWidth();
299 _left = $ul.hasClass("rtl") ? _left - $ul.outerWidth()
300 : _left + $$.outerWidth();
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();
313 _top += $$.outerHeight();
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({
319 left: (is_rtl_theme) ? '' : _left,
320 right: (is_rtl_theme) ? _right : '',
322 }).on('mouseover',function(){
323 //maintaining the dropdown container
324 var menu = sf.getMenu($menu),
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),
333 clearTimeout(menu.sfTimer);
335 menu.sfTimer = setTimeout(function() {
336 $$.hideSuperfishUl();
337 $(menu).removeClass(sf.defaults['retainClass']);
338 $(menu).hideSuperfishUl();
342 $$.attr("ul-child-id", _id);
346 //restore back the element to the original structure
347 this.each(function(){
348 var _id = $(this).attr("ul-child-id"),
350 $(this).append(_elem.off('mouseover mouseout').css({
362 bcClass: 'sf-breadcrumb',
363 menuClass: 'sf-js-enabled',
364 anchorClass: 'sf-with-ul',
365 arrowClass: 'sf-sub-indicator',
366 shadowClass: 'sf-shadow'
369 hoverClass: 'sfHover',
370 retainClass: 'retainThisItem',
372 pathClass: 'overideThisToUse',
382 // true disables hoverIntent detection
383 onInit: function() {},
384 // callback functions
385 onBeforeShow: function() {},
386 onShow: function() {},
387 onHide: function() {},
389 // true - open first level on click (like classic application menu),
391 // true - if it is RTL theme
395 hideSuperfishUl: function() {
397 not = (o.retainPath === true) ? o.$path: '';
398 o.retainPath = false;
400 var $ul = $(['li.', o.hoverClass].join(''), this).add(this).not
401 (not).removeClass(o.hoverClass).find('>ul').hide().css('visibility', 'hidden');
405 showSuperfishUl: function() {
407 sh = sf.c.shadowClass + '-off',
408 $ul = this.addClass(o.hoverClass).find('>ul:hidden').show().css('visibility', 'visible');
409 sf.positionMenu.call($ul);
411 o.onBeforeShow.call($ul);
412 sf.IEfix.call(this, $ul);
413 $ul.animate(o.animation, o.speed,