$(document).ready(main); $(window).on("load", function() { /* All elements on the page have exact dimensions once the images are loaded. Make sure to readjust the footer position. */ adjustFooter(); }); var referrerList = [ {url: '.+tanukisoftware\.com\/.+'}, {url: '.+tanukisoftware\.org\/.+'}, {url: '.+tsl\.local\/.+'} ]; var floatDivInitialized = false; var windowWidth = $(window).width(); var brandBtn; var otherBtn; var switchTabsImgs; /* This function is called when the document is ready */ function main() { if (doSwitchTabs) { brandBtn = $("#nav-container-top .switchable.navbar-brand"); otherBtn = $("#nav-container-top .switchable").not(brandBtn); // invert the link's positions for the switchTabs() animation which will be executed at the end of this function otherBtn.insertBefore(brandBtn); brandBtn.show(); otherBtn.show(); } if($("#sidebar").is(":visible") && $("#content").length) { floatDiv($("#sidebar"), $("#content").outerHeight(true)); } // only collapse if the user is browsing the site if(matchReferrer(referrerList) == true) { $("div[class$='collapse']").toggle(); $("span[class$='details']").toggleClass('details-flip'); } $('[data-toggle="slide-collapse"]').on('click', function() { toggleMenu($($(this).data('target'))); }); try { document.createEvent("TouchEvent"); // On mobile where the touch event is supported, handle dropdown menus with clicks (otherwise they would not disappear when scolling). handleDropdownEvents(); } catch (e) { $("#nav-container-top .navbar-right .dropdown").addClass("desktop"); } if($('.welcome').length == 0) { $("#nav-container-top li.dropdown.login-menu").addClass("not-logged-in"); } $('#menu .dropdown>a').on('click', function() { $dpdwn = $(this).parent(); if (!$dpdwn.hasClass("open")) { $ul = $dpdwn.parent(); $offset = ($(this).outerHeight(true) * ($ul.find(">li:not(.visible-1030)").index($dpdwn))) + parseInt($ul.css("margin-top")); $menu = $("#menu"); // scroll back to the current dropdown if the scrolling was too low. if ($menu.scrollTop() > $offset) $menu.animate({ scrollTop: $offset }, "fast"); } }); $('#toggle-search').on('click', function() { $sb = $("#cse-search-box"); if($sb.is(":visible")) { $('#menu > .nav:last-child').removeClass('disabled'); } else { // disable the "about us" subpanel which would be displayed on the top of the search-box if the mouse passes over it. $('#menu > .nav:last-child').addClass('disabled'); // set the focus on the search-box (this needs a delay as the box needs to be set visible first). setTimeout(function() { $sb.find('.form-control').get(0).focus(); }, 10); } }); $(window).on('resize', function(){ if ($(this).width() !== windowWidth) { // Hide the panel menu when the width of the window changed (otherwise if you go back to desktop mode, the menu would be broken) $('#overlay').removeAttr('style'); $('#menu').removeAttr('style'); $header = $('header'); $header.removeClass('style'); $header.add($('body')).removeClass('fixed-position'); $header.next().css({'margin-top': '0'}); windowWidth = $(this).width(); } // It is possible that floatDiv was not yet called if the sidebar was not visible on a narrow window. Call it now if it becomes visible. if (!floatDivInitialized && $("#sidebar").is(":visible") && $("#content").length) { floatDiv($("#sidebar"), $("#content").innerHeight()); } adjustFooter(); }); adjustFooter(); $collapsible = $('.collapsible'); if($collapsible.length > 0) { // If the page contains collapsible panel, the height may change as they are expanded/reduced. $collapsible.on('click', function() { // The default duration for a collapsible is 300ms: adjust at 1/3, 2/3, 100% + 10ms. This is done to avoid an effect where the footer would jump after the panel is fully expanded. setTimeout(adjustFooter, 100); setTimeout(adjustFooter, 200); setTimeout(adjustFooter, 310); }); // When the page loads, it may take a few ms for all panels to expand. setTimeout(adjustFooter, 310); // sometimes effects are slower than configured, so make sure to readjust the footer after a while setTimeout(adjustFooter, 500); } if (doSwitchTabs) { switchTabsImgs = $(".switchable img"); if (switchTabsImgs[0].complete && switchTabsImgs[1].complete) { switchTabs(); } else { if (!switchTabsImgs[0].complete) switchTabsImgs[0].addEventListener('load', switchTabs); if (!switchTabsImgs[1].complete) switchTabsImgs[1].addEventListener('load', switchTabs); } } } function matchReferrer(referrerList) { var referrer = document.referrer.toLowerCase(); for(var i=0; i < referrerList.length; i++) { if(referrer.match(referrerList[i].url) != null ) { return true; } } return false; } function toggleMenu($navMenuCont) { $overlay = $('#overlay'); $overlay.unbind('touchstart'); $header = $('header'); // Make sure all dropdown are closed before showing the panel. $navMenuCont.find('.dropdown').removeClass('open'); // If the user clicks multiple times on the menu button, it's possible that the 'left' property stays in the style which would break the animation below. $style = $navMenuCont.attr('style'); if (typeof $style !== 'undefined' && $style !== false && $style.indexOf('left:') >= 0) { $navMenuCont.attr('style', $style.replace(/left:.*?;/gi, '')); } // Same with the opacity of the overlay. $style = $overlay.attr('style'); if (typeof $style !== 'undefined' && $style !== false && $style.indexOf('opacity:') >= 0) { $overlay.attr('style', $style.replace(/opacity:.*?;/gi, '')); } $scrollTop = $(window).scrollTop(); $navMenuCont.css({'height': 'calc(100vh - ' + ($header.height() - $scrollTop) + 'px)'}); // mobile menu slide from the left, and in parrallel an overlay will fad-in. // https://stackoverflow.com/questions/31641352/how-to-slide-nav-bar-from-left-instead-from-top // https://stackoverflow.com/questions/1251300/how-to-run-two-jquery-animations-simultaneously $overlay.fadeToggle({ duration: 250, queue: false }); $navMenuCont.animate({ left: 'toggle' }, { duration: 250, queue: false, complete:function() { if (($navMenuCont.is(":visible"))) { $header.css({'top': -($scrollTop) + 'px'}); $header.add($('body')).addClass('fixed-position'); $header.next().css({'margin-top': $header.height() + 'px'}); $header.animate({ top: '0' }, { duration: 250, complete:function() { $navMenuCont.css({'height': 'calc(100vh - ' + $header.height() + 'px)'}); $overlay.bind('touchstart', function() { toggleMenu($($('[data-toggle="slide-collapse"]').data('target'))); }); } } ); } else { $header.animate({ top: -($scrollTop) + 'px' }, { duration: 250, complete:function() { $header.css({'top': '0'}); $header.add($('body')).removeClass('fixed-position'); $header.next().css({'margin-top': '0'}); } } ); } } } ); } /** * On mobiles, the :hover state is activated when touching a dropdown but doesn't disappear * until the user taps somewhere else on the screen (srolling doesn't count as a tap). * To hide the dropdown menus when scrolling, we need to toggle them using click events. * See: https://www.prowebdesign.ro/how-to-deal-with-hover-on-touch-screen-devices/ */ function handleDropdownEvents() { $("#nav-container-top .navbar-right .dropdown").on('click', function(e) { $dps = $(".dropdown"); $dp = $dps.filter(this); // Always make sure to collapse all other dropdowns. $dps.not($dp).removeClass("mobileTouched"); // Toggle the current dropdown. $dp.toggleClass("mobileTouched"); setTimeout(function() { // A tap/click anywhere outside the current dropdown should hide it. $(document).not($dp).not($dp.find(".dropdown-menu")).click(function() { $dp.removeClass("mobileTouched"); }); }, 10); // Stop propagation so we don't fall to the $(document) click handler. e.stopPropagation(); }); $(window).scroll(function() { // Hide dropdowns when scrolling. $(".dropdown").removeClass("mobileTouched"); }); } function floatDiv(fltdiv, max_height) { floatDivInitialized = true; step = 10; fltdiv_top = fltdiv.offset().top; fltdiv_height = fltdiv.height(); $(window).scroll(function () { if($(this).scrollTop() > fltdiv_top) { delta = $(this).scrollTop() - fltdiv_top; if( delta+step+fltdiv_height >= max_height ) { top_value = max_height - fltdiv_height; } else { top_value = delta + step; } fltdiv.css("top", top_value); } else { fltdiv.css("top", 0); } }); } /** * Adjust the footer size. */ function adjustFooter() { $html = $('html'); $html.removeClass("footerBottom"); if ($('body').height() <= window.innerHeight) { $html.addClass("footerBottom"); } } function setEqualHeight(columns) { var tallest_column = 0; columns.each( function() { current_height = $(this).height(); if(current_height > tallest_column) { tallest_column = current_height; } } ); columns.height(tallest_column); } function getParameterByName(name) { url = window.location.href; name = name.replace(/[\[\]]/g, "\\$&"); var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex.exec(url); if (!results) return null; if (!results[2]) return ''; return decodeURIComponent(results[2].replace(/\+/g, " ")); } function switchTabs(e) { if (!(switchTabsImgs[0].complete) || !(switchTabsImgs[1].complete)) { return; } if (otherBtn.length) { $.when(brandBtn.animate({ left: -otherBtn.outerWidth(true) }, 400), otherBtn.animate({ left: brandBtn.outerWidth(true) }, 400)).done(function () { otherBtn.css('left', '0px'); brandBtn.css('left', '0px'); brandBtn.insertBefore(otherBtn); }); } }