//** Smooth Navigational Menu- By Dynamic Drive DHTML code library: http://www.dynamicdrive.com
//** Script Download/ instructions page: http://www.dynamicdrive.com/dynamicindex1/ddlevelsmenu/
//** Menu created: Nov 12, 2008
//** Dec 12th, 08" (v1.01): Fixed Shadow issue when multiple LIs within the same UL (level) contain sub menus: http://www.dynamicdrive.com/forums/showthread.php?t=39177&highlight=smooth
//** Feb 11th, 09" (v1.02): The currently active main menu item (LI A) now gets a CSS class of ".selected", including sub menu items.
//** May 1st, 09" (v1.3):
//** 1) Now supports vertical (side bar) menu mode- set "orientation" to 'v'
//** 2) In IE6, shadows are now always disabled
//** July 27th, 09" (v1.31): Fixed bug so shadows can be disabled if desired.
//** Feb 2nd, 10" (v1.4): Adds ability to specify delay before sub menus appear and disappear, respectively. See showhidedelay variable below
//** Dec 17th, 10" (v1.5): Updated menu shadow to use CSS3 box shadows when the browser is FF3.5+, IE9+, Opera9.5+, or Safari3+/Chrome. Only .js file changed.
//** Jun 28th, 2012: Unofficial update adds optional hover images for down and right arrows in the format: filename_over.ext
//** These must be present for whichever or both of the arrow(s) are used and will preload.
//** Dec 23rd, 2012 Unofficial update to fixed configurable z-index, add method option to init "toggle" which activates on click or "hover"
//** which activates on mouse over/out - defaults to "toggle" (click activation), with detection of touch devices to activate on click for them.
//** Add option for when there are two or more menus using "toggle" activation, whether or not all previously opened menus collapse
//** on new menu activation, or just those within that specific menu
//** See: http://www.dynamicdrive.com/forums/showthread.php?72449-PLEASE-HELP-with-Smooth-Navigational-Menu-(v1-51)&p=288466#post288466
//** Feb 7th, 2013 Unofficial update change fixed configurable z-index back to graduated for cases of main UL wrapping. Update off menu click detection in
//** ipad/iphone to touchstart because document click wasn't registering. see: http://www.dynamicdrive.com/forums/showthread.php?72825
//** Feb 14th, 2013 Add window.ontouchstart to means tests for detecting touch browsers - thanks DD!
//** Feb 15th, 2013 Add 'ontouchstart' in window and 'ontouchstart' in document.documentElement to means tests for detecting touch browsers - thanks DD!
//** Feb 20th, 2013 correct for IE 9+ sometimes adding a pixel to the offsetHeight of the top level trigger for horizontal menus
//** Feb 23rd, 2013 move CSS3 shadow adjustment for IE 9+ to the script, add resize event for all browsers to reposition open toggle
//** menus and shadows in window if they would have gone to a different position at the new window dimensions
//** Feb 25th, 2013 (v2.0) All unofficial updates by John merged into official and now called v2.0. Changed "method" option's default value to "hover"
//** May 14th, 2013 (v2.1) Adds class 'repositioned' to menus moved due to being too close to the browser's right edge
//** May 30th, 2013 (v2.1) Change from version sniffing to means testing for jQuery versions which require added code for click toggle event handling
//** Sept 15th, 2013 add workaround for false positives for touch on Chrome
//** Sept 22nd, 2013 (v2.2) Add vertical repositioning if sub menu will not fit in the viewable vertical area. May be turned off by setting
// repositionv: false,
//** in the init. Sub menus that are vertically repositioned will have the class 'repositionedv' added to them.
var ddsmoothmenu = {
///////////////////////// Global Configuration Options: /////////////////////////
//Specify full URL to down and right arrow images (23 is padding-right for top level LIs with drop downs, 6 is for vertical top level items with fly outs):
arrowimages: {down:['downarrowclass', 'http://ulib.arsomsilp.ac.th/ULIB6/library.webbox.cascademenu/smoothmenu/down.gif', 23], right:['rightarrowclass', 'http://ulib.arsomsilp.ac.th/ULIB6/library.webbox.cascademenu/smoothmenu/right.gif', 6]},
transition: {overtime:300, outtime:300}, //duration of slide in/ out animation, in milliseconds
shadow: false, //enable shadow? (offsets now set in ddsmoothmenu.css stylesheet)
showhidedelay: {showdelay: 100, hidedelay: 200}, //set delay in milliseconds before sub menus appear and disappear, respectively
zindexvalue: 140, //set z-index value for menus
closeonnonmenuclick: true, //when clicking outside of any "toggle" method menu, should all "toggle" menus close?
closeonmouseout: false, //when leaving a "toggle" menu, should all "toggle" menus close? Will not work on touchscreen
/////////////////////// End Global Configuration Options ////////////////////////
overarrowre: /(?=\.(gif|jpg|jpeg|png|bmp))/i,
overarrowaddtofilename: '_over',
detecttouch: !!('ontouchstart' in window) || !!('ontouchstart' in document.documentElement) || !!window.ontouchstart || (!!window.Touch && !!window.Touch.length) || !!window.onmsgesturechange || (window.DocumentTouch && window.document instanceof window.DocumentTouch),
detectwebkit: navigator.userAgent.toLowerCase().indexOf("applewebkit") > -1, //detect WebKit browsers (Safari, Chrome etc)
idevice: /ipad|iphone/i.test(navigator.userAgent),
detectie6: (function(){var ie; return (ie = /MSIE (\d+)/.exec(navigator.userAgent)) && ie[1] < 7;})(),
detectie9: (function(){var ie; return (ie = /MSIE (\d+)/.exec(navigator.userAgent)) && ie[1] > 8;})(),
ie9shadow: function(){},
css3support: typeof document.documentElement.style.boxShadow === 'string' || (!document.all && document.querySelector), //detect browsers that support CSS3 box shadows (ie9+ or FF3.5+, Safari3+, Chrome etc)
prevobjs: [], menus: null,
executelink: function($, prevobjs, e){
var prevscount = prevobjs.length, link = e.target;
while(--prevscount > -1){
if(prevobjs[prevscount] === this){
prevobjs.splice(prevscount, 1);
if(link.href !== ddsmoothmenu.emptyhash && link.href && $(link).is('a') && !$(link).children('span.' + ddsmoothmenu.arrowimages.down[0] +', span.' + ddsmoothmenu.arrowimages.right[0]).length){
if(link.target && link.target !== '_self'){
window.open(link.href, link.target);
} else {
window.location.href = link.href;
}
e.stopPropagation();
}
}
}
},
repositionv: function($subul, $link, newtop, winheight, doctop, method, menutop){
menutop = menutop || 0;
var topinc = 0, doclimit = winheight + doctop;
$subul.css({top: newtop, display: 'block'});
while($subul.offset().top < doctop) {
$subul.css({top: ++newtop});
++topinc;
}
if(!topinc && $link.offset().top + $link.outerHeight() < doclimit && $subul.data('height') + $subul.offset().top > doclimit){
$subul.css({top: doctop - $link.parents('ul').last().offset().top - $link.position().top});
}
method === 'toggle' && $subul.css({display: 'none'});
if(newtop !== menutop){$subul.addClass('repositionedv');}
return [topinc, newtop];
},
updateprev: function($, prevobjs, $curobj){
var prevscount = prevobjs.length, prevobj, $indexobj = $curobj.parents().add(this);
while(--prevscount > -1){
if($indexobj.index((prevobj = prevobjs[prevscount])) < 0){
$(prevobj).trigger('click', [1]);
prevobjs.splice(prevscount, 1);
}
}
prevobjs.push(this);
},
subulpreventemptyclose: function(e){
var link = e.target;
if(link.href === ddsmoothmenu.emptyhash && $(link).parent('li').find('ul').length < 1){
e.preventDefault();
e.stopPropagation();
}
},
getajaxmenu: function($, setting, nobuild){ //function to fetch external page containing the panel DIVs
var $menucontainer=$('#'+setting.contentsource[0]); //reference empty div on page that will hold menu
$menucontainer.html("Loading Menu...");
$.ajax({
url: setting.contentsource[1], //path to external menu file
async: true,
error: function(ajaxrequest){
$menucontainer.html('Error fetching content. Server Response: '+ajaxrequest.responseText);
},
success: function(content){
$menucontainer.html(content);
!!!nobuild && ddsmoothmenu.buildmenu($, setting);
}
});
},
closeall: function(e){
var smoothmenu = ddsmoothmenu, prevscount;
if(!smoothmenu.globaltrackopen){return;}
if(e.type === 'mouseleave' || ((e.type === 'click' || e.type === 'touchstart') && smoothmenu.menus.index(e.target) < 0)){
prevscount = smoothmenu.prevobjs.length;
while(--prevscount > -1){
$(smoothmenu.prevobjs[prevscount]).trigger('click');
smoothmenu.prevobjs.splice(prevscount, 1);
}
}
},
emptyhash: $('').get(0).href,
buildmenu: function($, setting){
var smoothmenu = ddsmoothmenu;
smoothmenu.globaltrackopen = smoothmenu.closeonnonmenuclick || smoothmenu.closeonmouseout;
var zsub = 0; //subtractor to be incremented so that each top level menu can be covered by previous one's drop downs
var prevobjs = smoothmenu.globaltrackopen? smoothmenu.prevobjs : [];
var $mainparent = $("#"+setting.mainmenuid).removeClass("ddsmoothmenu ddsmoothmenu-v").addClass(setting.classname || "ddsmoothmenu");
setting.repositionv = setting.repositionv !== false;
var $mainmenu = $mainparent.find('>ul'); //reference main menu UL
var method = smoothmenu.detecttouch? 'toggle' : setting.method === 'toggle'? 'toggle' : 'hover';
var $topheaders = $mainmenu.find('>li>ul').parent();//has('ul');
var orient = setting.orientation!='v'? 'down' : 'right', $parentshadow = $(document.body);
$mainmenu.click(function(e){e.target.href === smoothmenu.emptyhash && e.preventDefault();});
if(method === 'toggle') {
if(smoothmenu.globaltrackopen){
smoothmenu.menus = smoothmenu.menus? smoothmenu.menus.add($mainmenu.add($mainmenu.find('*'))) : $mainmenu.add($mainmenu.find('*'));
}
if(smoothmenu.closeonnonmenuclick){
if(orient === 'down'){$mainparent.click(function(e){e.stopPropagation();});}
$(document).unbind('click.smoothmenu').bind('click.smoothmenu', smoothmenu.closeall);
if(smoothmenu.idevice){
document.removeEventListener('touchstart', smoothmenu.closeall, false);
document.addEventListener('touchstart', smoothmenu.closeall, false);
}
} else if (setting.closeonnonmenuclick){
if(orient === 'down'){$mainparent.click(function(e){e.stopPropagation();});}
$(document).bind('click.' + setting.mainmenuid, function(e){$mainmenu.find('li>a.selected').parent().trigger('click');});
if(smoothmenu.idevice){
document.addEventListener('touchstart', function(e){$mainmenu.find('li>a.selected').parent().trigger('click');}, false);
}
}
if(smoothmenu.closeonmouseout){
var $leaveobj = orient === 'down'? $mainparent : $mainmenu;
$leaveobj.bind('mouseleave.smoothmenu', smoothmenu.closeall);
} else if (setting.closeonmouseout){
var $leaveobj = orient === 'down'? $mainparent : $mainmenu;
$leaveobj.bind('mouseleave.smoothmenu', function(){$mainmenu.find('li>a.selected').parent().trigger('click');});
}
if(!$('style[title="ddsmoothmenushadowsnone"]').length){
$('head').append('');
}
var shadowstimer;
$(window).bind('resize scroll', function(){
clearTimeout(shadowstimer);
var $selected = $mainmenu.find('li>a.selected').parent(),
$shadows = $('.ddshadow').addClass('ddsmoothmenushadowsnone');
$selected.eq(0).trigger('click');
$selected.trigger('click');
shadowstimer = setTimeout(function(){$shadows.removeClass('ddsmoothmenushadowsnone');}, 100);
});
}
$topheaders.each(function(){
var $curobj=$(this).css({zIndex: (setting.zindexvalue || smoothmenu.zindexvalue) + zsub--}); //reference current LI header
var $subul=$curobj.children('ul:eq(0)').css({display:'block'}).data('timers', {});
var $link = $curobj.children("a:eq(0)").css({paddingRight: smoothmenu.arrowimages[orient][2]}).append( //add arrow images
''
);
var dimensions = {
w : $link.outerWidth(),
h : $curobj.innerHeight(),
subulw : $subul.outerWidth(),
subulh : $subul.outerHeight()
};
var menutop = orient === 'down'? dimensions.h : 0;
$subul.css({top: menutop});
function restore(){$link.removeClass('selected');}
method === 'toggle' && $subul.click(smoothmenu.subulpreventemptyclose);
$curobj[method](
function(e){
if(!$curobj.data('headers')){
smoothmenu.buildsubheaders($, $subul.find('>li>ul').parent(), setting, method, prevobjs);
$curobj.data('headers', true).find('>ul').each(function(i, ul){
var $ul = $(ul);
$ul.data('height', $ul.outerHeight());
}).css({display:'none', visibility:'visible'});
}
method === 'toggle' && smoothmenu.updateprev.call(this, $, prevobjs, $curobj);
clearTimeout($subul.data('timers').hidetimer);
$link.addClass('selected');
$subul.data('timers').showtimer=setTimeout(function(){
var menuleft = orient === 'down'? 0 : dimensions.w;
var menumoved = menuleft, newtop, doctop, winheight, topinc = 0;
menuleft=($curobj.offset().left+menuleft+dimensions.subulw>$(window).width())? (orient === 'down'? -dimensions.subulw+dimensions.w : -dimensions.w) : menuleft; //calculate this sub menu's offsets from its parent
menumoved = menumoved !== menuleft;
$subul.css({top: menutop}).removeClass('repositionedv');
if(setting.repositionv && $link.offset().top + menutop + $subul.data('height') > (winheight = $(window).height()) + (doctop = $(document).scrollTop())){
newtop = (orient === 'down'? 0 : $link.outerHeight()) - $subul.data('height');
topinc = smoothmenu.repositionv($subul, $link, newtop, winheight, doctop, method, menutop)[0];
}
$subul.css({left:menuleft, width:dimensions.subulw}).stop(true, true).animate({height:'show',opacity:'show'}, smoothmenu.transition.overtime, function(){this.style.removeAttribute && this.style.removeAttribute('filter');});
if(menumoved){$subul.addClass('repositioned');} else {$subul.removeClass('repositioned');}
if (setting.shadow){
if(!$curobj.data('$shadow')){
$curobj.data('$shadow', $('').addClass('ddshadow toplevelshadow').prependTo($parentshadow).css({zIndex: $curobj.css('zIndex')})); //insert shadow DIV and set it to parent node for the next shadow div
}
smoothmenu.ie9shadow($curobj.data('$shadow'));
var offsets = $subul.offset();
var shadowleft = offsets.left;
var shadowtop = offsets.top;
$curobj.data('$shadow').css({overflow: 'visible', width:dimensions.subulw, left:shadowleft, top:shadowtop}).stop(true, true).animate({height:dimensions.subulh}, smoothmenu.transition.overtime);
}
}, smoothmenu.showhidedelay.showdelay);
},
function(e, speed){
var $shadow = $curobj.data('$shadow');
if(method === 'hover'){restore();}
else{smoothmenu.executelink.call(this, $, prevobjs, e);}
clearTimeout($subul.data('timers').showtimer);
$subul.data('timers').hidetimer=setTimeout(function(){
$subul.stop(true, true).animate({height:'hide', opacity:'hide'}, speed || smoothmenu.transition.outtime, function(){method === 'toggle' && restore();});
if ($shadow){
if (!smoothmenu.css3support && smoothmenu.detectwebkit){ //in WebKit browsers, set first child shadow's opacity to 0, as "overflow:hidden" doesn't work in them
$shadow.children('div:eq(0)').css({opacity:0});
}
$shadow.stop(true, true).animate({height:0}, speed || smoothmenu.transition.outtime, function(){if(method === 'toggle'){this.style.overflow = 'hidden';}});
}
}, smoothmenu.showhidedelay.hidedelay);
}
); //end hover/toggle
}); //end $topheaders.each()
},
buildsubheaders: function($, $headers, setting, method, prevobjs){
//setting.$mainparent.data('$headers').add($headers);
$headers.each(function(){ //loop through each LI header
var smoothmenu = ddsmoothmenu;
var $curobj=$(this).css({zIndex: $(this).parent('ul').css('z-index')}); //reference current LI header
var $subul=$curobj.children('ul:eq(0)').css({display:'block'}).data('timers', {}), $parentshadow;
method === 'toggle' && $subul.click(smoothmenu.subulpreventemptyclose);
var $link = $curobj.children("a:eq(0)").append( //add arrow images
''
);
var dimensions = {
w : $link.outerWidth(),
subulw : $subul.outerWidth(),
subulh : $subul.outerHeight()
};
$subul.css({top: 0});
function restore(){$link.removeClass('selected');}
$curobj[method](
function(e){
if(!$curobj.data('headers')){
smoothmenu.buildsubheaders($, $subul.find('>li>ul').parent(), setting, method, prevobjs);
$curobj.data('headers', true).find('>ul').each(function(i, ul){
var $ul = $(ul);
$ul.data('height', $ul.height());
}).css({display:'none', visibility:'visible'});
}
method === 'toggle' && smoothmenu.updateprev.call(this, $, prevobjs, $curobj);
clearTimeout($subul.data('timers').hidetimer);
$link.addClass('selected');
$subul.data('timers').showtimer=setTimeout(function(){
var menuleft= dimensions.w;
var menumoved = menuleft, newtop, doctop, winheight, topinc = 0;
menuleft=($curobj.offset().left+menuleft+dimensions.subulw>$(window).width())? -dimensions.w : menuleft; //calculate this sub menu's offsets from its parent
menumoved = menumoved !== menuleft;
$subul.css({top: 0}).removeClass('repositionedv');
if(setting.repositionv && $link.offset().top + $subul.data('height') > (winheight = $(window).height()) + (doctop = $(document).scrollTop())){
newtop = $link.outerHeight() - $subul.data('height');
topinc = smoothmenu.repositionv($subul, $link, newtop, winheight, doctop, method);
newtop = topinc[1];
topinc = topinc[0];
}
$subul.css({left:menuleft, width:dimensions.subulw}).stop(true, true).animate({height:'show',opacity:'show'}, smoothmenu.transition.overtime, function(){this.style.removeAttribute && this.style.removeAttribute('filter');});
if(menumoved){$subul.addClass('repositioned');} else {$subul.removeClass('repositioned');}
if (setting.shadow){
if(!$curobj.data('$shadow')){
$parentshadow = $curobj.parents("li:eq(0)").data('$shadow');
$curobj.data('$shadow', $('').addClass('ddshadow').prependTo($parentshadow).css({zIndex: $parentshadow.css('z-index')})); //insert shadow DIV and set it to parent node for the next shadow div
}
var offsets = $subul.offset();
var shadowleft = menuleft;
var shadowtop = $curobj.position().top - (newtop? $subul.data('height') - $link.outerHeight() - topinc : 0);
if (smoothmenu.detectwebkit && !smoothmenu.css3support){ //in WebKit browsers, restore shadow's opacity to full
$curobj.data('$shadow').css({opacity:1});
}
$curobj.data('$shadow').css({overflow: 'visible', width:dimensions.subulw, left:shadowleft, top:shadowtop}).stop(true, true).animate({height:dimensions.subulh}, smoothmenu.transition.overtime);
}
}, smoothmenu.showhidedelay.showdelay);
},
function(e, speed){
var $shadow = $curobj.data('$shadow');
if(method === 'hover'){restore();}
else{smoothmenu.executelink.call(this, $, prevobjs, e);}
clearTimeout($subul.data('timers').showtimer);
$subul.data('timers').hidetimer=setTimeout(function(){
$subul.stop(true, true).animate({height:'hide', opacity:'hide'}, speed || smoothmenu.transition.outtime, function(){
method === 'toggle' && restore();
});
if ($shadow){
if (!smoothmenu.css3support && smoothmenu.detectwebkit){ //in WebKit browsers, set first child shadow's opacity to 0, as "overflow:hidden" doesn't work in them
$shadow.children('div:eq(0)').css({opacity:0});
}
$shadow.stop(true, true).animate({height:0}, speed || smoothmenu.transition.outtime, function(){if(method === 'toggle'){this.style.overflow = 'hidden';}});
}
}, smoothmenu.showhidedelay.hidedelay);
}
); //end hover/toggle for subheaders
}); //end $headers.each() for subheaders
},
init: function(setting){
if(this.detectie6 && parseFloat(jQuery.fn.jquery) > 1.3){
this.init = function(setting){
if (typeof setting.contentsource=="object"){ //if external ajax menu
jQuery(function($){ddsmoothmenu.getajaxmenu($, setting, 'nobuild');});
}
return false;
};
jQuery('link[href*="ddsmoothmenu"]').attr('disabled', true);
jQuery(function($){
alert('You Seriously Need to Update Your Browser!\n\nDynamic Drive Smooth Navigational Menu Showing Text Only Menu(s)\n\nDEVELOPER\'s NOTE: This script will run in IE 6 when using jQuery 1.3.2 or less,\nbut not real well.');
$('link[href*="ddsmoothmenu"]').attr('disabled', true);
});
return this.init(setting);
}
var mainmenuid = '#' + setting.mainmenuid, right, down, stylestring = ['\n'], stylesleft = setting.arrowswap? 4 : 2;
function addstyles(){
if(stylesleft){return;}
if (typeof setting.customtheme=="object" && setting.customtheme.length==2){ //override default menu colors (default/hover) with custom set?
var mainselector=(setting.orientation=="v")? mainmenuid : mainmenuid+', '+mainmenuid;
stylestring.push([mainselector,' ul li a {background:',setting.customtheme[0],';}\n',
mainmenuid,' ul li a:hover {background:',setting.customtheme[1],';}'].join(''));
}
stylestring.push('\n\n'].join('\n'));
ddsmoothmenu.ie9shadow = function(){}; //becomes empty function after running once
}; //end Scripted CSS Patch
var jqheight = $.fn.height, jqwidth = $.fn.width; //begin jQuery Patch for IE 9+ .height() and .width()
$.extend($.fn, {
height: function(){
var obj = this.get(0);
if(this.length < 1 || arguments.length || obj === window || obj === document){
return jqheight.apply(this, arguments);
}
return parseFloat(document.defaultView.getComputedStyle(obj, null).getPropertyValue('height'));
},
innerHeight: function(){
if(this.length < 1){return null;}
var val = this.height(), obj = this.get(0), getter = document.defaultView.getComputedStyle(obj, null);
val += parseInt(getter.getPropertyValue('padding-top'));
val += parseInt(getter.getPropertyValue('padding-bottom'));
return val;
},
outerHeight: function(bool){
if(this.length < 1){return null;}
var val = this.innerHeight(), obj = this.get(0), getter = document.defaultView.getComputedStyle(obj, null);
val += parseInt(getter.getPropertyValue('border-top-width'));
val += parseInt(getter.getPropertyValue('border-bottom-width'));
if(bool){
val += parseInt(getter.getPropertyValue('margin-top'));
val += parseInt(getter.getPropertyValue('margin-bottom'));
}
return val;
},
width: function(){
var obj = this.get(0);
if(this.length < 1 || arguments.length || obj === window || obj === document){
return jqwidth.apply(this, arguments);
}
return parseFloat(document.defaultView.getComputedStyle(obj, null).getPropertyValue('width'));
},
innerWidth: function(){
if(this.length < 1){return null;}
var val = this.width(), obj = this.get(0), getter = document.defaultView.getComputedStyle(obj, null);
val += parseInt(getter.getPropertyValue('padding-right'));
val += parseInt(getter.getPropertyValue('padding-left'));
return val;
},
outerWidth: function(bool){
if(this.length < 1){return null;}
var val = this.innerWidth(), obj = this.get(0), getter = document.defaultView.getComputedStyle(obj, null);
val += parseInt(getter.getPropertyValue('border-right-width'));
val += parseInt(getter.getPropertyValue('border-left-width'));
if(bool){
val += parseInt(getter.getPropertyValue('margin-right'));
val += parseInt(getter.getPropertyValue('margin-left'));
}
return val;
}
}); //end jQuery Patch for IE 9+ .height() and .width()
})(jQuery);
}