/** Submenu hover adjustment. Automatically move up a submenu to fit into screen when some part of it goes beneath window. */ ace.sidebar_hoverable = function($) { if( !('querySelector' in document) || !('removeProperty' in document.body.style) ) return; //ignore IE8 & below //on window resize or sidebar expand/collapse a previously "pulled up" submenu should be reset back to its default position //for example if "pulled up" in "responsive-min" mode, in "fullmode" should not remain "pulled up" ace.helper.sidebar_hover = { reset : function() { $sidebar.find('.submenu').each(function() { var sub = this, li = this.parentNode; if(sub) { sub.style.removeProperty('top') sub.style.removeProperty('bottom'); var menu_text = li.querySelector('.menu-text'); if(menu_text) { menu_text.style.removeProperty('margin-top') } } if( li.className.lastIndexOf('_up') >= 0 ) {//has .pull_up $(li).removeClass('pull_up'); } }); } } var is_navbar_fixed = 'getComputedStyle' in window ? //navbar.offsetHeight is used to force redraw and recalculate 'sidebar.style.position' esp for webkit! function() { navbar.offsetHeight; return window.getComputedStyle(navbar).position == 'fixed' } : function() { navbar.offsetHeight; return $navbar.css('position') == 'fixed' } $(window).on('resize.ace_hover', function() { navbar_fixed = is_navbar_fixed(); ace.helper.sidebar_hover.reset(); }) $(document).on('settings.ace.hover', function(e, event_name, event_val) { if(event_name == 'sidebar_collapsed') ace.helper.sidebar_hover.reset(); else if(event_name == 'navbar_fixed') navbar_fixed = event_val; }) /////////////////////////////////////////////// var $sidebar = $('.sidebar').eq(0), sidebar = $sidebar.get(0), nav_list = $sidebar.find('.nav-list').get(0); var $navbar = $('.navbar').eq(0), navbar = $navbar.get(0), horizontal = $sidebar.hasClass('h-sidebar'), navbar_fixed = $navbar.css('position') == 'fixed'; $sidebar.find('.submenu').parent().addClass('hsub');//add .hsub (has-sub) class //some mobile browsers don't have mouseenter $sidebar.on('mouseenter.ace_hover', '.nav-list li.hsub', function (e) { //ignore if collapsible mode (mobile view .navbar-collapse) so it doesn't trigger submenu movements //or return if horizontal but not mobile_view (style 1&3) if( ace.vars['collapsible'] || (horizontal && !ace.vars['mobile_view']) ) return; var sub = this.querySelector('.submenu'); if(sub) { //try to move/adjust submenu if the parent is a li.hover if( ace.helper.hasClass(this, 'hover') ) { adjust_submenu.call(this, sub); } //or if submenu is minimized else if( this.parentNode == nav_list && ace.vars['minimized'] ) { adjust_submenu.call(this, sub); } } }) var $diff = 50; function adjust_submenu(sub) { var $sub = $(sub); sub.style.removeProperty('top') sub.style.removeProperty('bottom'); var menu_text = null if( ace.vars['minimized'] && (menu_text = sub.parentNode.querySelector('.menu-text')) ) { //2nd level items don't have .menu-text menu_text.style.removeProperty('margin-top') } var off = $sub.offset(); var scroll = ace.helper.scrollTop(); var pull_up = false; var $scroll = scroll if( navbar_fixed ) { $scroll += navbar.clientHeight + 1; //let's avoid our submenu from going below navbar //because of chrome z-index stacking issue and firefox's normal .submenu over fixed .navbar flicker issue } var sub_h = sub.scrollHeight; if(menu_text) { sub_h += 40; off.top -= 40; } var sub_bottom = parseInt(off.top + sub_h) var diff //if the bottom of menu is going to go below visible window if( (diff = sub_bottom - (window.innerHeight + scroll - 50)) > 0 ) { //if it needs to be moved top a lot! use bottom unless it makes it go out of window top if(sub_h - diff < $diff && off.top - diff > $scroll ) { sub.style.top = 'auto'; sub.style.bottom = '-10px'; if( menu_text ) { //menu_text.style.marginTop = -(sub_h - 10)+'px'; menu_text.style.marginTop = -(sub_h - 50)+'px';// -10 - 40 for the above extra 40 pull_up = true; } } else { //when top of menu goes out of browser window's top or below fixed navbar if( off.top - diff < $scroll ) { diff = off.top - $scroll; } //when bottom of menu goes above bottom of parent LI /** else */ if(sub_bottom - diff < off.top + $diff) { diff -= $diff; } var at_least = menu_text ? 40 : 20;//it we are going to move up less than at_least, then ignore if( diff > at_least ) { sub.style.top = -(diff) + 'px'; if( menu_text ) { menu_text.style.marginTop = -(diff) + 'px'; pull_up = true; } } } } //pull_up means, pull the menu up a little bit, and some styling may need to change var pos = this.className.lastIndexOf('pull_up');//pull_up if (pull_up) { if (pos == -1) this.className = this.className + ' pull_up'; } else { if (pos >= 0) this.className = this.className.replace(/(^|\s)pull_up($|\s)/ , ''); } //again force redraw for safari! if( ace.vars['safari'] ) { ace.helper.redraw(sub) } } }