1/* 2 * Implements the hamburger menu behaviour 3 * 4 * 5 * @since 4.8 moved from avia.js to own file as some user request to customize this feature 6 */ 7(function($) 8{ 9 "use strict"; 10 11 $(document).ready( function() 12 { 13 $.avia_utilities = $.avia_utilities || {}; 14 15 if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) && 'ontouchstart' in document.documentElement ) 16 { 17 $.avia_utilities.isMobile = true; 18 } 19 else 20 { 21 $.avia_utilities.isMobile = false; 22 } 23 24 //activates the hamburger mobile menu 25 avia_hamburger_menu(); 26 27 }); 28 29 $.avia_utilities = $.avia_utilities || {}; 30 31 function avia_hamburger_menu() 32 { 33 var header = $('#header'), 34 header_main = $('#main .av-logo-container'), //check if we got a top menu that is above the header 35 menu = $('#avia-menu'), 36 burger_wrap = $('.av-burger-menu-main a'), 37 htmlEL = $('html').eq(0), 38 overlay = $('<div class="av-burger-overlay"></div>'), 39 overlay_scroll = $('<div class="av-burger-overlay-scroll"></div>').appendTo(overlay), 40 inner_overlay = $('<div class="av-burger-overlay-inner"></div>').appendTo(overlay_scroll), 41 bgColor = $('<div class="av-burger-overlay-bg"></div>').appendTo(overlay), 42 animating = false, 43 first_level = {}, 44 logo_container = $('.av-logo-container .inner-container'), 45 menu_in_logo_container = logo_container.find('.main_menu'), 46 cloneFirst = htmlEL.is('.html_av-submenu-display-click.html_av-submenu-clone, .html_av-submenu-display-hover.html_av-submenu-clone'), 47 menu_generated = false, 48 cloned_menu_cnt = 0; 49 50 /** 51 * Check for alternate mobile menu 52 */ 53 var alternate = $('#avia_alternate_menu'); 54 if( alternate.length > 0 ) 55 { 56 menu = alternate; 57 } 58 59 var set_list_container_height = function() 60 { 61 //necessary for ios since the height is usually not 100% but 100% - menu bar which can be requested by window.innerHeight 62 if($.avia_utilities.isMobile) 63 { 64 overlay_scroll.outerHeight(window.innerHeight); 65 } 66 }, 67 create_list = function( items , append_to ) 68 { 69 if(!items) return; 70 71 var list, link, current, subitems, megacolumns, sub_current, sub_current_list, new_li, new_ul; 72 73 items.each(function() 74 { 75 current = $(this); 76 subitems = current.find(' > .sub-menu > li'); //find sublists of a regular defined menu 77 if( subitems.length == 0 ) 78 { 79 subitems = current.find(' > .children > li'); //find sublists of a fallback menu 80 } 81 megacolumns = current.find( '.avia_mega_div > .sub-menu > li.menu-item' ); 82 83 // href = '#': we have a custom link that should not link to something - is also in use by megamenu for titles 84 var cur_menu = current.find('>a'); 85 var clone_events = true; 86 87 if( cur_menu.length ) 88 { 89 if( cur_menu.get(0).hash == '#' || 'undefined' == typeof cur_menu.attr('href') || cur_menu.attr('href') == '#' ) 90 { 91 // eventhandler conflict 'click' by megamenu (returns false) - ignore all handlers 92 if( subitems.length > 0 || megacolumns.length > 0 ) 93 { 94 clone_events = false; 95 } 96 } 97 } 98 99 link = cur_menu.clone(clone_events).attr('style',''); 100 101 // megamenus can have '' as url in top menu - allow click event in burger 102 if( 'undefined' == typeof cur_menu.attr('href') ) 103 { 104 link.attr( 'href', '#' ); 105 } 106 107 new_li = $('<li>').append( link ); 108 109 // Copy user set classes for menu items - these must not start with menu-item, page-item, page_item (used by default classes) 110 var cls = []; 111 if( 'undefined' != typeof current.attr('class') ) 112 { 113 cls = current.attr('class').split(/\s+/); 114 $.each( cls, function( index, value ){ 115 if( ( value.indexOf('menu-item') != 0 ) && ( value.indexOf('page-item') < 0 ) && ( value.indexOf('page_item') != 0 ) && ( value.indexOf('dropdown_ul') < 0 ) ) 116 { 117 // 'current-menu-item' is also copied !! 118 new_li.addClass( value ); 119 } 120 return true; 121 }); 122 } 123 124 if( 'undefined' != typeof current.attr('id') && '' != current.attr('id') ) 125 { 126 new_li.addClass(current.attr('id')); 127 } 128 else 129 { 130 // fallback menu has no id -> try to find page id in class 131 $.each( cls, function( index, value ){ 132 if( value.indexOf('page-item-') >= 0 ) 133 { 134 new_li.addClass(value); 135 return false;
136 } 137 }); 138 } 139 140 append_to.append(new_li); 141 142 if(subitems.length) 143 { 144 new_ul = $('<ul class="sub-menu">').appendTo(new_li); 145 146 if(cloneFirst && ( link.get(0).hash != '#' && link.attr('href') != '#' )) 147 { 148 new_li.clone(true).prependTo(new_ul); 149 } 150 151 new_li.addClass('av-width-submenu').find('>a').append('<span class="av-submenu-indicator">'); 152 153 create_list( subitems , new_ul); 154 } 155 else if(megacolumns.length) //if we got no normal sublists try megamenu columns and sublists 156 { 157 new_ul = $('<ul class="sub-menu">').appendTo(new_li); 158 159 if(cloneFirst && ( link.get(0).hash != '#' && link.attr('href') != '#' )) 160 { 161 new_li.clone(true).prependTo(new_ul); 162 } 163 164 megacolumns.each(function(iteration) 165 { 166 var megacolumn = $(this), 167 mega_current = megacolumn.find( '> .sub-menu' ), // can be 0 if only a column is used without submenus 168 mega_title = megacolumn.find( '> .mega_menu_title' ), 169 mega_title_link = mega_title.find('a').attr('href') || "#", 170 current_megas = mega_current.length > 0 ? mega_current.find('>li') : null, 171 mega_title_set = false, 172 mega_link = new_li.find('>a'), 173 hide_enty = ''; 174 175 // ignore columns that have no actual link and no subitems 176 if( ( current_megas === null ) || ( current_megas.length == 0 ) ) 177 { 178 if( mega_title_link == '#' ) 179 { 180 hide_enty = ' style="display: none;"'; 181 } 182 } 183 184 if(iteration == 0) new_li.addClass('av-width-submenu').find('>a').append('<span class="av-submenu-indicator">'); 185 186 //if we got a title split up submenu items into multiple columns 187 if(mega_title.length && mega_title.text() != "") 188 { 189 mega_title_set = true; 190 191 //if we are within the first iteration we got a new submenu, otherwise we start a new one 192 if(iteration > 0) 193 { 194 var check_li = new_li.parents('li').eq(0); 195 196 if(check_li.length) new_li = check_li; 197 198 new_ul = $('<ul class="sub-menu">').appendTo(new_li); 199 } 200 201 202 new_li = $('<li' + hide_enty + '>').appendTo(new_ul); 203 new_ul = $('<ul class="sub-menu">').appendTo(new_li); 204 205 $('<a href="'+mega_title_link+'"><span class="avia-bullet"></span><span class="avia-menu-text">' +mega_title.text()+ '</span></a>').insertBefore(new_ul); 206 mega_link = new_li.find('>a'); 207 208 // Clone if we have submenus 209 if(cloneFirst && ( mega_current.length > 0 ) && ( mega_link.length && mega_link.get(0).hash != '#' && mega_link.attr('href') != '#' )) 210 { 211 new_li.clone(true).addClass('av-cloned-title').prependTo(new_ul); 212 } 213 214 } 215 216 // do not append av-submenu-indicator if no submenus (otherwise link action is blocked !!!) 217 if( mega_title_set && ( mega_current.length > 0 ) ) new_li.addClass('av-width-submenu').find('>a').append('<span class="av-submenu-indicator">'); 218 create_list( current_megas , new_ul); 219 }); 220 221 } 222 223 }); 224 225 burger_wrap.trigger( 'avia_burger_list_created' ); 226 return list; 227 }; 228 229 var burger_ul, burger; 230 231 //prevent scrolling of outer window when scrolling inside 232 $('body').on( 'mousewheel DOMMouseScroll touchmove', '.av-burger-overlay-scroll', function (e) { 233 234 var height = this.offsetHeight, 235 scrollHeight = this.scrollHeight, 236 direction = e.originalEvent.wheelDelta; 237 238 if(scrollHeight != this.clientHeight) 239 { 240 if( (this.scrollTop >= (scrollHeight - height) && direction < 0) || (this.scrollTop <= 0 && direction > 0) ) { 241 e.preventDefault(); 242 } 243 } 244 else 245 { 246 e.preventDefault(); 247 } 248 }); 249 250 //prevent scrolling for the rest of the screen 251 $(document).on( 'mousewheel DOMMouseScroll touchmove', '.av-burger-overlay-bg, .av-burger-overlay-active .av-burger-menu-main', function (e) 252 { 253 e.preventDefault(); 254 }); 255 256 //prevent scrolling on mobile devices 257 var touchPos = {}; 258 259 $(document).on('touchstart', '.av-burger-overlay-scroll', function(e) 260 { 261 touchPos.Y = e.originalEvent.touches[0].clientY; 262 }); 263 264 $(document).on('touchend', '.av-burger-overlay-scroll', function(e) 265 { 266 touchPos = {}; 267 }); 268 269 //prevent rubberband scrolling http://blog.christoffer.me/six-things-i-learnt-about-ios-safaris-rubber-band-scrolling/ 270 $(document).on( 'touchmove', '.av-burger-overlay-scroll', function (e) 271 { 272 if(!touchPos.Y) 273 { 274 touchPos.Y = e.originalEvent.touches[0].clientY; 275 } 276 277 var differenceY = e.originalEvent.touches[0].clientY - touchPos.Y, 278 element = this, 279 top = element.scrollTop, 280 totalScroll = element.scrollHeight, 281 currentScroll = top + element.offsetHeight, 282 direction = differenceY > 0 ? "up" : "down"; 283 284 $('body').get(0).scrollTop = touchPos.body; 285 286 if ( top <= 0 ) 287 { 288 if(direction == "up") e.preventDefault(); 289 290 } else if ( currentScroll >= totalScroll ) 291 { 292 if(direction == "down") e.preventDefault(); 293 } 294 }); 295 296 $(window).on( 'debouncedresize', function (e) 297 {
298 // close burger menu when returning to desktop 299 if(burger && burger.length) 300 { 301 if( ! burger_wrap.is(':visible') ) 302 { 303 burger.filter(".is-active").parents('a').eq(0).trigger('click'); 304 } 305 } 306 307 set_list_container_height(); 308 }); 309 310 //close overlay on overlay click 311 $('.html_av-overlay-side').on( 'click', '.av-burger-overlay-bg', function (e) 312 { 313 e.preventDefault(); 314 burger.parents('a').eq(0).trigger('click'); 315 }); 316 317 //close overlay when smooth scrollign begins 318 $(window).on('avia_smooth_scroll_start', function() 319 { 320 if(burger && burger.length) 321 { 322 burger.filter(".is-active").parents('a').eq(0).trigger('click'); 323 } 324 }); 325 326 327 //toogle hide/show for submenu items 328 $('.html_av-submenu-display-hover').on( 'mouseenter', '.av-width-submenu', function (e) 329 { 330 $(this).children("ul.sub-menu").slideDown('fast'); 331 }); 332 333 $('.html_av-submenu-display-hover').on( 'mouseleave', '.av-width-submenu', function (e) 334 { 335 $(this).children("ul.sub-menu").slideUp('fast'); 336 }); 337 338 $('.html_av-submenu-display-hover').on( 'click', '.av-width-submenu > a', function (e) 339 { 340 e.preventDefault(); 341 e.stopImmediatePropagation(); 342 }); 343 344 // for mobile we use same behaviour as submenu-display-click 345 $('.html_av-submenu-display-hover').on( 'touchstart', '.av-width-submenu > a', function (e) 346 { 347 var menu = $(this); 348 toggle_submenu( menu, e ); 349 }); 350 351 352 //toogle hide/show for submenu items 353 $('.html_av-submenu-display-click').on( 'click', '.av-width-submenu > a', function (e) 354 { 355 var menu = $(this); 356 toggle_submenu( menu, e ); 357 }); 358 359 360 // close mobile menu if click on active menu item 361 $('.html_av-submenu-display-click').on( 'click', '.av-burger-overlay a', function (e) 362 { 363 var loc = window.location.href.match(/(^[^#]*)/)[0]; 364 var cur = $(this).attr('href').match(/(^[^#]*)/)[0]; 365 366 if( cur == loc ) 367 { 368 e.preventDefault(); 369 e.stopImmediatePropagation(); 370 371 burger.parents('a').eq(0).trigger('click'); 372 return false; 373 } 374 return true; 375 }); 376 377 378 function toggle_submenu( menu, e ) 379 { 380 e.preventDefault(); 381 e.stopImmediatePropagation(); 382 383 var parent = menu.parents('li').eq(0); 384 385 parent.toggleClass('av-show-submenu'); 386 387 if(parent.is('.av-show-submenu')) 388 { 389 parent.children("ul.sub-menu").slideDown('fast'); 390 } 391 else 392 { 393 parent.children("ul.sub-menu").slideUp('fast'); 394 } 395 }; 396 397 398 (function normalize_layout() 399 { 400 //if we got the menu outside of the main menu container we need to add it to the container as well 401 if(menu_in_logo_container.length) return; 402 403 var menu2 = $('#header .main_menu').clone(true), 404 ul = menu2.find('ul.av-main-nav'), 405 id = ul.attr('id'); 406 407 if( 'string' == typeof id && '' != id.trim() ) 408 { 409 ul.attr('id', id + '-' + cloned_menu_cnt++ ); 410 } 411 menu2.find('.menu-item:not(.menu-item-avia-special)').remove(); 412 menu2.insertAfter(logo_container.find('.logo').first()); 413 414 //check if we got social icons and append it to the secondary menu 415 var social = $('#header .social_bookmarks').clone(true); 416 if(!social.length) social = $('.av-logo-container .social_bookmarks').clone(true); 417 418 if( social.length ) 419 { 420 menu2.find('.avia-menu').addClass('av_menu_icon_beside'); 421 menu2.append(social); 422 } 423 424 //re select the burger menu if we added a new one 425 burger_wrap = $('.av-burger-menu-main a'); 426 }()); 427 428 429 430 burger_wrap.on('click', function(e) 431 { 432 if(animating) return; 433 burger = $(this).find('.av-hamburger'), 434 animating = true; 435 436 if(!menu_generated) 437 { 438 menu_generated = true; 439 burger.addClass("av-inserted-main-menu"); 440 441 burger_ul = $('<ul>').attr({id:'av-burger-menu-ul', class:''}); 442 var first_level_items = menu.find('> li:not(.menu-item-avia-special)'); //select all first level items that are not special items 443 var list = create_list( first_level_items , burger_ul); 444 445 burger_ul.find('.noMobile').remove(); //remove any menu items with the class noMobile so user can filter manually if he wants 446 burger_ul.appendTo(inner_overlay);
447 first_level = inner_overlay.find('#av-burger-menu-ul > li'); 448 449 if($.fn.avia_smoothscroll){ 450 $('a[href*="#"]', overlay).avia_smoothscroll(overlay); 451 } 452 } 453 454 if(burger.is(".is-active")) 455 { 456 burger.removeClass("is-active"); 457 htmlEL.removeClass("av-burger-overlay-active-delayed"); 458 459 overlay.animate({opacity:0}, function() 460 { 461 overlay.css({display:'none'}); 462 htmlEL.removeClass("av-burger-overlay-active"); 463 animating = false; 464 }); 465 466 } 467 else 468 { 469 set_list_container_height(); 470 471 var offsetTop = header_main.length ? header_main.outerHeight() + header_main.position().top : header.outerHeight() + header.position().top; 472 473 overlay.appendTo($(e.target).parents('.avia-menu')); 474 475 burger_ul.css({padding:( offsetTop ) + "px 0px"}); 476 477 first_level.removeClass('av-active-burger-items'); 478 479 burger.addClass("is-active"); 480 htmlEL.addClass("av-burger-overlay-active"); 481 overlay.css({display:'block'}).animate({opacity:1}, function() 482 { 483 animating = false; 484 }); 485 486 setTimeout(function() 487 { 488 htmlEL.addClass("av-burger-overlay-active-delayed"); 489 490 }, 100); 491 492 first_level.each(function(i) 493 { 494 var _self = $(this); 495 setTimeout(function() 496 { 497 _self.addClass('av-active-burger-items'); 498 }, (i + 1) * 125); 499 }); 500 501 } 502 503 e.preventDefault(); 504 }); 505 506 507 } 508 509 510 511 512 513 514})( jQuery );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.