1/* global jQuery */ 2( function( $ ){ 3 4 var FrontMenu = { 5 6 init: function() { 7 8 this.header_wrap = $( '#header' ); 9 this.header_wrap_in = this.header_wrap.find( '#header_in' ); 10 this.menu_wrap = this.header_wrap.find( '#site_header_nav' ); 11 this.top_level_menu_items = this.menu_wrap.find( '.menu > .menu-item' ); 12 this.items_with_submenu = this.menu_wrap.find( '.menu-item-has-children' ); 13 this.items_with_submenu_anchors = this.items_with_submenu.children( 'span' ); 14 this.menu_on_the_right = this.header_wrap.find( '#header_in' ).hasClass( 'fix_width header_in_s1' ); 15 this.logo = this.header_wrap.find( '#site_title' ); 16 this.submenus = this.top_level_menu_items.find( '.sub-menu' ); 17 this.mobile_nav_button = $( '#mobile_nav' ); 18 19 this.tolerance_margin = 10; 20 this.placeholders_added = false; 21 this.is_mobile_menu = false; 22 this.is_full_width_menu = this.header_wrap.hasClass( 'header_in_s2' ); 23 24 this.bindEvents(); 25 26 }, 27 28 bindEvents: function() { 29 30 this.checkForMobileMenu() ;//DOM ready 31 $( window ).load( $.proxy( this.checkForMobileMenu, this ) ); 32 $( window ).on( 'resize', $.proxy( this.checkForMobileMenu, this ) ); 33 34 35 }, 36 37 checkForMobileMenu: function() { 38 39 var available_width = 0, 40 all_items_width = 0; 41 42 this.header_wrap.removeClass( 'is-mobile_menu' ); 43 this.is_mobile_menu = false; 44 45 for( var i = 0, length = this.top_level_menu_items.length; i < length; i++ ) { 46 all_items_width = all_items_width + this.top_level_menu_items.eq( i ).outerWidth( true ); 47 } 48 49 if( this.menu_on_the_right ) { 50 available_width = this.header_wrap_in.width() - this.logo.outerWidth(); 51 } else { 52 available_width = this.menu_wrap.width(); 53 } 54 55 if( ( available_width - this.tolerance_margin ) <= all_items_width ) { 56 //menu has to wrap to mobile 57 this.header_wrap.addClass( 'is-mobile_menu' ); 58 this.is_mobile_menu = true; 59 } 60 61 62 //Bind specific events 63 this.unbindDesktopEvents(); 64 this.unbindMobileEvents(); 65 66 if( this.is_mobile_menu ) { 67 this.bindMobileEvents(); 68 } else { 69 this.bindDesktopEvents(); 70 } 71 72 }, 73 74 bindDesktopEvents: function() { 75 76 this.checkForOverflowedSubmenus(); 77 $( window ).on( 'load.mio_menu__desktop__checkoverflow', $.proxy( this.checkForOverflowedSubmenus, this ) ); 78 $( window ).on( 'resize.mio_menu__desktop__checkoverflow', $.proxy( this.checkForOverflowedSubmenus, this ) ); 79 80 //Hovers 81 var t = this; 82 this.items_with_submenu.on( 'mouseenter.mio_menu__desktop__hovers', function(){ 83 t.showHideSubmenu( $( this ), 'show' ); 84 } ); 85 this.items_with_submenu.on( 'mouseleave.mio_menu__desktop__hovers', function(){ 86 t.showHideSubmenu( $( this ), 'hide' ); 87 } ); 88 89 //Click on parent item 90 this.items_with_submenu_anchors.on( 'touchstart.mio_menu__desktop__taps', function(){ 91 var el = $( this ); 92 el.data( 'scrolling', false ); 93 } ); 94 this.items_with_submenu_anchors.on( 'touchmove.mio_menu__desktop__taps', function(){ 95 var el = $( this ); 96 el.data( 'scrolling', true ); 97 } ); 98 this.items_with_submenu_anchors.on( 'touchend.mio__front_menu__open_submenu', function( evt ){ 99 var el = $( this ); 100 if( el.data( 'scrolling' ) !== true ){ 101 t.checkForTap( evt, el ); 102 } 103 el.data( 'scrolling', false ); 104 } ); 105 106 }, 107 108 unbindDesktopEvents: function() { 109 110 $( window ).off( 'load.mio_menu__desktop__checkoverflow' ); 111 $( window ).off( 'resize.mio_menu__desktop__checkoverflow' ); 112 113 //Hovers 114 this.items_with_submenu.off( 'mouseenter.mio_menu__desktop__hovers' ); 115 this.items_with_submenu.off( 'mouseleave.mio_menu__desktop__hovers' ); 116 117 //Click on parent item 118 this.items_with_submenu_anchors.off( 'touchstart.mio_menu__desktop__taps' ); 119 this.items_with_submenu_anchors.off( 'touchmove.mio_menu__desktop__taps' ); 120 this.items_with_submenu_anchors.off( 'touchend.mio__front_menu__open_submenu' ); 121 122 //Runtimes 123 this.items_with_submenu_anchors.off( 'click.mio_menu__desktop__prevent_click' ); 124 $( document ).off( 'click.mio__front_menu__close_submenu' ); 125 126 127 }, 128 129 bindMobileEvents: function() { 130 131 var t = this; 132 this.items_with_submenu_anchors.on( 'click.mio_menu__mobile__toggle_submenu', function( evt ){ 133 evt.preventDefault(); 134 t.showHideSubmenu( $( this ).parent(), 'toggle', true ); 135 } ); 136 137 this.mobile_nav_button.on( 'click.mio_menu__mobile__toggle_menu', function( evt ){ 138 evt.preventDefault(); 139 t.toggleMobileMenu(); 140 } ); 141 142 }, 143 144 unbindMobileEvents: function() { 145 146 this.items_with_submenu_anchors.off( 'click.mio_menu__mobile__toggle_submenu' ); 147 this.mobile_nav_button.off( 'click.mio_menu__mobile__toggle_menu' ); 148 149 }, 150 151 checkForOverflowedSubmenus: function() { 152 153 154 var body = $( 'html' ); 155 body.css( 'overflow-x', '' ); 156 157 var document_width = $( window ).width(); 158 159 this.items_with_submenu.removeClass( 'is-submenu_on_left' ); 160 161 this.submenus.show(); 162 163 var t = this; 164 this.submenus.each( function(){ 165 var submenu = $( this ); 166 var adjusted = false;
167 168 var is_top_level_on_s2 = ( t.is_full_width_menu && submenu.parents( '.sub-menu' ).length === 0 ); 169 170 if( is_top_level_on_s2 ) { //Only on top-level menus on s2 header style 171 submenu.css({ 172 'left': '50%', 173 'right': '', 174 'margin-left': '' 175 }); 176 } else { 177 submenu.css({ 178 'left': '', 179 'right': '' 180 }); 181 } 182 183 var submenu_right_side_offset = ( submenu.offset().left + submenu.outerWidth( true ) ); 184 185 if( submenu_right_side_offset > document_width ) { 186 adjusted = true; 187 188 var is_third_level = ( $( this ).parents( '.sub-menu' ).length > 0 ); 189 190 if( is_third_level ) { 191 $( this ).parent().addClass( 'is-submenu_on_left' ); 192 } 193 194 if( is_top_level_on_s2 ) { 195 submenu.css({ 196 'margin-left': ( document_width - submenu_right_side_offset ) 197 }); 198 } else { 199 submenu.css({ 200 'left': 'auto', 201 'right': ( is_third_level ) ? '100%' : '0' 202 }); 203 } 204 205 } 206 207 //Additional check for S2 menus on left side 208 if( is_top_level_on_s2 && adjusted === false ) { 209 210 var submenu_left_offset = submenu.offset().left, 211 menu_left_offset = t.menu_wrap.offset().left; 212 213 214 if( submenu_left_offset < menu_left_offset ) { 215 submenu.css( { 216 'margin-left': ( menu_left_offset - submenu_left_offset ) 217 } ); 218 } 219 220 } 221 222 } ); 223 224 this.submenus.hide(); 225 226 body.css( 'overflow-x', 'hidden' ); 227 228 }, 229 230 /* 231 * @param action 'show'|'hide'|'toggle' 232 */ 233 showHideSubmenu: function( element, action, slide ) { 234 slide = typeof slide === 'undefined' ? false : true; 235 236 if( action === 'toggle' ) { 237 action = ( element.data( 'mio_hovering' ) === true ) ? 'hide' : 'show'; //If submenu is open, close it and vice versa 238 } 239 240 element.data( 'mio_hovering', ( action === 'show' ) ); 241 242 var submenu = element.children( '.sub-menu' ); 243 var velocity_action; 244 if( action === 'show' ) { 245 velocity_action = ( slide ) ? 'slideDown' : 'fadeIn'; 246 } else { 247 velocity_action = ( slide ) ? 'slideUp' : 'fadeOut'; 248 } 249 250 element.children( 'span' ).toggleClass('opened'); 251 252 253 var complete_function = ( action === 'show' ) ? null : function( el ) { 254 $( el ).css( 'opacity', '' ); 255 }; 256 257 submenu.velocity( 'stop' ).velocity( velocity_action, { duration: 200, complete: complete_function } ); 258 259 }, 260 261 262 /* 263 * If we detect click on parent and submenu is not visible at this time - it was probably tap and we want just to show submenu 264 */ 265 checkForTap: function( evt, anchor_element ) { 266 267 //Prevent clicks legacy event 268 evt.preventDefault(); 269 anchor_element.on( 'click.mio_menu__desktop__prevent_click', function( evt ){ 270 evt.preventDefault(); 271 } ); 272 273 //Add placeholder items 274 if( this.placeholders_added === false ) { 275 this.addPlaceholderItems(); 276 } 277 278 //Close all other parent submenus - only on desktop menu 279 if( this.is_mobile_menu === false ) { 280 this.closeAllOpenSubmenus( anchor_element.parent() ); 281 } 282 283 //Open submenu 284 this.showHideSubmenu( anchor_element.parent(), 'show' ); 285 286 287 //Close on tap outside menu 288 this.bindCloseSubmenuOnClickOutside( anchor_element.parent() ); 289 290 }, 291 292 bindCloseSubmenuOnClickOutside: function( submenu_parent ) { 293 294 this.items_with_submenu.on( 'click', function( evt ){ 295 evt.stopPropagation(); 296 } ); 297 298 var t = this; 299 $( document ).on( 'click.mio__front_menu__close_submenu', function(){ 300 t.showHideSubmenu( submenu_parent, 'hide' ); 301 $( document ).off( 'click.mio__front_menu__close_submenu' ); 302 } ); 303 304 }, 305 306 /* 307 * @param jQuery li-element of currently clicked submenu - it's parent submenu won't be closed 308 */ 309 closeAllOpenSubmenus: function( clicked_parent ) { 310 clicked_parent = clicked_parent || false; 311 312 var open_menus = this.items_with_submenu.filter( function(){ 313 var submenu_parent = $( this ); 314 if( clicked_parent !== false ) { 315 if( submenu_parent.is( clicked_parent.parents( 'ul:first' ).parent( 'li' ) ) ) return false; //We don't want to close parent submenu_wrapper of current submenu wrapper 316 } 317 return submenu_parent.data( 'mio_hovering' ); //Mio hovering is also indicating, if current menu is open 318 } ); 319 320 var t = this; 321 open_menus.each( function(){ 322 t.showHideSubmenu( $( this ), 'hide' ); 323 } ); 324 }, 325 326 addPlaceholderItems: function() { 327 328 this.items_with_submenu.each( function(){ 329 var parent = $( this ), 330 new_element = parent.clone().empty().removeClass( 'menu-item-has-children' ); 331 new_element.append( parent.clone().children( 'a' ) ); 332 parent.find('.sub-menu:first').prepend( new_element ); 333 } ); 334 335 this.checkForOverflowedSubmenus(); 336 337 this.placeholders_added = true; 338 339 }, 340 341 toggleMobileMenu: function() { 342 343 var menu_open = this.menu_wrap.is( ':visible' ), 344 velocity_action = ( menu_open ) ? 'slideUp' : 'slideDown', 345 complete_function = ( menu_open ) ? function( el ){ 346 $( el ).attr( 'style', '' ); 347 } : false;
348 349 this.menu_wrap.velocity( 'stop' ).velocity( velocity_action, { duration: 500, complete: complete_function } ); 350 351 $('#mobile_nav span').hide(); 352 353 if(menu_open) { 354 $('.mobile_nav_menu').show(); 355 } else { 356 $('.mobile_nav_close').show(); 357 } 358 359 } 360 361 362 363 }; 364 365 366 //Dom ready 367 $( function(){ 368 FrontMenu.init(); 369 } ); 370 371 //Menu changed in editor 372 $( document ).on( 'mio_editor__replaced_menu', function() { 373 FrontMenu.unbindDesktopEvents(); 374 FrontMenu.unbindMobileEvents(); 375 FrontMenu.init(); 376 } ); 377 378 379} )( 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.