//** All Levels Navigational Menu- (c) Dynamic Drive DHTML code library: http://www.dynamicdrive.com //** Script Download/ instructions page: http://www.dynamicdrive.com/dynamicindex1/ddlevelsmenu/ //** Usage Terms: http://www.dynamicdrive.com/notice.htm //** Current version: 4.0 See changelog.txt for details if (typeof dd_domreadycheck=="undefined") //global variable to detect if DOM is ready var dd_domreadycheck=false var ddlevelsmenu={ mql: (window.matchMedia)? window.matchMedia("screen and (max-width: 700px)") : {matches:false, addListener: function(){}}, // CSS media query to switch to mobile menu when matched enableshim: true, //enable IFRAME shim to prevent drop down menus from being hidden below SELECT or FLASH elements? (tip: disable if not in use, for efficiency) arrowpointers:{ downarrow: ["ddlevelsfiles/arrow-down.gif", 11,7], //[path_to_down_arrow, arrowwidth, arrowheight] rightarrow: ["ddlevelsfiles/arrow-right.gif", 12,12], //[path_to_right_arrow, arrowwidth, arrowheight] backarrow: ["ddlevelsfiles/left.gif"], //[path_to_back_arrow, arrowwidth, arrowheight] showarrow: {toplevel: true, sublevel: true} //Show arrow images on top level items and sub level items, respectively? }, hideinterval: 200, //delay in milliseconds before entire menu disappears onmouseout. effects: {enableswipe: true, enableslide: true, enablefade: true, duration: 200}, httpsiframesrc: "about:blank", //If menu is run on a secure (https) page, the IFRAME shim feature used by the script should point to an *blank* page *within* the secure area to prevent an IE security prompt. Specify full URL to that page on your server (leave as is if not applicable). ///No need to edit beyond here//////////////////// topmenuids: [], //array containing ids of all the primary menus on the page menuclone: {}, //object containing a clone of each top level menu plus its sub ULs (for mobile menu sake) topitems: {}, //object array containing all top menu item links subuls: {}, //object array containing all ULs lastactivesubul: {}, //object object containing info for last mouse out menu item's UL topitemsindex: -1, ulindex: -1, hidetimers: {}, //object array timer shimadded: false, nonFF: !/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent), //detect non FF browsers ismobile:navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i) != null, //boolean check for popular mobile browsers mobilezindex: 1000, getoffset:function(what, offsettype){ return (what.offsetParent)? what[offsettype]+this.getoffset(what.offsetParent, offsettype) : what[offsettype] }, getoffsetof:function(el){ el._offsets={left:this.getoffset(el, "offsetLeft"), top:this.getoffset(el, "offsetTop")} }, getwindowsize:function(){ this.docwidth=window.innerWidth? window.innerWidth-10 : this.standardbody.clientWidth-10 this.docheight=window.innerHeight? window.innerHeight-15 : this.standardbody.clientHeight-18 }, gettopitemsdimensions:function(){ for (var m=0; m0){ this.shimmy.topshim.style.left=scrollX+"px" this.shimmy.topshim.style.top=scrollY+"px" this.shimmy.topshim.style.width="99%" this.shimmy.topshim.style.height=topgap+"px" //distance from top window edge to top of menu item } if (bottomgap>0){ this.shimmy.bottomshim.style.left=scrollX+"px" this.shimmy.bottomshim.style.top=header._offsets.top + header._dimensions.h +"px" this.shimmy.bottomshim.style.width="99%" this.shimmy.bottomshim.style.height=bottomgap+"px" //distance from bottom of menu item to bottom window edge } } }, hideshim:function(){ this.shimmy.topshim.style.width=this.shimmy.bottomshim.style.width=0 this.shimmy.topshim.style.height=this.shimmy.bottomshim.style.height=0 }, getoffset:function(what, offsettype){ return (what.offsetParent)? what[offsettype]+this.getoffset(what.offsetParent, offsettype) : what[offsettype] }, buildmenu:function(mainmenuid, header, submenu, submenupos, istoplevel, dir){ header._master=mainmenuid //Indicate which top menu this header is associated with header._pos=submenupos //Indicate pos of sub menu this header is associated with header._istoplevel=istoplevel if (istoplevel){ this.addEvent(header, function(e){ ddlevelsmenu.hidemenu(ddlevelsmenu.subuls[this._master][parseInt(this._pos)].parentNode) }, "click") } this.subuls[mainmenuid][submenupos]=submenu header._dimensions={w:header.offsetWidth, h:header.offsetHeight, submenuw:submenu.offsetWidth, submenuh:submenu.offsetHeight} this.getoffsetof(header) submenu.parentNode.style.left=0 submenu.parentNode.style.top=0 submenu.parentNode.style.visibility="hidden" submenu.style.visibility="hidden" this.addEvent(header, function(e){ //mouseover event if (ddlevelsmenu.ismobile || !ddlevelsmenu.isContained(this, e)){ var submenu=ddlevelsmenu.subuls[this._master][parseInt(this._pos)] if (this._istoplevel){ ddlevelsmenu.css(this, "selected", "add") clearTimeout(ddlevelsmenu.hidetimers[this._master][this._pos]) } ddlevelsmenu.getoffsetof(header) var scrollX=window.pageXOffset? window.pageXOffset : ddlevelsmenu.standardbody.scrollLeft var scrollY=window.pageYOffset? window.pageYOffset : ddlevelsmenu.standardbody.scrollTop var submenurightedge=this._offsets.left + this._dimensions.submenuw + (this._istoplevel && dir=="topbar"? 0 : this._dimensions.w) var submenubottomedge=this._offsets.top + this._dimensions.submenuh //Sub menu starting left position var menuleft=(this._istoplevel? this._offsets.left + (dir=="sidebar"? this._dimensions.w : 0) : this._dimensions.w) if (submenurightedge-scrollX>ddlevelsmenu.docwidth){ // not enough room to drop right? menuleft+= -this._dimensions.submenuw + (this._istoplevel && dir=="topbar" ? this._dimensions.w : -this._dimensions.w) if ( !(this._istoplevel && dir=="topbar") && (this._offsets.left - this._dimensions.submenuw) < scrollX ){ // if no room to drop left either menuleft = 0 } } submenu.parentNode.style.left=menuleft+"px" //Sub menu starting top position var menutop=(this._istoplevel? this._offsets.top + (dir=="sidebar"? 0 : this._dimensions.h) : this.offsetTop) if (submenubottomedge-scrollY>ddlevelsmenu.docheight){ //no room downwards? if (this._dimensions.submenuh0){ //if user clicks on a header (instead of a menu item) e.preventDefault() e.stopPropagation() } } } }, (this.ismobile)? "click" : "mouseover") this.addEvent(header, function(e){ //mouseout event var submenu=ddlevelsmenu.subuls[this._master][parseInt(this._pos)] if (this._istoplevel){ if (!ddlevelsmenu.isContained(this, e) && !ddlevelsmenu.isContained(submenu.parentNode, e)) //hide drop down div if mouse moves out of menu bar item but not into drop down div itself ddlevelsmenu.hidemenu(submenu.parentNode) } else if (!this._istoplevel && !ddlevelsmenu.isContained(this, e)){ ddlevelsmenu.hidemenu(submenu.parentNode) } }, "mouseout") }, buildmobilemenu:function(menuid, ul, dir){ function flattenul(ul, cloneulBol, callback, finalcall){ var callback = callback || function(){} var finalcall = finalcall || function(){} var docfrag = document.createDocumentFragment() var targetul = cloneulBol? ul.cloneNode(true) : ul var subuls = targetul.getElementsByTagName('ul') var subulscount = subuls.length for (var i=subulscount-1; i>=0; i--){ var subul = subuls[i] var header = subuls[i].parentNode docfrag.appendChild( subuls[i] ) callback(i, header, subul) } docfrag.appendChild( targetul ) finalcall(targetul) return docfrag } if (!document.getElementById(menuid+'-mobile')){ // if mobile menu outermost container not created yet var mobilecontainer = document.createElement('nav') mobilecontainer.setAttribute('id', menuid+'-mobile') mobilecontainer.className = 'mobilelevelsmenu' //mobilecontainer.style.display = 'none' document.body.appendChild(mobilecontainer) var mobiletoggle = document.getElementById(menuid+'-mobiletoggle') if (mobiletoggle){ this.addEvent(mobiletoggle, function(e){ ddlevelsmenu.togglemobilemenu(menuid) e.stopPropagation() e.preventDefault() }, "click") } } else{ // else, just reference mobile menu var mobilecontainer = document.getElementById(menuid+'-mobile') } var flattened = flattenul(ul, false, function(i, header, subul){ // loop through header LIs and sub ULs var rightarrow = document.createElement('img') rightarrow.src = ddlevelsmenu.arrowpointers.rightarrow[0] rightarrow.className = "rightarrowpointer" header.getElementsByTagName('a')[0].appendChild(rightarrow) header._submenuref = subul subul.className = 'submenu' var breadcrumb = document.createElement('li') breadcrumb.className = "breadcrumb" breadcrumb.innerHTML = ' ' + header.getElementsByTagName('a')[0].firstChild.nodeValue breadcrumb._headerref = header subul.insertBefore(breadcrumb, subul.getElementsByTagName('li')[0]) ddlevelsmenu.addEvent(header, function(e){ var headermenu = this.parentNode var submenu = this._submenuref ddlevelsmenu.animatemobilesubmenu(submenu, '100%', 0) e.stopPropagation() e.preventDefault() }, "click") ddlevelsmenu.addEvent(breadcrumb, function(e){ var parentmenu = this._headerref.parentNode ddlevelsmenu.animatemobilesubmenu(parentmenu, '-100%', 0) e.stopPropagation() e.preventDefault() }, "click") }, function(topul){ topul.style.zIndex = ddlevelsmenu.mobilezindex++ } ) mobilecontainer.appendChild(flattened) }, setopacity:function(el, value){ el.style.opacity=value if (typeof el.style.opacity!="string"){ //if it's not a string (ie: number instead), it means property not supported el.style.MozOpacity=value try{ if (el.filters){ el.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity="+ value*100 +")" } } catch(e){} } }, animatemobilesubmenu:function(targetul, beforeleft, afterleft){ // See http://stackoverflow.com/questions/18564942/clean-way-to-programmatically-use-css-transitions-from-js/31862081#31862081 this.css(targetul, 'notransition', 'add') targetul.style.zIndex = ddlevelsmenu.mobilezindex++ targetul.style.left = beforeleft window.getComputedStyle(targetul).left // force layout reflow this.css(targetul, 'notransition', 'remove') targetul.style.left = afterleft }, togglemobilemenu:function(mainmenuid, xoffset, yoffset){ var toggler = document.getElementById(mainmenuid + '-mobiletoggle') var mobilemenu = document.getElementById(mainmenuid + '-mobile') if (mobilemenu){ if (!ddlevelsmenu.css(mobilemenu, 'open', 'check')){ ddlevelsmenu.css(mobilemenu, 'open', 'add') if (toggler) ddlevelsmenu.css(toggler, 'open', 'add') } else{ ddlevelsmenu.css(mobilemenu, 'open', 'remove') if (toggler) ddlevelsmenu.css(toggler, 'open', 'remove') } } return false }, showmenu:function(header, submenu, dir){ if (this.effects.enableswipe || this.effects.enablefade){ if (this.effects.enableswipe){ var endpoint=(header._istoplevel && dir=="topbar")? header._dimensions.submenuh : header._dimensions.submenuw submenu.parentNode.style.width=submenu.parentNode.style.height=0 submenu.parentNode.style.overflow="hidden" } if (this.effects.enablefade){ submenu.parentNode.style.width=submenu.offsetWidth+"px" submenu.parentNode.style.height=submenu.offsetHeight+"px" this.setopacity(submenu.parentNode, 0) //set opacity to 0 so menu appears hidden initially } submenu._curanimatedegree=0 submenu.parentNode.style.visibility="visible" submenu.style.visibility="visible" clearInterval(submenu._animatetimer) submenu._starttime=new Date().getTime() //get time just before animation is run submenu._animatetimer=setInterval(function(){ddlevelsmenu.revealmenu(header, submenu, endpoint, dir)}, 10) } else{ submenu.parentNode.style.visibility="visible" submenu.style.visibility="visible" } }, revealmenu:function(header, submenu, endpoint, dir){ var elapsed=new Date().getTime()-submenu._starttime //get time animation has run if (elapsed0) //account for page being in IFRAME, in which above doesn't fire in IE this.addEvent(window, function(){functionref()}, "load"); }, init:function(mainmenuid, dir, mobile){ var desktopmenu = document.getElementById(mainmenuid) var mobilemenu = document.getElementById(mainmenuid + '-mobile') var toggler = document.getElementById(mainmenuid + '-mobiletoggle') if (typeof this.menuclone[mainmenuid] != "object"){ // if sub menus haven't been cloned yet (for sake of mobile menu generation) this.menuclone[mainmenuid] = desktopmenu.getElementsByTagName("ul")[0].cloneNode(true) // clone and get UL inside top menu container var menuclone = this.menuclone[mainmenuid] var alllinks=menuclone.getElementsByTagName("a") for (var i=0; i