1var wpAjaxUrl = 'https://www.obdlink.com/wp-admin/admin-ajax.php';var flBuilderUrl = 'https://www.obdlink.com/wp-content/plugins/bb-plugin/';var FLBuilderLayoutConfig = { 2 anchorLinkAnimations : { 3 duration : 1000, 4 easing : 'swing', 5 offset : 100 6 }, 7 paths : { 8 pluginUrl : 'https://www.obdlink.com/wp-content/plugins/bb-plugin/', 9 wpAjaxUrl : 'https://www.obdlink.com/wp-admin/admin-ajax.php' 10 }, 11 breakpoints : { 12 small : 768, 13 medium : 992, 14 large : 1200 }, 15 waypoint: { 16 offset: 80 17 }, 18 emptyColWidth: '0%' 19}; 20(function($){ 21 22 if(typeof FLBuilderLayout != 'undefined') { 23 return; 24 } 25 26 /** 27 * Helper class with generic logic for a builder layout. 28 * 29 * @class FLBuilderLayout 30 * @since 1.0 31 */ 32 FLBuilderLayout = { 33 34 /** 35 * Initializes a builder layout. 36 * 37 * @since 1.0 38 * @method init 39 */ 40 init: function() 41 { 42 // Destroy existing layout events. 43 FLBuilderLayout._destroy(); 44 45 // Init CSS classes. 46 FLBuilderLayout._initClasses(); 47 48 // Init backgrounds. 49 FLBuilderLayout._initBackgroun
49ds(); 50 51 // Init buttons. 52 FLBuilderLayout._initButtons(); 53 54 // Init row shape layer height. 55 FLBuilderLayout._initRowShapeLayerHeight(); 56 57 // Only init if the builder isn't active. 58 if ( 0 === $('.fl-builder-edit').length ) { 59 60 // Init anchor links. 61 FLBuilderLayout._initAnchorLinks(); 62 63 // Init the browser hash. 64 FLBuilderLayout._initHash(); 65 66 // Init forms. 67 FLBuilderLayout._initForms(); 68 69 FLBuilderLayout._reorderMenu(); 70 } 71 else { 72 FLBuilderLayout._initNestedColsWidth(); 73 } 74 $('body').removeClass( 'fl-no-js' ); 75 }, 76 77 /** 78 * Public method for refreshing Wookmark or MosaicFlow galleries 79 * within an element. 80 * 81 * @since 1.7.4 82 * @method refreshGalleries 83 */ 84 refreshGalleries: function( element ) 85 { 86 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 87 mfContent = $element.find( '.fl-mosaicflow-content' ), 88 wmContent = $element.find( '.fl-gallery' ), 89 mfObject = null; 90 91 if ( mfContent ) { 92 93 mfObject = mfContent.data( 'mosaicflow' ); 94 95 if ( mfObject ) { 96 mfObject.columns = $( [] ); 97 mfObject.columnsHeights = []; 98 mfContent.data( 'mosaicflow', mfObject ); 99 mfContent.mosaicflow( 'refill' ); 100 } 101 } 102 if ( wmContent ) { 103 wmContent.trigger( 'refreshWookmark' ); 104 } 105 }, 106 107 /** 108 * Public method for refreshing Masonry within an element 109 * 110 * @since 1.8.1 111 * @method refreshGridLayout 112 */ 113 refreshGridLayout: function( element ) 114 { 115 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 116 msnryContent = $element.find('.masonry'); 117 118 if ( msnryContent.length ) { 119 msnryContent.masonry('layout'); 120 } 121 }, 122 123 /** 124 * Public method for reloading BxSlider within an element 125 * 126 * @since 1.8.1 127 * @method reloadSlider 128 */ 129 reloadSlider: function( content ) 130 { 131 var $content = 'undefined' == typeof content ? $('body') : $(content); 132 133 // reload sliders. 134 if ($content.find('.bx-viewport > div').length > 0) { 135 $.each($content.find('.bx-viewport > div'), function (key, slider) { 136 setTimeout(function () { 137 $(slider).data('bxSlider').reloadSlider(); 138 }, 100); 139 }); 140 } 141 }, 142 143 /** 144 * Public method for resizing WP audio player 145 * 146 * @since 1.8.2 147 * @method resizeAudio 148 */ 149 resizeAudio: function( element ) 150 { 151 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 152 audioPlayers = $element.find('.wp-audio-shortcode.mejs-audio'), 153 player = null, 154 mejsPlayer = null, 155 rail = null, 156 railWidth = 400; 157 158 if ( audioPlayers.length && typeof mejs !== 'undefined' ) { 159 audioPlayers.each(function(){ 160 player = $(this); 161 mejsPlayer = mejs.players[player.attr('id')]; 162 rail = player.find('.mejs-controls .mejs-time-rail'); 163 var innerMejs = player.find('.mejs-inner'), 164 total = player.find('.mejs-controls .mejs-time-total'); 165 166 if ( typeof mejsPlayer !== 'undefined' ) { 167 railWidth = Math.ceil(player.width() * 0.8); 168 169 if ( innerMejs.length ) { 170 171 rail.css('width', railWidth +'px!important'); 172 //total.width(rail.width() - 10); 173 174 mejsPlayer.options.autosizeProgress = true; 175 176 // webkit has trouble doing this without a delay 177 setTimeout(function () { 178 mejsPlayer.setControlsSize(); 179 }, 50); 180 181 player.find('.mejs-inner').css({ 182 visibility: 'visible', 183 height: 'inherit' 184 }); 185 } 186 } 187 }); 188 } 189 }, 190 191 /** 192 * Public method for preloading WP audio player when it's inside the hidden element 193 * 194 * @since 1.8.2 195 * @method preloadAudio 196 */ 197 preloadAudio: function(element) 198 { 199 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 200 contentWrap = $element.closest('.fl-accordion-item'), 201 audioPlayers = $element.find('.wp-audio-shortcode.mejs-audio'); 202 203 if ( ! contentWrap.hasClass('fl-accordion-item-active') && audioPlayers.find('.mejs-inner').length ) { 204 audioPlayers.find('.mejs-inner').css({ 205 visibility : 'hidden', 206 height: 0 207 }); 208 } 209 }, 210 211 /** 212 * Public method for resizing slideshow momdule within the tab 213 * 214 * @since 1.10.5 215 * @method resizeSlideshow 216 */ 217 resizeSlideshow: function(){ 218 if(typeof YUI !== 'undefined') { 219 YUI().use('node-event-simulate', function(Y) { 220 Y.one(window).simulate("resize"); 221 }); 222 } 223 }, 224 225 /** 226 * Public method for reloading an embedded Google Map within the tabs or hidden element. 227 * 228 * @since 2.2 229 * @method reloadGoogleMap 230 */ 231 reloadGoogleMap: function(element){ 232 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ),
233 googleMap = $element.find( 'iframe[src*="google.com/maps"]' ); 234 235 if ( googleMap.length ) { 236 googleMap.attr( 'src', function(i, val) { 237 return val; 238 }); 239 } 240 }, 241 242 /** 243 * Unbinds builder layout events. 244 * 245 * @since 1.0 246 * @access private 247 * @method _destroy 248 */ 249 _destroy: function() 250 { 251 var win = $(window); 252 253 win.off('scroll.fl-bg-parallax'); 254 win.off('resize.fl-bg-video'); 255 }, 256 257 /** 258 * Checks to see if the current device has touch enabled. 259 * 260 * @since 1.0 261 * @access private 262 * @method _isTouch 263 * @return {Boolean} 264 */ 265 _isTouch: function() 266 { 267 if(('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch)) { 268 return true; 269 } 270 271 return false; 272 }, 273 274 /** 275 * Checks to see if the current device is mobile. 276 * 277 * @since 1.7 278 * @access private 279 * @method _isMobile 280 * @return {Boolean} 281 */ 282 _isMobile: function() 283 { 284 return /Mobile|Android|Silk\/|Kindle|BlackBerry|Opera Mini|Opera Mobi|webOS/i.test( navigator.userAgent ); 285 }, 286 287 /** 288 * Initializes builder body classes. 289 * 290 * @since 1.0 291 * @access private 292 * @method _initClasses 293 */ 294 _initClasses: function() 295 { 296 var body = $( 'body' ), 297 ua = navigator.userAgent; 298 299 // Add the builder body class (skip on block-only frontend to avoid layout break). 300 if ( ! body.hasClass( 'fl-builder-blocks-only' ) && ! body.hasClass( 'archive' ) && $( '.fl-builder-content-primary' ).length > 0 ) { 301 body.addClass('fl-builder'); 302 } 303 304 // Add the builder touch body class. 305 if(FLBuilderLayout._isTouch()) { 306 body.addClass('fl-builder-touch'); 307 } 308 309 // Add the builder mobile body class. 310 if(FLBuilderLayout._isMobile()) { 311 body.addClass('fl-builder-mobile'); 312 } 313 314 if ( $(window).width() < FLBuilderLayoutConfig.breakpoints.small ) { 315 body.addClass( 'fl-builder-breakpoint-small' ); 316 } 317 318 if ( $(window).width() > FLBuilderLayoutConfig.breakpoints.small && $(window).width() < FLBuilderLayoutConfig.breakpoints.medium ) { 319 body.addClass( 'fl-builder-breakpoint-medium' ); 320 } 321 322 if ( $(window).width() > FLBuilderLayoutConfig.breakpoints.medium && $(window).width() < FLBuilderLayoutConfig.breakpoints.large ) { 323 body.addClass( 'fl-builder-breakpoint-large' ); 324 } 325 326 if ( $(window).width() > FLBuilderLayoutConfig.breakpoints.large ) { 327 body.addClass( 'fl-builder-breakpoint-default' ); 328 } 329 330 // IE11 body class. 331 if ( ua.indexOf( 'Trident/7.0' ) > -1 && ua.indexOf( 'rv:11.0' ) > -1 ) { 332 body.addClass( 'fl-builder-ie-11' ); 333 } 334 }, 335 336 /** 337 * Initializes builder node backgrounds that require 338 * additional JavaScript logic such as parallax. 339 * 340 * @since 1.1.4 341 * @access private
342 * @method _initBackgrounds 343 */ 344 _initBackgrounds: function() 345 { 346 var win = $(window); 347 348 // Init parallax backgrounds. 349 if($('.fl-row-bg-parallax').length > 0 && !FLBuilderLayout._isMobile()) { 350 FLBuilderLayout._scrollParallaxBackgrounds(); 351 FLBuilderLayout._initParallaxBackgrounds(); 352 win.on('resize.fl-bg-parallax', FLBuilderLayout._initParallaxBackgrounds); 353 win.on('scroll.fl-bg-parallax', FLBuilderLayout._scrollParallaxBackgrounds); 354 } 355 356 // Init video backgrounds. 357 if($('.fl-bg-video').length > 0) { 358 FLBuilderLayout._initBgVideos(); 359 FLBuilderLayout._resizeBgVideos(); 360 361 // Ensure FLBuilderLayout._resizeBgVideos() is only called once on window resize. 362 var resizeBGTimer = null; 363 win.on('resize.fl-bg-video', function(e){ 364 clearTimeout( resizeBGTimer ); 365 resizeBGTimer = setTimeout(function() { 366 FLBuilderLayout._resizeBgVideos(e); 367 }, 100 ); 368 }); 369 } 370 }, 371 372 /** 373 * Initializes all buttons in a layout. 374 * 375 * @since 2.10 376 * @access private 377 * @method _initButtons 378 */ 379 _initButtons: function() 380 { 381 // Trigger click event on Enter or Space key press for deprecated button markup. 382 $('a.fl-button[role="button"]').on('keydown', function(event) { 383 if (event.key === 'Enter' || event.key === ' ') { 384 // Necessary to prevent the default behavior of the space key from scrolling the page 385 event.preventDefault(); 386 $(this).trigger('click'); 387 } 388 }); 389 }, 390 391 /** 392 * Initializes all parallax backgrounds in a layout. 393 * 394 * @since 1.1.4 395 * @access private 396 * @method _initParallaxBackgrounds 397 */ 398 _initParallaxBackgrounds: function() 399 { 400 $('.fl-row-bg-parallax').each(FLBuilderLayout._initParallaxBackground); 401 }, 402 403 /** 404 * Initializes a single parallax background. 405 * 406 * @since 1.1.4 407 * @access private 408 * @method _initParallaxBackgrounds 409 */ 410 _initParallaxBackground: function() 411 { 412 var row = $(this), 413 content = row.find('> .fl-row-content-wrap'), 414 winWidth = $(window).width(), 415 screenSize = '', 416 imageSrc = { 417 default: '', 418 medium: '', 419 responsive: '', 420 }; 421 422 imageSrc.default = row.data('parallax-image') || ''; 423 imageSrc.medium = row.data('parallax-image-medium') || imageSrc.default; 424 imageSrc.responsive = row.data('parallax-image-responsive') || imageSrc.medium; 425 426 if (winWidth > FLBuilderLayoutConfig.breakpoints.medium) { 427 screenSize = 'default'; 428 } else if (winWidth > FLBuilderLayoutConfig.breakpoints.small && winWidth <= FLBuilderLayoutConfig.breakpoints.medium ) { 429 screenSize = 'medium'; 430 } else if (winWidth <= FLBuilderLayoutConfig.breakpoints.small) { 431 screenSize = 'responsive'; 432 } 433 434 content.css('background-image', 'url(' + imageSrc[screenSize] + ')'); 435 row.data('current-image-loaded', screenSize ); 436 437 }, 438 439 /** 440 * Fires when the window is scrolled to adjust 441 * parallax backgrounds. 442 * 443 * @since 1.1.4 444 * @access private 445 * @method _scrollParallaxBackgrounds 446 */ 447 _scrollParallaxBackgrounds: function() 448 { 449 $('.fl-row-bg-parallax').each(FLBuilderLayout._scrollParallaxBackground); 450 }, 451 452 /** 453 * Fires when the window is scrolled to adjust 454 * a single parallax background. 455 * 456 * @since 1.1.4 457 * @access private 458 * @method _scrollParallaxBackground 459 */ 460 _scrollParallaxBackground: function() 461 { 462 var win = $(window), 463 row = $(this), 464 content = row.find('> .fl-row-content-wrap'), 465 speed = row.data('parallax-speed'), 466 offset = content.offset(), 467 yPos = -((win.scrollTop() - offset.top) / speed), 468 initialOffset = ( row.data('parallax-offset') != null ) ? row.data('parallax-offset') : 0, 469 totalOffset = yPos - initialOffset; 470 471 content.css('background-position', 'center ' + totalOffset + 'px'); 472 }, 473 474 /** 475 * Initializes all video backgrounds. 476 * 477 * @since 1.6.3.3 478 * @access private 479 * @method _initBgVideos 480 */ 481 _initBgVideos: function() 482 { 483 $('.fl-bg-video').each(FLBuilderLayout._initBgVideo); 484 }, 485 486 /** 487 * Initializes a video background. 488 * 489 * @since 1.6.3.3 490 * @access private 491 * @method _initBgVideo 492 */ 493 _initBgVideo: function() 494 { 495 var wrap = $( this ), 496 width = wrap.data( 'width' ), 497 height = wrap.data( 'height' ), 498 mp4 = wrap.data( 'mp4' ), 499 youtube = wrap.data( 'youtube'), 500 vimeo = wrap.data( 'vimeo'), 501 mp4Type = wrap.data( 'mp4-type' ), 502 webm = wrap.data( 'webm' ), 503 webmType = wrap.data( 'webm-type' ), 504 fallback = wrap.data( 'fallback' ), 505 loaded = wrap.data( 'loaded' ), 506 videoMobile = wrap.data( 'video-mobile' ), 507 playPauseButton = wrap.find('.fl-bg-video-play-pause-control'), 508 fallbackTag = '', 509 videoTag = null, 510 mp4Tag = null, 511 webmTag = null; 512 513 // Return if the video has been loaded for this row. 514 if ( loaded ) { 515 return; 516 } 517
518 videoTag = $( '<video autoplay loop muted playsinline></video>' ); 519 520 /** 521 * Add poster image (fallback image) 522 */ 523 if( 'undefined' != typeof fallback && '' != fallback ) { 524 videoTag.attr( 'poster', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' ) 525 videoTag.css({ 526 backgroundImage: 'url("' + fallback + '")', 527 backgroundColor: 'transparent', 528 backgroundRepeat: 'no-repeat', 529 backgroundSize: 'cover', 530 backgroundPosition: 'center center', 531 }) 532 } 533 534 // MP4 Source Tag 535 if ( 'undefined' != typeof mp4 && '' != mp4 ) { 536 537 mp4Tag = $( '<source />' ); 538 mp4Tag.attr( 'src', mp4 ); 539 mp4Tag.attr( 'type', mp4Type ); 540 541 videoTag.append( mp4Tag ); 542 } 543 // WebM Source Tag 544 if ( 'undefined' != typeof webm && '' != webm ) { 545 546 webmTag = $( '<source />' ); 547 webmTag.attr( 'src', webm ); 548 webmTag.attr( 'type', webmType ); 549 550 videoTag.append( webmTag ); 551 } 552 553 // This is either desktop, or mobile is enabled. 554 if ( ! FLBuilderLayout._isMobile() || ( FLBuilderLayout._isMobile() && "yes" == videoMobile ) ) { 555 if ( 'undefined' != typeof youtube ) { 556 FLBuilderLayout._initYoutubeBgVideo.apply( this ); 557 } 558 else if ( 'undefined' != typeof vimeo ) { 559 FLBuilderLayout._initVimeoBgVideo.apply( this ); 560 } 561 else { 562 wrap.append( videoTag ); 563 564 if ( playPauseButton.length > 0 ) { 565 var video = videoTag[0]; 566 567 playPauseButton.on( 'click', { video: video }, function(e) { 568 var video = e.data.video; 569 570 if ( video.paused ) { 571 video.play(); 572 } else { 573 video.pause(); 574 } 575 } ); 576 577 $( video ).on( 'play playing', function () { 578 playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' ); 579 } ); 580 581 $( video ).on( 'pause ended waiting', function () { 582 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 583 } ); 584 } 585 } 586 } 587 else { 588 // if we are here, it means we are on mobile and NO is set so remove video src and use fallback 589 videoTag.attr('src', '') 590 wrap.append( videoTag ); 591 } 592 593 // Mark this video as loaded. 594 wrap.data('loaded', true); 595 }, 596 597 /** 598 * Initializes Youtube video background 599 * 600 * @since 1.9 601 * @access private 602 * @method _initYoutubeBgVideo 603 */ 604 _initYoutubeBgVideo: function() 605 { 606 var playerWrap = $(this), 607 videoId = playerWrap.data('video-id'), 608 videoPlayer = playerWrap.find('.fl-bg-video-player'), 609 enableAudio = playerWrap.data('enable-audio'), 610 audioButton = playerWrap.find('.fl-bg-video-audio'), 611 playPauseButton = playerWrap.find('.fl-bg-video-play-pause-control'), 612 startTime = 'undefined' !== typeof playerWrap.data('start') ? playerWrap.data('start') : 0, 613 startTime = 'undefined' !== typeof playerWrap.data('t') && startTime === 0 ? playerWrap.data('t') : startTime, 614 endTime = 'undefined' !== typeof playerWrap.data('end') ? playerWrap.data('end') : 0, 615 loop = 'undefined' !== typeof playerWrap.data('loop') ? playerWrap.data('loop') : 1, 616 stateCount = 0, 617 player,fallback_showing; 618 619 if ( videoId ) { 620 fallback = playerWrap.data('fallback') || false 621 if( fallback ) { 622 playerWrap.find('iframe').remove() 623 fallbackTag = $( '<div></div>' ); 624 fallbackTag.addClass( 'fl-bg-video-fallback' ); 625 fallbackTag.css( 'background-image', 'url(' + playerWrap.data('fallback') + ')' ); 626 fallbackTag.css( 'background-size', 'cover' ); 627 fallbackTag.css( 'transition', 'background-image 1s') 628 playerWrap.append( fallbackTag ); 629 fallback_showing = true; 630 } 631 FLBuilderLayout._onYoutubeApiReady( function( YT ) { 632 setTimeout( function() { 633 634 player = new YT.Player( videoPlayer[0], { 635 videoId: videoId, 636 events: { 637 onReady: function(event) { 638 if ( "no" === enableAudio || FLBuilderLayout._isMobile() ) { 639 event.target.mute(); 640 } 641 else if ( "yes" === enableAudio && event.target.isMuted ) { 642 event.target.unMute(); 643 } 644 645 // Store an instance to a parent 646 playerWrap.data('YTPlayer', player); 647 FLBuilderLayout._resizeYoutubeBgVideo.apply(playerWrap); 648 649 // Queue the video. 650 event.target.playVideo(); 651 652 if ( audioButton.length > 0 && ! FLBuilderLayout._isMobile() ) {
653 audioButton.on( 'click', {button: audioButton, player: player}, FLBuilderLayout._toggleBgVideoAudio ); 654 } 655 656 if ( playPauseButton.length > 0 ) { 657 playPauseButton.on( 'click', {player: player}, function(e) { 658 var player = e.data.player; 659 660 if ( 1 === player.getPlayerState() ) { 661 player.pauseVideo(); 662 } else { 663 player.playVideo(); 664 } 665 }); 666 } 667 }, 668 onStateChange: function( event ) { 669 670 if ( event.data === 1 ) { 671 if ( fallback_showing ) { 672 $( '.fl-bg-video-fallback' ).css( 'background-image', 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)' ) 673 } 674 } 675 // Manual check if video is not playable in some browsers. 676 // StateChange order: [-1, 3, -1] 677 if ( stateCount < 4 ) { 678 stateCount++; 679 } 680 681 // Comply with the audio policy in some browsers like Chrome and Safari. 682 if ( stateCount > 1 && -1 === event.data && "yes" === enableAudio ) { 683 player.mute(); 684 player.playVideo(); 685 audioButton.show(); 686 } 687 688 if ( event.data === YT.PlayerState.ENDED && 1 === loop ) { 689 if ( startTime > 0 ) { 690 player.seekTo( startTime ); 691 } 692 else { 693 player.playVideo(); 694 } 695 } 696 697 if ( event.data === YT.PlayerState.PLAYING ) { 698 playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' ); 699 } else if ( event.data === YT.PlayerState.PAUSED ) { 700 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 701 } else if ( event.data === YT.PlayerState.BUFFERING ) { 702 playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' ); 703 } else if ( event.data === YT.PlayerState.CUED ) { 704 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 705 } else if ( event.data === YT.PlayerState.ENDED ) { 706 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 707 } 708 }, 709 onError: function(event) { 710 console.info('YT Error: ' + event.data) 711 FLBuilderLayout._onErrorYoutubeVimeo(playerWrap) 712 } 713 }, 714 playerVars: { 715 playsinline: FLBuilderLayout._isMobile() ? 1 : 0, 716 controls: 0, 717 showinfo: 0, 718 rel : 0, 719 start: startTime, 720 end: endTime, 721 } 722 } ); 723 }, 1 ); 724 } ); 725 } 726 }, 727 728 /** 729 * On youtube or vimeo error show the fallback image if available. 730 * @since 2.0.7 731 */ 732 _onErrorYoutubeVimeo: function(playerWrap) { 733 734 fallback = playerWrap.data('fallback') || false 735 if( ! fallback ) { 736 return false; 737 } 738 playerWrap.find('iframe').remove() 739 fallbackTag = $( '<div></div>' ); 740 fallbackTag.addClass( 'fl-bg-video-fallback' ); 741 fallbackTag.css( 'background-image', 'url(' + playerWrap.data('fallback') + ')' ); 742 playerWrap.append( fallbackTag ); 743 }, 744 745 /** 746 * Check if Youtube API has been downloaded 747 * 748 * @since 1.9 749 * @access private 750 * @method _onYoutubeApiReady 751 * @param {Function} callback Method to call when YT API has been loaded 752 */ 753 _onYoutubeApiReady: function( callback ) { 754 if ( window.YT && YT.loaded ) { 755 callback( YT ); 756 } else { 757 // If not ready check again by timeout.. 758 setTimeout( function() { 759 FLBuilderLayout._onYoutubeApiReady( callback ); 760 }, 350 ); 761 } 762 }, 763 764 /** 765 * Initializes Vimeo video background 766 * 767 * @since 1.9 768 * @access private 769 * @method _initVimeoBgVideo 770 */ 771 _initVimeoBgVideo: function() 772 { 773 var playerWrap = $(this), 774 videoId = playerWrap.data('video-id'), 775 videoHash = playerWrap.data('video-hash'), 776 videoPlayer = playerWrap.find('.fl-bg-video-player'), 777 enableAudio = playerWrap.data('enable-audio'), 778 audioButton = playerWrap.find('.fl-bg-video-audio'), 779 playPauseButton = playerWrap.find('.fl-bg-video-play-pause-control'), 780 playerState = '', 781 player, 782 width = playerWrap.outerWidth(), 783 ua = navigator.userAgent; 784 785 if ( typeof Vimeo !== 'undefined' && videoId ) { 786 787 const vimOptions = { 788 loop : true, 789 title : false, 790 portrait : false, 791 background : true, 792 autopause : false, 793 muted : true, 794 }; 795 796 if ( videoHash.length ) { 797 vimOptions.url = `https://player.vimeo.com/video/${ videoId }?h=${ videoHash }`; 798 } else { 799 vimOptions.id = videoId; 800 } 801 802 player = new Vimeo.Player(videoPlayer[0], vimOptions ); 803 playerWrap.data('VMPlayer', player); 804 if ( "no" === enableAudio ) { 805 player.setVolume(0); 806 } 807 else if ("yes" === enableAudio ) { 808 // Chrome, Safari, Firefox have audio policy restrictions for autoplay videos. 809 if ( ua.indexOf("Safari") > -1 || ua.indexOf("Chrome") > -1 || ua.indexOf("Firefox") > -1 ) { 810 player.setVolume(0); 811 audioButton.show(); 812 } 813 else { 814 player.setVolume(1); 815 } 816 } 817 818 player.play().catch(function(error) { 819 FLBuilderLayout._onErrorYoutubeVimeo(playerWrap) 820 }); 821 822 if ( audioButton.length > 0 ) {
823 audioButton.on( 'click', {button: audioButton, player: player}, FLBuilderLayout._toggleBgVideoAudio ); 824 } 825 826 player.on( 'play', function() { 827 playerState = 'play'; 828 playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' ); 829 } ); 830 player.on( 'pause', function() { 831 playerState = 'pause'; 832 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 833 } ); 834 player.on( 'ended', function() { 835 playerState = 'ended'; 836 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 837 } ); 838 player.on( 'bufferstart', function() { 839 playerState = 'bufferstart'; 840 playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' ); 841 } ); 842 843 if ( playPauseButton.length > 0 ) { 844 playPauseButton.on( 'click', { player: player }, function( e ) { 845 var player = e.data.player; 846 847 if ( playerState === 'play' ) { 848 player.pause(); 849 } else { 850 player.play(); 851 } 852 } ); 853 } 854 } 855 }, 856 857 /** 858 * Mute / unmute audio on row's video background. 859 * It works for both Youtube and Vimeo. 860 * 861 * @since 2.1.3 862 * @access private 863 * @method _toggleBgVideoAudio 864 * @param {Object} e Method arguments 865 */ 866 _toggleBgVideoAudio: function( e ) { 867 var player = e.data.player, 868 control = e.data.button.find('.fl-audio-control'); 869 870 if ( control.hasClass( 'fa-volume-off' ) ) { 871 // Unmute 872 control 873 .removeClass( 'fa-volume-off' ) 874 .addClass( 'fa-volume-up' ); 875 e.data.button.find( '.fa-times' ).hide(); 876 877 if ( 'function' === typeof player.unMute ) { 878 player.unMute(); 879 } 880 else { 881 player.setVolume( 1 ); 882 } 883 } 884 else { 885 // Mute 886 control 887 .removeClass( 'fa-volume-up' ) 888 .addClass( 'fa-volume-off' ); 889 e.data.button.find( '.fa-times' ).show(); 890 891 if ( 'function' === typeof player.unMute ) { 892 player.mute(); 893 } 894 else { 895 player.setVolume( 0 ); 896 } 897 } 898 }, 899 900 /** 901 * Fires when there is an error loading a video 902 * background source and shows the fallback. 903 * 904 * @since 1.6.3.3 905 * @access private 906 * @method _videoBgSourceError 907 * @param {Object} e An event object 908 * @deprecated 2.0.3 909 */ 910 _videoBgSourceError: function( e ) 911 { 912 var source = $( e.target ), 913 wrap = source.closest( '.fl-bg-video' ), 914 vid = wrap.find( 'video' ), 915 fallback = wrap.data( 'fallback' ), 916 fallbackTag = ''; 917 source.remove(); 918 919 if ( vid.find( 'source' ).length ) { 920 // Don't show the fallback if we still have other sources to check. 921 return; 922 } else if ( '' !== fallback ) { 923 fallbackTag = $( '<div></div>' ); 924 fallbackTag.addClass( 'fl-bg-video-fallback' ); 925 fallbackTag.css( 'background-image', 'url(' + fallback + ')' ); 926 wrap.append( fallbackTag ); 927 vid.remove(); 928 } 929 }, 930 931 /** 932 * Fires when the window is resized to resize 933 * all video backgrounds. 934 * 935 * @since 1.1.4 936 * @access private 937 * @method _resizeBgVideos 938 */ 939 _resizeBgVideos: function() 940 { 941 $('.fl-bg-video').each( function() { 942 943 FLBuilderLayout._resizeBgVideo.apply( this ); 944 945 if ( $( this ).parent().find( 'img' ).length > 0 ) { 946 $( this ).parent().imagesLoaded( $.proxy( FLBuilderLayout._resizeBgVideo, this ) ); 947 } 948 } ); 949 }, 950 951 /** 952 * Fires when the window is resized to resize 953 * a single video background. 954 * 955 * @since 1.1.4 956 * @access private 957 * @method _resizeBgVideo 958 */ 959 _resizeBgVideo: function() 960 { 961 if ( 0 === $( this ).find( 'video' ).length && 0 === $( this ).find( 'iframe' ).length ) { 962 return; 963 } 964 965 var wrap = $(this), 966 wrapHeight = wrap.outerHeight(), 967 wrapWidth = wrap.outerWidth(), 968 vid = wrap.find('video'), 969 vidHeight = wrap.data('height'), 970 vidWidth = wrap.data('width'), 971 newWidth = wrapWidth, 972 newHeight = Math.round(vidHeight * wrapWidth/vidWidth), 973 newLeft = 0, 974 newTop = 0, 975 iframe = wrap.find('iframe'), 976 isRowFullHeight = $(this).closest('.fl-row-bg-video').hasClass('fl-row-full-height'), 977 vidCSS = { 978 top: '50%', 979 left: '50%', 980 transform: 'translate(-50%,-50%)', 981 }; 982 983 if ( vid.length ) { 984 if(vidHeight === '' || typeof vidHeight === 'undefined' || vidWidth === '' || typeof vidWidth === 'undefined') { 985 vid.css({ 986 'left' : '0px', 987 'top' : '0px', 988 'width' : newWidth + 'px' 989 }); 990 991 // Try to set the actual video dimension on 'loadedmetadata' when using URL as video source 992 vid.on('loadedmetadata', FLBuilderLayout._resizeOnLoadedMeta); 993 994 return; 995 } 996 997 if ( ! isRowFullHeight ) { 998 if ( newHeight < wrapHeight ) { 999 newHeight = wrapHeight; 1000 newLeft = -((newWidth - wrapWidth) / 2); 1001 newWidth = vidHeight ? Math.round(vidWidth * wrapHeight/vidHeight) : newWidth; 1002 } 1003 else { 1004 newTop = -((newHeight - wrapHeight)/2); 1005 } 1006 vidCSS = { 1007 left : newLeft + 'px', 1008 top : newTop + 'px', 1009 height : newHeight + 'px', 1010 width : newWidth + 'px', 1011 } 1012 } 1013 1014 vid.css( vidCSS ); 1015 1016 } 1017 else if ( iframe.length ) { 1018 1019 // Resize Youtube video player within iframe tag 1020 if ( typeof wrap.data('youtube') !== 'undefined' ) { 1021 FLBuilderLayout._resizeYoutubeBgVideo.apply(this); 1022 } 1023 } 1024 }, 1025 1026 /** 1027 * Fires when video meta has been loaded. 1028 * This will be Triggered when width/height attributes were not specified during video background resizing. 1029 * 1030 * @since 1.8.5 1031 * @access private 1032 * @method _resizeOnLoadedMeta 1033 */ 1034 _resizeOnLoadedMeta: function(){ 1035 var video = $(this), 1036 wrapHeight = video.parent().outerHeight(), 1037 wrapWidth = video.parent().outerWidth(), 1038 vidWidth = video[0].videoWidth, 1039 vidHeight = video[0].videoHeight, 1040 newHeight = Math.round(vidHeight * wrapWidth/vidWidth), 1041 newWidth = wrapWidth, 1042 newLeft = 0, 1043 newTop = 0; 1044 1045 if(newHeight < wrapHeight) { 1046 newHeight = wrapHeight; 1047 newWidth = Math.round(vidWidth * wrapHeight/vidHeight); 1048 newLeft = -((newWidth - wrapWidth)/2); 1049 } 1050 else { 1051 newTop = -((newHeight - wrapHeight)/2); 1052 } 1053 1054 video.parent().data('width', vidWidth); 1055 video.parent().data('height', vidHeight); 1056 1057 video.css({ 1058 'left' : newLeft + 'px', 1059 'top' : newTop + 'px', 1060 'width' : newWidth + 'px', 1061 'height' : newHeight + 'px' 1062 }); 1063 }, 1064 1065 /** 1066 * Fires when the window is resized to resize 1067 * a single Youtube video background. 1068 * 1069 * @since 1.9 1070 * @access private 1071 * @method _resizeYoutubeBgVideo 1072 */ 1073 _resizeYoutubeBgVideo: function() 1074 { 1075 var wrap = $(this), 1076 wrapWidth = wrap.outerWidth(), 1077 wrapHeight = wrap.outerHeight(), 1078 player = wrap.data('YTPlayer'), 1079 video = player ? player.getIframe() : null, 1080 aspectRatioSetting = '16:9', // Medium 1081 aspectRatioArray = aspectRatioSetting.split( ':' ),
1082 aspectRatio = aspectRatioArray[0] / aspectRatioArray[1], 1083 ratioWidth = wrapWidth / aspectRatio, 1084 ratioHeight = wrapHeight * aspectRatio, 1085 isWidthFixed = wrapWidth / wrapHeight > aspectRatio, 1086 width = isWidthFixed ? wrapWidth : ratioHeight, 1087 height = isWidthFixed ? ratioWidth : wrapHeight; 1088 1089 if ( video ) { 1090 $(video).width( width ).height( height ); 1091 } 1092 }, 1093 1094 /** 1095 * Opens a tab or accordion item if the browser hash is set 1096 * to the ID of one on the page. 1097 * 1098 * @since 1.6.0 1099 * @access private 1100 * @method _initHash 1101 */ 1102 _initHash: function() 1103 { 1104 var hash = window.location.hash.replace( '#', '' ).split( '/' ).shift(), 1105 element = null, 1106 tabs = null, 1107 responsiveLabel = null, 1108 tabIndex = null, 1109 label = null; 1110 1111 if ( '' !== hash ) { 1112 1113 try { 1114 1115 element = $( '#' + hash ); 1116 1117 if ( element.length > 0 ) { 1118 1119 if ( element.hasClass( 'fl-accordion-item' ) ) { 1120 setTimeout( function() { 1121 element.find( '.fl-accordion-button' ).trigger( 'click' ); 1122 }, 100 ); 1123 } 1124 if ( element.hasClass( 'fl-tabs-panel' ) ) { 1125 setTimeout( function() { 1126 tabs = element.closest( '.fl-tabs' ); 1127 responsiveLabel = element.find( '.fl-tabs-panel-label' ); 1128 tabIndex = responsiveLabel.data( 'index' ); 1129 label = tabs.find( '.fl-tabs-labels .fl-tabs-label[data-index=' + tabIndex + ']' ); 1130 1131 label[0].click(); 1132 FLBuilderLayout._scrollToElement(element); 1133 }, 100 ); 1134 } 1135 } 1136 } 1137 catch( e ) {} 1138 } 1139 }, 1140 1141 /** 1142 * Initializes all anchor links on the page for smooth scrolling. 1143 * 1144 * @since 1.4.9 1145 * @access private 1146 * @method _initAnchorLinks 1147 */ 1148 _initAnchorLinks: function() 1149 { 1150 $( 'a, [role="link"]' ).each( FLBuilderLayout._initAnchorLink ); 1151 }, 1152 1153 /** 1154 * Initializes a single anchor link for smooth scrolling. 1155 * 1156 * @since 1.4.9 1157 * @access private 1158 * @method _initAnchorLink 1159 */ 1160 _initAnchorLink: function() 1161 { 1162 var link = $( this ), 1163 href = link.data( 'url' ) ? link.data( 'url' ) : link.attr( 'href' ), 1164 target = link.data( 'url' ) ? new URL( href, window.location.href ) : this, 1165 loc = window.location, 1166 id = null, 1167 element = null, 1168 flNode = false; 1169 1170 if ( 'undefined' != typeof href && href.indexOf( '#' ) > -1 && link.closest('svg').length < 1 ) { 1171 1172 if ( loc.pathname.replace( /^\//, '' ) == target.pathname.replace( /^\//, '' ) && loc.hostname == target.hostname ) { 1173 1174 try { 1175 1176 id = href.split( '#' ).pop(); 1177 // If there is no ID then we have nowhere to look 1178 // Fixes a quirk in jQuery and FireFox 1179 if( ! id ) { 1180 return; 1181 } 1182 element = $( '#' + id ); 1183 1184 if ( element.length > 0 ) { 1185 flNode = element.hasClass( 'fl-row' ) || element.hasClass( 'fl-col' ) || element.hasClass( 'fl-module' ); 1186 if ( !element.hasClass( 'fl-no-scroll' ) && ( link.hasClass( 'fl-scroll-link' ) || flNode ) ) { 1187 $( link ).on( 'click', FLBuilderLayout._scrollToElementOnLinkClick ); 1188 } 1189 if ( element.hasClass( 'fl-accordion-item' ) ) { 1190 $( link ).on( 'click', FLBuilderLayout._scrollToAccordionOnLinkClick ); 1191 } 1192 if ( element.hasClass( 'fl-tabs-panel' ) ) { 1193 $( link ).on( 'click', FLBuilderLayout._scrollToTabOnLinkClick ); 1194 } 1195 } 1196 } 1197 catch( e ) {} 1198 } 1199 } 1200 }, 1201 1202 /** 1203 * Scrolls to an element when an anchor link is clicked. 1204 * 1205 * @since 1.4.9 1206 * @access private 1207 * @method _scrollToElementOnLinkClick 1208 * @param {Object} e An event object. 1209 * @param {Function} callback A function to call when the scroll is complete. 1210 */ 1211 _scrollToElementOnLinkClick: function( e, callback ) 1212 { 1213 var attribute = $( this ).data( 'url' ) ? $( this ).data( 'url' ) : $( this ).attr( 'href' ); 1214 var element = $( '#' + attribute.split( '#' ).pop() ); 1215 1216 FLBuilderLayout._scrollToElement( element, callback ); 1217 1218 e.preventDefault(); 1219 }, 1220 1221 /** 1222 * Scrolls to an element. 1223 * 1224 * @since 1.6.4.5 1225 * @access private 1226 * @method _scrollToElement 1227 * @param {Object} element The element to scroll to. 1228 * @param {Function} callback A function to call when the scroll is complete. 1229 */ 1230 _scrollToElement: function( element, callback ) 1231 { 1232 var config = FLBuilderLayoutConfig.anchorLinkAnimations, 1233 dest = 0, 1234 win = $( window ), 1235 doc = $( document ); 1236 1237 if ( element.length > 0 ) { 1238 1239 if ( 'fixed' === element.css('position') || 'fixed' === element.parent().css('position') ) { 1240 dest = element.position().top; 1241 } 1242 else if ( element.offset().top > doc.height() - win.height() ) { 1243 dest = doc.height() - win.height(); 1244 } 1245 else { 1246 dest = element.offset().top - config.offset; 1247 } 1248 1249 $( 'html, body' ).stop( true ).animate( { scrollTop: dest }, config.duration, config.easing, function() { 1250 1251 if ( 'undefined' != typeof callback ) { 1252 callback(); 1253 } 1254 1255 if ( undefined != element.attr( 'id' ) && window.location.hash !== '#' + element.attr( 'id' ) ) { 1256 1257 var firefox_version = window.navigator.userAgent.match( /Firefox\/(\d+)\./ ), 1258 firefox_version = firefox_version ? parseInt( firefox_version[1], 10 ) : null; 1259 1260 if ( firefox_version !== null && firefox_version < 135 ) { 1261 window.location.hash = element.attr( 'id' ); 1262 } else { 1263 if ( history.pushState ) { 1264 history.pushState( null, null, '#' + element.attr( 'id' ) ); 1265 } else { 1266 window.location.hash = element.attr( 'id' ); 1267 } 1268 } 1269 } 1270 } ); 1271 } 1272 }, 1273 1274 /** 1275 * Scrolls to an accordion item when a link is clicked. 1276 * 1277 * @since 1.5.9 1278 * @access private 1279 * @method _scrollToAccordionOnLinkClick 1280 * @param {Object} e An event object. 1281 */ 1282 _scrollToAccordionOnLinkClick: function( e ) 1283 { 1284 var element = $( '#' + $( this ).attr( 'href' ).split( '#' ).pop() ); 1285 1286 if ( element.length > 0 ) { 1287 1288 var callback = function() { 1289 if ( element ) { 1290 element.find( '.fl-accordion-button' ).trigger( 'click' ); 1291 element = false;
1292 } 1293 }; 1294 1295 FLBuilderLayout._scrollToElementOnLinkClick.call( this, e, callback ); 1296 } 1297 }, 1298 1299 /** 1300 * Scrolls to a tab panel when a link is clicked. 1301 * 1302 * @since 1.5.9 1303 * @access private 1304 * @method _scrollToTabOnLinkClick 1305 * @param {Object} e An event object. 1306 */ 1307 _scrollToTabOnLinkClick: function( e ) 1308 { 1309 var element = $( '#' + $( this ).attr( 'href' ).split( '#' ).pop() ), 1310 tabs = null, 1311 label = null, 1312 responsiveLabel = null; 1313 1314 if ( element.length > 0 ) { 1315 1316 tabs = element.closest( '.fl-tabs' ); 1317 responsiveLabel = element.find( '.fl-tabs-panel-label' ); 1318 tabIndex = responsiveLabel.data( 'index' ); 1319 label = tabs.find( '.fl-tabs-labels .fl-tabs-label[data-index=' + tabIndex + ']' ); 1320 1321 if ( responsiveLabel.is( ':visible' ) ) { 1322 1323 var callback = function() { 1324 if ( element ) { 1325 responsiveLabel.trigger( $.Event( 'click', { which: 1 } ) ); 1326 } 1327 }; 1328 1329 FLBuilderLayout._scrollToElementOnLinkClick.call( this, e, callback ); 1330 } 1331 else { 1332 label[0].click(); 1333 FLBuilderLayout._scrollToElement( element ); 1334 } 1335 1336 e.preventDefault(); 1337 } 1338 }, 1339 1340 /** 1341 * Initializes all builder forms on a page. 1342 * 1343 * @since 1.5.4 1344 * @access private 1345 * @method _initForms 1346 */ 1347 _initForms: function() 1348 { 1349 if ( ! FLBuilderLayout._hasPlaceholderSupport ) { 1350 $( '.fl-form-field input' ).each( FLBuilderLayout._initFormFieldPlaceholderFallback ); 1351 } 1352 1353 $( '.fl-form-field input' ).on( 'focus', FLBuilderLayout._clearFormFieldError ); 1354 }, 1355 1356 /** 1357 * Checks to see if the current device has HTML5 1358 * placeholder support. 1359 * 1360 * @since 1.5.4 1361 * @access private 1362 * @method _hasPlaceholderSupport 1363 * @return {Boolean} 1364 */ 1365 _hasPlaceholderSupport: function() 1366 { 1367 var input = document.createElement( 'input' ); 1368 1369 return 'undefined' != input.placeholder; 1370 }, 1371 1372 /** 1373 * Initializes the fallback for when placeholders aren't supported. 1374 * 1375 * @since 1.5.4 1376 * @access private 1377 * @method _initFormFieldPlaceholderFallback 1378 */ 1379 _initFormFieldPlaceholderFallback: function() 1380 { 1381 var field = $( this ), 1382 val = field.val(), 1383 placeholder = field.attr( 'placeholder' ); 1384 1385 if ( 'undefined' != placeholder && '' === val ) { 1386 field.val( placeholder ); 1387 field.on( 'focus', FLBuilderLayout._hideFormFieldPlaceholderFallback ); 1388 field.on( 'blur', FLBuilderLayout._showFormFieldPlaceholderFallback ); 1389 } 1390 }, 1391 1392 /** 1393 * Hides a fallback placeholder on focus. 1394 * 1395 * @since 1.5.4 1396 * @access private 1397 * @method _hideFormFieldPlaceholderFallback 1398 */ 1399 _hideFormFieldPlaceholderFallback: function() 1400 { 1401 var field = $( this ), 1402 val = field.val(), 1403 placeholder = field.attr( 'placeholder' ); 1404 1405 if ( val == placeholder ) { 1406 field.val( '' ); 1407 } 1408 }, 1409 1410 /** 1411 * Shows a fallback placeholder on blur. 1412 * 1413 * @since 1.5.4 1414 * @access private 1415 * @method _showFormFieldPlaceholderFallback 1416 */ 1417 _showFormFieldPlaceholderFallback: function() 1418 { 1419 var field = $( this ), 1420 val = field.val(), 1421 placeholder = field.attr( 'placeholder' ); 1422 1423 if ( '' === val ) { 1424 field.val( placeholder ); 1425 } 1426 }, 1427 1428 /** 1429 * Clears a form field error message. 1430 * 1431 * @since 1.5.4 1432 * @access private 1433 * @method _clearFormFieldError 1434 */ 1435 _clearFormFieldError: function() 1436 { 1437 var field = $( this ); 1438 1439 field.removeAttr( 'aria-invalid' ); 1440 field.removeClass( 'fl-form-error' ); 1441 const message = field.attr('aria-describedby'); 1442 message ? $( '#' + message ).hide() : field.siblings( '.fl-form-error-message' ).hide(); 1443 }, 1444 1445 /** 1446 * Init Row Shape Layer's height. 1447 * 1448 * @since 2.5.3 1449 * @access private 1450 * @method _initRowShapeLayerHeight 1451 */ 1452 _initRowShapeLayerHeight: function () { 1453 FLBuilderLayout._adjustRowShapeLayerHeight(); 1454 $( window ).on( 'resize', FLBuilderLayout._adjustRowShapeLayerHeight ); 1455 }, 1456 1457 /** 1458 * Set parent column width of nested columns when it's zero or blank. 1459 * 1460 * @since 2.7.4 1461 * @access private 1462 * @method _initNestedColsWidth 1463 */ 1464 _initNestedColsWidth: function() 1465 { 1466 var nestedCols = $( '.fl-col-has-cols' ); 1467 1468 if ( nestedCols.length <= 0 ) { 1469 return; 1470 } 1471 1472 $( nestedCols ).each( function(index, col ){ 1473 if ( $( col ).width() <= 0 ) { 1474 $( col ).css( 'width', FLBuilderLayoutConfig.emptyColWidth ); 1475 } 1476 }); 1477 }, 1478 1479 /** 1480 * Adjust Row Shape Layer's height to fix to remove the fine line that appears on certain screen sizes. 1481 * 1482 * @since 2.5.3 1483 * @access private 1484 * @method _adjustRowShapeLayerHeight 1485 */
1486 _adjustRowShapeLayerHeight: function() { 1487 var rowShapeLayers = $('.fl-builder-shape-layer'); 1488 1489 $( rowShapeLayers ).each(function (index) { 1490 var rowShapeLayer = $(this), 1491 shape = $(rowShapeLayer).find('svg'), 1492 height = shape.height(), 1493 excludeShapes = '.fl-builder-shape-circle, .fl-builder-shape-dot-cluster, .fl-builder-shape-topography, .fl-builder-shape-rect'; 1494 1495 if ( ! rowShapeLayer.is( excludeShapes ) ) { 1496 $(shape).css('height', Math.ceil( height ) ); 1497 } 1498 }); 1499 }, 1500 _string_to_slug: function( str ) { 1501 str = str.replace(/^\s+|\s+$/g, ''); // trim 1502 if ( 'undefined' == typeof window._fl_string_to_slug_regex ) { 1503 regex = new RegExp('[^a-zA-Z0-9\'":() !.,-_|]', 'g'); 1504 } else { 1505 regex = new RegExp('[^' + window._fl_string_to_slug_regex + '\'":\(\) !.,-_|\\\p{Letter}]', 'ug'); 1506 } 1507 str = str.replace(regex, '') // remove invalid chars 1508 .replace(/\s+/g, ' '); // collapse whitespace and replace by a space 1509 return str; 1510 }, 1511 _reorderMenu: function() { 1512 if ( $('#wp-admin-bar-fl-builder-frontend-edit-link-default li').length > 1 ) { 1513 $( '#wp-admin-bar-fl-builder-frontend-duplicate-link' ) 1514 .appendTo('#wp-admin-bar-fl-builder-frontend-edit-link-default') 1515 .css( 'padding-top', '5px' ) 1516 .css( 'border-top', '2px solid #1D2125' ) 1517 .css( 'margin-top', '5px' ) 1518 } 1519 } 1520 }; 1521 1522 /* Initializes the builder layout. */ 1523 $(function(){ 1524 FLBuilderLayout.init(); 1525 }); 1526 1527})(jQuery); 1528(function($){ 1529 1530 if(typeof FLBuilderLayoutModules !== 'undefined') { 1531 return; 1532 } 1533 1534 /** 1535 * Helper class with generic logic for builder modules. 1536 * If generic module code needs to work in both the block 1537 * editor and builder, it should go here. Otherwise, it 1538 * should go on FLBuilderLayout. 1539 * 1540 * @since 2.9 1541 */ 1542 FLBuilderLayoutModules = { 1543 1544 /** 1545 * Initializes builder module logic. 1546 * 1547 * @since 2.9 1548 * @method init 1549 */ 1550 init: function() 1551 { 1552 // Only init if the builder isn't active. 1553 if ( 0 === $('.fl-builder-edit').length ) { 1554 1555 // Init module animations. 1556 FLBuilderLayoutModules._initModuleAnimations(); 1557 } 1558 }, 1559 1560 /** 1561 * Initializes module animations. 1562 * 1563 * @since 1.1.9 1564 * @access private 1565 * @method _initModuleAnimations 1566 */ 1567 _initModuleAnimations: function() 1568 { 1569 if(typeof jQuery.fn.waypoint !== 'undefined') { 1570 $('.fl-animation').each( function() { 1571 var node = $( this ), 1572 nodeTop = node.offset().top, 1573 winHeight = $( window ).height(), 1574 bodyHeight = $( 'body' ).height(), 1575 waypoint = FLBuilderLayoutConfig.waypoint, 1576 offset = '80%'; 1577 1578 if ( typeof waypoint.offset !== undefined ) { 1579 offset = FLBuilderLayoutConfig.waypoint.offset + '%'; 1580 } 1581 1582 if ( bodyHeight - nodeTop < winHeight * 0.2 ) { 1583 offset = '100%'; 1584 } 1585 1586 node.waypoint({ 1587 offset: offset, 1588 handler: FLBuilderLayoutModules._doModuleAnimation 1589 }); 1590 } ); 1591 } 1592 }, 1593 1594 /** 1595 * Runs a module animation. 1596 * 1597 * @since 1.1.9 1598 * @access private 1599 * @method _doModuleAnimation 1600 */ 1601 _doModuleAnimation: function() 1602 { 1603 var module = 'undefined' == typeof this.element ? $(this) : $(this.element), 1604 delay = parseFloat(module.data('animation-delay')), 1605 duration = parseFloat(module.data('animation-duration')); 1606 1607 if ( ! isNaN( duration ) ) { 1608 module.css( 'animation-duration', duration + 's' ); 1609 } 1610 1611 if(!isNaN(delay) && delay > 0) { 1612 setTimeout(function(){ 1613 module.addClass('fl-animated'); 1614 }, delay * 1000); 1615 } else { 1616 setTimeout(function(){ 1617 module.addClass('fl-animated'); 1618 }, 1); 1619 } 1620 } 1621 }; 1622 1623 /* Initializes builder module logic. */ 1624 $(function(){ 1625 FLBuilderLayoutModules.init(); 1626 }); 1627 1628})(jQuery); 1629 1630/* Start Global JS */ 1631const addToCart = document.querySelector('#singleCartBTN .add_to_cart_button'); 1632 1633if (addToCart) { 1634 addToCart.innerHTML = "buy now"; 1635 addToCart.style.color = 'rgba(255, 255, 255, 1)'; 1636 addToCart.addEventListener('mouseover', function(e) { 1637 e.target.style.color = '#ffca08'; 1638 }); 1639 addToCart.addEventListener('mouseout', function(e) { 1640 e.target.style.color = '#ffffff'; 1641 }); 1642} 1643/* End Global JS */ 1644 1645 1646;(function($) { 1647 1648 FLBuilderTabs = function( settings ) 1649 { 1650 this.settings = settings; 1651 this.nodeClass = '.fl-node-' + settings.id; 1652 this.tabsOnMobile = settings.tabsOnMobile; 1653 this.activeTab = settings.activeTab; 1654 this._init(); 1655 }; 1656 1657 FLBuilderTabs.prototype = { 1658 1659 settings : {}, 1660 nodeClass : '', 1661 1662 _init: function() 1663 { 1664 var win = $(window); 1665 1666 $(this.nodeClass + ' .fl-tabs-labels .fl-tabs-label').click($.proxy(this._labelClick, this)); 1667 $(this.nodeClass + ' .fl-tabs-labels .fl-tabs-label').on('keydown', $.proxy(this._labelClick, this)); 1668 $(this.nodeClass + ' .fl-tabs-panels .fl-tabs-label').click($.proxy(this._responsiveLabelClick, this)); 1669 $(this.nodeClass + ' .fl-tabs-panels .fl-tabs-label').on('keydown', $.proxy(this._responsiveLabelClick, this)); 1670 1671 win.on('resize', $.proxy( this._setupTabs, this)); 1672
1673 if($(this.nodeClass + ' .fl-tabs-vertical').length > 0) { 1674 this._resize(); 1675 win.off('resize' + this.nodeClass); 1676 win.on('resize' + this.nodeClass, $.proxy(this._resize, this)); 1677 } 1678 1679 FLBuilderLayout.preloadAudio( this.nodeClass + ' .fl-tabs-panel-content' ); 1680 this._setupTabs(); 1681 }, 1682 1683 _labelClick: function(e) 1684 { 1685 var label = $(e.target).closest('.fl-tabs-label'), 1686 index = label.data('index'), 1687 wrap = label.closest('.fl-tabs'), 1688 allIcons = wrap.find('.fl-tabs-panels .fl-tabs-label .fas'), 1689 icon = wrap.find('.fl-tabs-panels .fl-tabs-label[data-index="' + index + '"] .fas'); 1690 1691 // Click or keyboard (enter or space) input? 1692 if(!this._validClick(e)) { 1693 return; 1694 } 1695 1696 // Toggle the responsive icons. 1697 allIcons.addClass('fa-plus'); 1698 icon.removeClass('fa-plus'); 1699 1700 // Toggle the tabs. 1701 wrap.find('.fl-tabs-labels:first .fl-tab-active').removeClass('fl-tab-active').attr('aria-selected', 'false'); 1702 wrap.find('.fl-tabs-panels:first > .fl-tabs-panel > .fl-tab-active').removeClass('fl-tab-active').attr('aria-selected', 'false'); 1703 wrap.find('.fl-tabs-panels:first > .fl-tabs-panel > .fl-tabs-panel-content').attr('aria-hidden', 'true').css('display', ''); 1704 1705 wrap.find('.fl-tabs-labels:first .fl-tabs-label[data-index="' + index + '"]').addClass('fl-tab-active').attr('aria-selected', 'true'); 1706 wrap.find('.fl-tabs-panels:first > .fl-tabs-panel > .fl-tabs-panel-label[data-index="' + index + '"]').addClass('fl-tab-active').attr('aria-selected', 'true'); 1707 wrap.find('.fl-tabs-panels:first > .fl-tabs-panel > .fl-tabs-panel-content[data-index="' + index + '"]').addClass('fl-tab-active').attr('aria-hidden', 'false'); 1708 1709 // Gallery module support. 1710 FLBuilderLayout.refreshGalleries( wrap.find('.fl-tabs-panel-content[data-index="' + index + '"]') ); 1711 1712 // Grid layout support (uses Masonry) 1713 FLBuilderLayout.refreshGridLayout( wrap.find('.fl-tabs-panel-content[data-index="' + index + '"]') ); 1714 1715 // Post Carousel support (uses BxSlider) 1716 FLBuilderLayout.reloadSlider( wrap.find('.fl-tabs-panel-content[data-index="' + index + '"]') ); 1717 1718 // WP audio shortcode support 1719 FLBuilderLayout.resizeAudio( wrap.find('.fl-tabs-panel-content[data-index="' + index + '"]') ); 1720 1721 // Slideshow module support. 1722 FLBuilderLayout.resizeSlideshow(); 1723 1724 // Reload Google Map embed. 1725 FLBuilderLayout.reloadGoogleMap( wrap.find('.fl-tabs-panel-content[data-index="' + index + '"]') ); 1726 1727 e.preventDefault(); 1728 }, 1729 1730 _responsiveLabelClick: function(e) 1731 { 1732 var label = $(e.target).closest('.fl-tabs-label'), 1733 wrap = label.closest('.fl-tabs'), 1734 index = label.data('index'), 1735 content = label.siblings('.fl-tabs-panel-content'), 1736 activeContent = wrap.find('.fl-tabs-panel-content.fl-tab-active'), 1737 allIcons = wrap.find('.fl-tabs-panels .fl-tabs-label > .fas'), 1738 icon = label.find('.fas'); 1739 1740 // Click or keyboard (enter or space) input? 1741 if(!this._validClick(e)) { 1742 return; 1743 } 1744 1745 if( label.hasClass( 'fl-tab-active' ) || wrap.hasClass( 'fl-tabs-animation' ) ) { 1746 return; 1747 } 1748 1749 // Toggle the icons. 1750 allIcons.addClass('fa-plus'); 1751 icon.removeClass('fa-plus'); 1752 1753 // Run the animations. 1754 wrap.addClass('fl-tabs-animation'); 1755 activeContent.slideUp('normal'); 1756 1757 content.slideDown('normal', function(){ 1758 1759 wrap.find('.fl-tab-active').removeClass('fl-tab-active').attr('aria-selected', 'false'); 1760 wrap.find('.fl-tabs-panels:first > .fl-tabs-panel > .fl-tabs-panel-content').attr('aria-hidden', 'true'); 1761 wrap.find('.fl-tabs-label[data-index="' + index + '"]').addClass('fl-tab-active').attr('aria-selected', 'true'); 1762 wrap.find('.fl-tabs-panels:first > .fl-tabs-panel > .fl-tabs-panel-content[data-index="' + index + '"]').attr('aria-hidden', 'false'); 1763 content.addClass('fl-tab-active'); 1764 wrap.removeClass('fl-tabs-animation'); 1765 1766 // Gallery module support. 1767 FLBuilderLayout.refreshGalleries( content ); 1768 1769 // Grid layout support (uses Masonry) 1770 FLBuilderLayout.refreshGridLayout( content ); 1771 1772 // Post Carousel support (uses BxSlider) 1773 FLBuilderLayout.reloadSlider( content ); 1774 1775 // WP audio shortcode support 1776 FLBuilderLayout.resizeAudio( content ); 1777 1778 // Reload Google Map embed. 1779 FLBuilderLayout.reloadGoogleMap( wrap.find('.fl-tabs-panel-content[data-index="' + index + '"]') ); 1780 1781 if(label.offset().top < $(window).scrollTop() + 100) { 1782 $('html, body').animate({ scrollTop: label.offset().top - 100 }, 500, 'swing'); 1783 } 1784 }); 1785 }, 1786 1787 _resize: function() 1788 {
1789 $(this.nodeClass + ' .fl-tabs-vertical').each($.proxy(this._resizeVertical, this)); 1790 }, 1791 1792 _resizeVertical: function(e) 1793 { 1794 var wrap = $(this.nodeClass + ' .fl-tabs-vertical'), 1795 labels = wrap.find('.fl-tabs-labels'), 1796 panels = wrap.find('.fl-tabs-panels'); 1797 1798 panels.css('min-height', labels.height() + 'px'); 1799 }, 1800 1801 _validClick: function(e) 1802 { 1803 return (e.which == 1 || e.which == 13 || e.which == 32) ? true : false; 1804 }, 1805 1806 _setupTabs: function() { 1807 index = this.activeTab - 1; 1808 if ( index < 0 ) { 1809 index = 0; 1810 } 1811 // $( this.nodeClass ).find('.fl-tabs-panel-content[data-index="' + index + '"]').addClass('fl-tab-active'); 1812 var winWidth = $(window).width(), 1813 activeTabContent = $( this.nodeClass + ' .fl-tabs-panel-content.fl-tab-active' ), 1814 activeTabPanel = activeTabContent.parent(), 1815 activeTabLabelIcon = activeTabPanel.find('.fl-tabs-label > i'), 1816 smallBreakPoint = FLBuilderLayoutConfig.breakpoints.small; 1817 1818 if ( winWidth <= smallBreakPoint && 'close-all' == this.tabsOnMobile ) { 1819 activeTabContent.hide(); 1820 activeTabLabelIcon.addClass('fa-plus'); 1821 activeTabPanel.find('.fl-tab-active').removeClass('fl-tab-active').attr('aria-hidden', 'true'); 1822 } else if ( winWidth >= smallBreakPoint ) { 1823 activeTabContent.show(); 1824 activeTabLabelIcon.removeClass('fa-plus'); 1825 } 1826 }, 1827 }; 1828 1829})(jQuery); 1830(function($) { 1831 1832 $(function() { 1833 1834 new FLBuilderTabs({ 1835 id: '5f8de49eea527', 1836 tabsOnMobile: 'open-active', 1837 activeTab: '1', 1838 }); 1839 }); 1840 1841})(jQuery); 1842 1843/* Start Global Node Custom JS */ 1844 1845/* End Global Node Custom JS */ 1846 1847 1848/* Start Layout Custom JS */ 1849 1850/* End Layout Custom JS */ 1851 1852 1853 1854;/* eslint-disable no-undef */ 1855(function($) { 1856 1857 /** 1858 * Class for Menu Module 1859 * 1860 * @since 1.6.1 1861 */ 1862 FLBuilderMenu = function( settings ){ 1863 1864 // set params 1865 this.nodeId = settings.id; 1866 this.nodeClass = '.fl-node-' + settings.id; 1867 this.wrapperClass = this.nodeClass + ' .fl-menu'; 1868 this.type = settings.type; 1869 this.mobileToggle = settings.mobile; 1870 this.mobileBelowRow = settings.mobileBelowRow; 1871 this.mobileFlyout = settings.mobileFlyout; 1872 this.breakPoints = settings.breakPoints; 1873 this.mobileBreakpoint = settings.mobileBreakpoint; 1874 this.currentBrowserWidth = $( window ).width(); 1875 this.postId = settings.postId; 1876 this.mobileStacked = settings.mobileStacked; 1877 this.submenuIcon = settings.submenuIcon; 1878 this.flyoutWidth = settings.flyoutWidth; 1879 1880 // initialize the menu 1881 this._initMenu(); 1882 1883 // check if viewport is resizing 1884 $( window ).on( 'resize', $.proxy( function() { 1885 1886 var width = $( window ).width(); 1887 1888 // if screen width is resized, reload the menu 1889 if( width != this.currentBrowserWidth ){ 1890 1891 this.currentBrowserWidth = width; 1892 this._initMenu( true ); 1893 this._clickOrHover( true ); 1894 } 1895 1896 this._resizeFlyoutMenuPanel(); 1897 }, this ) ); 1898 1899 $( window ).on( 'scroll', $.proxy( function() { 1900 this._resizeFlyoutMenuPanel(); 1901 }, this ) ); 1902 1903 // Move focus to the first menu item if the layout is set to BelowRow 1904 $( this.wrapperClass ).on( 'keydown', '.fl-menu-mobile-toggle, .menu-item:first a:first', $.proxy( function ( event ) { 1905 // Make sure it is a tab key with both the menu toggle enabled & below row layout set 1906 if ( event.key !== 'Tab' || ! this._isMenuToggle() || ! this._isMobileBelowRowEnabled() ) { 1907 return; 1908 } 1909 // Trigger focus on the mobile toggle button if the user is shift tabbing from the first menu item 1910 if ( event.shiftKey && $( event.currentTarget ).is( 'a' ) ) { 1911 event.preventDefault(); 1912 $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ).trigger( 'focus' ); 1913 } 1914 // Trigger focus on the first menu item if the user is tabbing from the mobile toggle button 1915 else if ( ! event.shiftKey && $( event.currentTarget ).hasClass( 'fl-menu-mobile-toggle' ) && $( event.currentTarget ).hasClass( 'fl-active' ) ) { 1916 event.preventDefault(); 1917 $( this.wrapperClass ).find( '.menu-item' ).first().find( 'a' ).first().trigger( 'focus' ); 1918 } 1919 }, this ) ); 1920 1921 // Close mobile menu or accordion when tabbing out from the first or last menu item 1922 $( this.wrapperClass ).on( 'focusout', $.proxy( function ( event ) { 1923 if ( $( this.wrapperClass + ' nav' ).has( $( event.relatedTarget ) ).length === 0 ) { 1924 if ( this.type === 'accordion' ) { 1925 this._toggleSubmenu( $( event.target ).parents( '.fl-has-submenu' ).last(), false ); 1926 } 1927 else { 1928 this._clickOrHover( true ); 1929 } 1930 const mobileToggle = $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ); 1931 if ( this._isMenuToggle() && mobileToggle.hasClass( 'fl-active' ) && ! $( event.relatedTarget ).is( mobileToggle ) && 'expanded' !== this.mobileToggle ) { 1932 mobileToggle.trigger( 'click' ); 1933 } 1934 } 1935 }, this ) ); 1936 1937 }; 1938 1939 FLBuilderMenu.prototype = { 1940 nodeClass : '', 1941 wrapperClass: '', 1942 type : '', 1943 breakPoints : {}, 1944 $submenus : null, 1945 1946 /** 1947 * Check if the screen size fits a mobile viewport. 1948 * 1949 * @since 1.6.1 1950 * @return bool 1951 */ 1952 _isMobile: function(){ 1953 return this.currentBrowserWidth <= this.breakPoints.small ? true : false;
1954 }, 1955 1956 /** 1957 * Check if the device is connected to a mouse. 1958 * 1959 * @since 2.9.0 1960 * @return bool 1961 */ 1962 _isMouseAvailable: function(){ 1963 return window.matchMedia("(pointer: fine) and (hover: hover)").matches; 1964 }, 1965 1966 /** 1967 * Check if the screen size fits a medium viewport. 1968 * 1969 * @since 1.10.5 1970 * @return bool 1971 */ 1972 _isMedium: function(){ 1973 return this.currentBrowserWidth <= this.breakPoints.medium ? true : false; 1974 }, 1975 1976 /** 1977 * Check if the screen size fits a large viewport. 1978 * 1979 * @since 1.10.5 1980 * @return bool 1981 */ 1982 _isLarge: function(){ 1983 return this.currentBrowserWidth <= this.breakPoints.large ? true : false; 1984 }, 1985 1986 /** 1987 * Check if the menu should toggle for the current viewport base on the selected breakpoint. 1988 * 1989 * @see this._isMobile() 1990 * @see this._isMedium() 1991 * @since 1.10.5 1992 * @return bool 1993 */ 1994 _isMenuToggle: function(){ 1995 if ( ( 'always' == this.mobileBreakpoint 1996 || ( this._isMobile() && 'mobile' == this.mobileBreakpoint ) 1997 || ( this._isMedium() && 'medium-mobile' == this.mobileBreakpoint ) 1998 || ( this._isLarge() && 'large-mobile' == this.mobileBreakpoint ) 1999 ) && ( $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ).is(':visible') || 'expanded' == this.mobileToggle ) ) { 2000 return true; 2001 } 2002 2003 return false; 2004 }, 2005 2006 /** 2007 * Initialize the toggle logic for the menu. 2008 * 2009 * @see this._isMenuToggle() 2010 * @see this._menuOnCLick() 2011 * @see this._clickOrHover() 2012 * @see this._submenuOnRight() 2013 * @see this._submenuRowZindexFix() 2014 * @see this._toggleForMobile() 2015 * @since 1.6.1 2016 * @return void 2017 */ 2018 _initMenu: function( resized = false ){ 2019 this._setupSubmenu(); 2020 if ( ! resized ) { 2021 this._menuOnFocus(); 2022 this._menuOnClick(); 2023 this._menuOnEscape(); 2024 } 2025 if ( $( this.nodeClass ).length && this.type == 'horizontal' ) { 2026 this._initMegaMenus(); 2027 } 2028 2029 if( this._isMenuToggle() || this.type == 'accordion' ){ 2030 2031 $( this.wrapperClass ).off( 'mouseenter mouseleave' ); 2032 this._clickOrHover(); 2033 2034 } else { 2035 this._submenuOnRight(); 2036 this._submenuRowZindexFix(); 2037 } 2038 2039 if( this.mobileToggle != 'expanded' ){ 2040 this._toggleForMobile(); 2041 } 2042 2043 if( $( this.wrapperClass ).find( '.fl-menu-search-item' ).length ){ 2044 this._toggleMenuSearch(); 2045 } 2046 2047 if( $( this.wrapperClass ).find( '.fl-menu-cart-item').length ){ 2048 this._wooUpdateParams(); 2049 } 2050 }, 2051 2052 /** 2053 * Whether the toggle behavior for submenus is through hovering or not. 2054 * 2055 * @since 2.5 2056 * @return void 2057 */ 2058 _setupSubmenu: function() { 2059 if ( ! this._isMouseAvailable() || this._isMenuToggle() || this.type === 'accordion' ) { 2060 $( this.wrapperClass ).addClass( 'no-hover' ); 2061 } 2062 else { 2063 $( this.wrapperClass ).removeClass( 'no-hover' ); 2064 } 2065 }, 2066 2067 /** 2068 * Logic for menu items when focused. 2069 * 2070 * @since 1.9.0 2071 * @return void 2072 */ 2073 _menuOnFocus: function(){ 2074 $( this.wrapperClass ).on( 'focus', 'a:not(.fl-button), .fl-menu-toggle', $.proxy( function( event ) { 2075 const focusedMenuItem = $( event.currentTarget ).closest( '.menu-item' ); 2076 const blurredMenuItem = $( event.relatedTarget ).closest( '.menu-item' ); 2077 // In case the blurred & focused items are siblings 2078 if ( focusedMenuItem.closest( 'ul' ).is( blurredMenuItem.closest( 'ul' ) ) ) { 2079 // Check if the focus event is not between the link & submenu icon of the same menu item 2080 if ( ! focusedMenuItem.is( blurredMenuItem ) ) { 2081 this._toggleSubmenu( blurredMenuItem, false ); 2082 } 2083 } 2084 // In case neither items are child & parent nor vice versa 2085 else if ( focusedMenuItem.has( blurredMenuItem ).length === blurredMenuItem.has( focusedMenuItem ).length ) { 2086 // Get the blurred item parent & is the sibling of the focused item 2087 blurredMenuItem.parents( '.fl-has-submenu' ).each( ( _, parent ) => { 2088 if ( focusedMenuItem.closest( 'ul' ).is( $( parent ).closest( 'ul' ) ) ) { 2089 this._toggleSubmenu( $( parent ), false ); 2090 return false; 2091 } 2092 } ); 2093 } 2094 // In case the blurred item is a child of the focused item
2095 else if ( focusedMenuItem.has( blurredMenuItem ).length ) { 2096 if ( this.type !== 'accordion' && this.submenuIcon === 'none' ) { 2097 this._toggleSubmenu( blurredMenuItem, false ); 2098 } 2099 } 2100 }, this ) ); 2101 }, 2102 2103 /** 2104 * Logic for menu items when Escape key is pressed. 2105 * 2106 * @since 2.7.1 2107 * @return void 2108 */ 2109 _menuOnEscape: function(){ 2110 $( this.wrapperClass ).on( 'keydown', $.proxy( function( event ){ 2111 if ( event.key !== 'Escape' ) return; 2112 const menuItem = $( event.target ).closest( '.menu-item' ); 2113 const mobileToggle = $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ); 2114 // In case there is a focused menu item 2115 if ( menuItem.length || $( event.target ).hasClass( 'fl-menu-mobile-close' ) ) { 2116 // Close the search form if it is open 2117 if ( menuItem.hasClass( 'fl-menu-search-item' ) && $( this.wrapperClass ).find('.fl-search-form-input-wrap').is( ':visible' ) ) { 2118 $( this.wrapperClass ).find('.fl-menu-search-item .fl-button:is(a, button:not([type="submit"]))').trigger( 'focus' ); 2119 } 2120 // Close all the submenus of the focused menu item 2121 else if ( menuItem.hasClass( 'fl-has-submenu' ) && menuItem.find( '.sub-menu' ).first().is( ':visible' ) ) { 2122 this._toggleSubmenu( menuItem, false ); 2123 } 2124 else { 2125 // Shift focus to the parent while closing all its submenus 2126 const parentMenuItem = menuItem.parents( '.fl-has-submenu' ).not( '.mega-menu.hide-heading, .mega-menu-disabled.hide-heading' ).first(); 2127 // Close the mobile menu if there is no parent menu item and the mobile toggle is active 2128 if ( parentMenuItem.length === 0 && this._isMenuToggle() && mobileToggle.hasClass( 'fl-active' ) ) { 2129 // Blur to trigger the focus-out event for closing the menu 2130 mobileToggle.trigger( 'blur' ).trigger( 'focus' ); 2131 } 2132 // Close the parent submenu and shift focus to its link 2133 else if ( parentMenuItem.length !== 0 ) { 2134 this._toggleSubmenu( parentMenuItem, false ); 2135 parentMenuItem.find( 'a' ).first().trigger( 'focus' ); 2136 } 2137 } 2138 } 2139 // Close the mobile menu if the mobile toggle is active 2140 else if ( $( event.target ).hasClass( 'fl-menu-mobile-toggle' ) && mobileToggle.hasClass( 'fl-active' ) ) { 2141 // Blur to trigger the focus-out event for closing the menu 2142 mobileToggle.trigger( 'blur' ).trigger( 'focus' ); 2143 } 2144 }, this ) ); 2145 }, 2146 2147 /** 2148 * Logic for menu items when clicked. 2149 * 2150 * @since 1.6.1 2151 * @return void 2152 */ 2153 _menuOnClick: function(){ 2154 // Fallback for span elements and fake links to interact with the keyboard 2155 $( this.wrapperClass ).on( 'keydown', 'a:not(.fl-button, [href]), span.fl-menu-toggle', $.proxy( function( event ) { 2156 if ( event.key === 'Enter' || event.key === ' ' ) { 2157 event.preventDefault(); 2158 $( event.currentTarget ).trigger( 'click' ); 2159 } 2160 }, this ) ); 2161 $( this.wrapperClass ).on( 'click', 'a, .fl-menu-toggle', $.proxy( function( event ) { 2162 // Only allow mouse clicks with accordion & mobile menus 2163 if ( this._isMouseAvailable() && ! this._isMenuToggle() && event.detail && this.type !== 'accordion' ) return; 2164 // Links only open & do not toggle submenus if there is either a submenu icon or an accordion layout 2165 if ( $( event.currentTarget ).is( 'a' ) && ( this.submenuIcon !== 'none' || this.type === 'accordion' ) ) return; 2166 const menuItem = $( event.currentTarget ).closest( '.menu-item, .fl-menu-logo' ); 2167 const menuLink = menuItem.find( 'a' ).first().attr( 'href' ); 2168 const submenuHidden = menuItem.find( '.sub-menu' ).first().is( ':hidden' ); 2169 if ( typeof menuLink === 'undefined' || menuLink === '#' || submenuHidden ) { 2170 event.preventDefault(); 2171 } 2172 if ( $( event.currentTarget ).hasClass( 'fl-menu-toggle' ) || submenuHidden ) { 2173 this._toggleSubmenu( menuItem, submenuHidden ); 2174 } 2175 }, this ) ); 2176 }, 2177 2178 /** 2179 * Controls toggling the visibility of all submenu items for desktop & mobile. 2180 * 2181 * @since 2.10 2182 * @return void 2183 */ 2184 _toggleSubmenu: function ( menuItem, opened ) { 2185 const togglingClass = this._isMenuToggle() || this.type === 'accordion' ? 'fl-active' : 'focus'; 2186 const toggleElement = this.submenuIcon === 'none' ? 'a' : '.fl-menu-toggle'; 2187 const hiddenMenu = '.mega-menu.hide-heading, .mega-menu-disabled.hide-heading'; 2188 if ( opened && menuItem.hasClass( 'fl-has-submenu' ) && ! menuItem.is( hiddenMenu ) ) { 2189 menuItem.addClass( togglingClass ); 2190 menuItem.find( toggleElement ).first().attr( 'aria-expanded', true ); 2191 if ( this._isMenuToggle() || this.type === 'accordion' ) { 2192 menuItem.find( '.sub-menu' ).first().filter( ':hidden' ).slideDown(); 2193 } 2194 } 2195 else { 2196 menuItem.parent().find( '.menu-item' ).removeClass( togglingClass ); 2197 menuItem.parent().find( '.fl-has-submenu' ).not( hiddenMenu ).find( toggleElement ).attr( 'aria-expanded', false ); 2198 if ( this._isMenuToggle() || this.type === 'accordion' ) {
2199 menuItem.find( '.sub-menu' ).filter( ':visible' ).slideUp(); 2200 } 2201 } 2202 }, 2203 2204 /** 2205 * Changes general styling and behavior of menus based on mobile / desktop viewport. 2206 * 2207 * @see this._isMenuToggle() 2208 * @since 1.6.1 2209 * @return void 2210 */ 2211 _clickOrHover: function( clear = false ){ 2212 const selector = this._isMobileBelowRowEnabled() ? this.nodeClass + '-clone' : this.nodeClass; 2213 this.$submenus = this.$submenus || $( selector ).find( '.sub-menu' ); 2214 2215 const className = this._isMenuToggle() || this.type === 'accordion' ? 'fl-active' : 'focus'; 2216 const toggleElement = this.submenuIcon === 'none' ? 'a' : '.fl-menu-toggle'; 2217 const hiddenMenu = '.mega-menu.hide-heading, .mega-menu-disabled.hide-heading'; 2218 $( selector ).find( '.fl-has-submenu' ).not( hiddenMenu ).each( function() { 2219 if( clear || ! $ ( this ).hasClass( className ) ){ 2220 if ( clear ) $( this ).removeClass( className ); 2221 $( this ).find( toggleElement ).first().attr( 'aria-expanded', false ); 2222 if ( className === 'fl-active' ) { 2223 $( this ).find( '.sub-menu' ).fadeOut(); 2224 } else if ( className === 'focus' ) { 2225 $( this ).find( '.sub-menu' ).css( { 2226 'display' : '', 2227 'opacity' : '' 2228 } ); 2229 } 2230 } 2231 } ); 2232 }, 2233 2234 /** 2235 * Logic to prevent submenus to go outside viewport boundaries. 2236 * 2237 * @since 1.6.1 2238 * @return void 2239 */ 2240 _submenuOnRight: function(){ 2241 2242 $( this.wrapperClass ) 2243 .on( 'mouseenter focus', '.fl-has-submenu', $.proxy( function( e ){ 2244 2245 if( $ ( e.currentTarget ).find('.sub-menu').length === 0 ) { 2246 return; 2247 } 2248 2249 var $link = $( e.currentTarget ), 2250 $parent = $link.parent(), 2251 $subMenu = $link.find( '.sub-menu' ), 2252 subMenuWidth = $subMenu.width(), 2253 subMenuPos = 0, 2254 bodyWidth = $( 'body' ).width(); 2255 2256 if( $link.closest( '.fl-menu-submenu-right' ).length !== 0) { 2257 2258 $link.addClass( 'fl-menu-submenu-right' ); 2259 2260 } else if( $( 'body' ).hasClass( 'rtl' ) ) { 2261 2262 subMenuPos = $parent.is( '.sub-menu' ) ? 2263 $parent.offset().left - subMenuWidth: 2264 $link.offset().left - $link.width() - subMenuWidth; 2265 2266 if( subMenuPos <= 0 ) { 2267 $link.addClass( 'fl-menu-submenu-right' ); 2268 } 2269 2270 } else { 2271 2272 subMenuPos = $parent.is( '.sub-menu' ) ? 2273 $parent.offset().left + $parent.width() + subMenuWidth : 2274 $link.offset().left + $link.width() + subMenuWidth; 2275 2276 if( subMenuPos > bodyWidth ) { 2277 $link.addClass('fl-menu-submenu-right'); 2278 } 2279 } 2280 }, this ) ) 2281 .on( 'mouseleave', '.fl-has-submenu', $.proxy( function( e ){ 2282 $( e.currentTarget ).removeClass( 'fl-menu-submenu-right' ); 2283 }, this ) ); 2284 2285 }, 2286 2287 /** 2288 * Logic to prevent submenus to go behind the next overlay row. 2289 * 2290 * @since 1.10.9 2291 * @return void 2292 */ 2293 _submenuRowZindexFix: function(){ 2294 2295 $( this.wrapperClass ) 2296 .on( 'mouseenter', 'ul.menu > .fl-has-submenu', $.proxy( function( e ){ 2297 2298 if( $ ( e.currentTarget ).find('.sub-menu').length === 0 ) { 2299 return; 2300 } 2301 2302 $( this.nodeClass ) 2303 .closest( '.fl-row' ) 2304 .find( '.fl-row-content' ) 2305 .css( 'z-index', '10' ); 2306 2307 }, this ) ) 2308 .on( 'mouseleave', 'ul.menu > .fl-has-submenu', $.proxy( function(){ 2309 2310 $( this.nodeClass ) 2311 .closest( '.fl-row' ) 2312 .find( '.fl-row-content' ) 2313 .css( 'z-index', '' ); 2314 2315 }, this ) ); 2316 }, 2317 2318 /** 2319 * Logic for the mobile menu button. 2320 * 2321 * @since 1.6.1 2322 * @return void 2323 */ 2324 _toggleForMobile: function(){ 2325 2326 var $wrapper = null, 2327 $menu = null, 2328 self = this; 2329 2330 $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ).attr('aria-controls', $( this.wrapperClass ).find( 'ul.menu' ).attr('id')); 2331 2332 if( this._isMenuToggle() ){ 2333 2334 if ( this._isMobileBelowRowEnabled() ) { 2335 this._placeMobileMenuBelowRow(); 2336 $wrapper = $( this.wrapperClass ); 2337 $menu = $( this.nodeClass + '-clone' ); 2338 $menu.find( 'ul.menu' ).show(); 2339 } 2340 else { 2341 $wrapper = $( this.wrapperClass ); 2342 $menu = $wrapper.find( '.menu' ); 2343 } 2344 2345 if( !$wrapper.find( '.fl-menu-mobile-toggle' ).hasClass( 'fl-active' ) && ! self.mobileFlyout ){ 2346 $menu.css({ display: 'none' }); 2347 } 2348 2349 // Flayout Menu 2350 if ( self.mobileFlyout ) { 2351 this._initFlyoutMenu(); 2352 } 2353 2354 $wrapper.on( 'click', '.fl-menu-mobile-toggle', function( e ){ 2355 e.stopImmediatePropagation(); 2356 2357 $( this ).toggleClass( 'fl-active' ); 2358 2359 if ( self.mobileFlyout ) { 2360 self._toggleFlyoutMenu(); 2361 const flyoutWrapper = $( '.fl-menu-mobile-flyout' ); 2362 if ( $( this ).hasClass( 'fl-active' ) ) { 2363 flyoutWrapper.attr( 'aria-hidden', false ); 2364 flyoutWrapper.find( 'a[href], button, input, select, textarea, span.fl-menu-toggle, [tabindex="-1"]' ).attr( 'tabindex', 0 ); 2365 } 2366 else { 2367 flyoutWrapper.attr( 'aria-hidden', true ); 2368 flyoutWrapper.find( 'a[href], button, input, select, textarea, span.fl-menu-toggle, [tabindex]:not
2368([tabindex="-1"])' ).attr( 'tabindex', -1 ); 2369 } 2370 } 2371 else { 2372 var targetMenu = null; 2373 2374 if ( self.mobileBelowRow ) { 2375 var $closestCol = $( this ).parents( '.fl-col, .fl-module-box' ), 2376 $closestColGroup = $closestCol.length ? $closestCol.parent( '.fl-col-group' ) : null; 2377 targetMenu = $closestCol.length ? $closestCol.last().next( '.fl-menu-mobile-clone' ) : null; 2378 2379 if ( $closestColGroup.length ) { 2380 if ( $closestColGroup.hasClass( 'fl-col-group-responsive-reversed' ) ) { 2381 $closestColGroup.find( '.fl-menu-mobile-clone' ).css( 'order', -1 ); 2382 } else if ( $closestColGroup ) { 2383 $closestColGroup.find( '.fl-menu-mobile-clone' ).css( 'order', 2 ); 2384 } 2385 } 2386 } else { 2387 targetMenu = $( this ).closest( '.fl-menu' ).find( 'ul.menu' ); 2388 } 2389 2390 if ( targetMenu.length ) { 2391 $menu = $( targetMenu ); 2392 } 2393 2394 $menu.slideToggle(); 2395 } 2396 2397 e.stopPropagation(); 2398 } ); 2399 2400 // Hide active menu when click on anchor link ID that exists on a page 2401 $menu.off().on( 'click', '.menu-item > a[href*="#"]:not([href="#"])', function(){ 2402 var $href = $(this).attr('href'), 2403 $targetID = $href.split('#')[1], 2404 element = $('#' + $targetID); 2405 if ( $('body').find(element).length > 0 ) { 2406 $( this ).toggleClass( 'fl-active' ); 2407 FLBuilderLayout._scrollToElement( element ); 2408 if ( ! self._isMenuToggle() ) { 2409 $menu.slideToggle(); 2410 } 2411 } 2412 }); 2413 } 2414 else { 2415 2416 if ( this._isMobileBelowRowEnabled() ) { 2417 this._removeMenuFromBelowRow(); 2418 } 2419 2420 $wrapper = $( this.wrapperClass ), 2421 $menu = $wrapper.find( 'ul.menu' ); 2422 $wrapper.find( '.fl-menu-mobile-toggle' ).removeClass( 'fl-active' ); 2423 $menu.css({ display: '' }); 2424 2425 if ( ! this._isMobileBelowRowEnabled() ) { 2426 $menu.off( 'click', '.menu-item > a[href*="#"]:not([href="#"])' ); 2427 } 2428 2429 if ( this.mobileFlyout && $wrapper.find( '.fl-menu-mobile-flyout' ).length > 0 ) { 2430 $( 'body' ).css( 'margin', '' ); 2431 $( '.fl-builder-ui-pinned-content-transform' ).css( 'transform', '' ); 2432 $menu.unwrap(); 2433 $wrapper.find( '.fl-menu-mobile-close' ).remove(); 2434 $wrapper.find( '.fl-menu-mobile-opacity' ).remove(); 2435 } 2436 } 2437 }, 2438 2439 /** 2440 * Init any mega menus that exist. 2441 * 2442 * @see this._isMenuToggle() 2443 * @since 1.10.4 2444 * @return void 2445 */ 2446 _initMegaMenus: function(){ 2447 2448 var module = $( this.nodeClass ), 2449 rowContent = module.closest( '.fl-row-content' ), 2450 rowWidth = rowContent.width(), 2451 megas = module.find( '.mega-menu' ), 2452 disabled = module.find( '.mega-menu-disabled' ), 2453 isToggle = this._isMenuToggle(); 2454 2455 if ( isToggle ) { 2456 megas.removeClass( 'mega-menu' ).addClass( 'mega-menu-disabled' ); 2457 module.find( 'li.mega-menu-disabled > ul.sub-menu' ).css( 'width', '' ); 2458 rowContent.css( 'position', '' ); 2459 } else { 2460 disabled.removeClass( 'mega-menu-disabled' ).addClass( 'mega-menu' ); 2461 module.find( 'li.mega-menu > ul.sub-menu' ).css( 'width', rowWidth + 'px' ); 2462 rowContent.css( 'position', 'relative' ); 2463 } 2464 }, 2465 2466 /** 2467 * Check to see if Below Row should be enabled. 2468 * 2469 * @since 1.11 2470 * @return boolean 2471 */ 2472 _isMobileBelowRowEnabled: function() { 2473 return this.mobileBelowRow && ( $( this.nodeClass ).parents( '.fl-col, .fl-module-box' ).length ); 2474 }, 2475 2476 /** 2477 * Logic for putting the mobile menu below the menu's 2478 * column so it spans the full width of the page. 2479 * 2480 * @since 1.10 2481 * @return void 2482 */ 2483 _placeMobileMenuBelowRow: function(){ 2484 2485 if ( $( this.nodeClass + '-clone' ).length ) { 2486 return; 2487 } 2488 2489 var module = $( this.nodeClass ), 2490 clone = null, 2491 col = module.parents( '.fl-col, .fl-module-box' ).last(); 2492 2493 if ( module.length < 1 ) { 2494 return; 2495 } 2496 2497 clone = ( module.length > 1 ) ? $( module[0] ).clone() : module.clone(); 2498 module.find( 'ul.menu' ).remove(); 2499 clone.addClass( ( this.nodeClass + '-clone' ).replace( '.', '' ) ); 2500 clone.addClass( 'fl-menu-mobile-clone' ); 2501 clone.find( '.fl-menu-mobile-toggle' ).remove(); 2502 col.after( clone ); 2503 2504 // Removes animation when enabled 2505 if ( module.hasClass( 'fl-animation' ) ) { 2506 clone.removeClass( 'fl-animation' ); 2507 } 2508 2509 this._menuOnFocus(); 2510 this._menuOnClick(); 2511 this._menuOnEscape(); 2512 }, 2513 2514 /** 2515 * Logic for removing the mobile menu from below the menu's 2516 * column and putting it back in the main wrapper. 2517 * 2518 * @since 1.10 2519 * @return void 2520 */ 2521 _removeMenuFromBelowRow: function(){ 2522 2523 if ( ! $( this.nodeClass + '-clone' ).length ) { 2524 return; 2525 } 2526 2527 var module = $( this.nodeClass ), 2528 clone = $( this.nodeClass + '-clone' ), 2529 menu = clone.find( 'ul.menu' ), 2530 nav = module.find( 'nav' ); 2531
2532 if ( nav.length ) { 2533 nav.append( menu ); 2534 } else { 2535 module.find( '.fl-menu-mobile-toggle' ).after( menu ); 2536 } 2537 clone.remove(); 2538 menu.find( 'a' ).each( FLBuilderLayout._initAnchorLink ); 2539 }, 2540 2541 /** 2542 * Logic for Flyout responsive menu. 2543 * 2544 * @since 2.2 2545 * @return void 2546 */ 2547 _initFlyoutMenu: function(){ 2548 var wrapper = $( this.wrapperClass ), 2549 menu = wrapper.find( 'ul.menu' ), 2550 button = wrapper.find( '.fl-menu-mobile-toggle' ); 2551 2552 if ( 0 === wrapper.find( '.fl-menu-mobile-flyout' ).length ) { 2553 menu.wrap( '<div class="fl-menu-mobile-flyout" aria-hidden="true"></div>' ); 2554 } 2555 2556 if ( 0 === wrapper.find( '.fl-menu-mobile-close' ).length ) { 2557 var close = window.fl_responsive_close || 'Close' 2558 wrapper.find( '.fl-menu-mobile-flyout' ).prepend( '<button class="fl-menu-mobile-close fl-content-ui-button" aria-label="' + close + '"><i class="fas fa-times"></i></button>' ); 2559 } 2560 2561 // Push with opacity 2562 if ( wrapper.hasClass( 'fl-menu-responsive-flyout-push-opacity' ) && 0 === wrapper.find( '.fl-menu-mobile-opacity' ).length ) { 2563 wrapper.append( '<div class="fl-menu-mobile-opacity"></div>' ); 2564 } 2565 2566 wrapper.off( 'click', '.fl-menu-mobile-opacity, .fl-menu-mobile-close' ).on( 'click', '.fl-menu-mobile-opacity, .fl-menu-mobile-close', function(e){ 2567 button.trigger('focus').trigger( 'click' ); 2568 e.stopPropagation(); 2569 }); 2570 2571 if ( 'undefined' !== typeof FLBuilder ) { 2572 FLBuilder.addHook('restartEditingSession', function(){ 2573 $( '.fl-builder-ui-pinned-content-transform' ).css( 'transform', '' ); 2574 2575 // Toggle active menu 2576 if ( button.hasClass( 'fl-active' ) ) { 2577 button.trigger( 'click' ); 2578 } 2579 }); 2580 } 2581 $( '.fl-menu-mobile-flyout' ).find( 'a[href], button, input, select, textarea, span.fl-menu-toggle, [tabindex]:not([tabindex="-1"])' ).attr( 'tabindex', -1 ); 2582 }, 2583 2584 /** 2585 * Logic to enable/disable the Flyout menu on button click. 2586 * 2587 * @since 2.2 2588 * @return void 2589 */ 2590 _toggleFlyoutMenu: function(){ 2591 var wrapper = $( this.wrapperClass ), 2592 button = wrapper.find( '.fl-menu-mobile-toggle' ), 2593 position = wrapper.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 2594 pushMenu = wrapper.hasClass( 'fl-menu-responsive-flyout-push' ) || wrapper.hasClass( 'fl-menu-responsive-flyout-push-opacity' ), 2595 opacity = wrapper.find( '.fl-menu-mobile-opacity' ), 2596 marginPos = {}, 2597 fixedPos = {}, 2598 fixedHeader = $('header, header > div'); 2599 2600 this._resizeFlyoutMenuPanel(); 2601 2602 // Fix the push menu when builder ui panel is pinned 2603 if ( $( '.fl-builder-ui-pinned-content-transform' ).length > 0 && ! $( 'body' ).hasClass( 'fl-builder-edit' ) ) { 2604 $( '.fl-builder-ui-pinned-content-transform' ).css( 'transform', 'none' ); 2605 } 2606 2607 if ( pushMenu ) { 2608 marginPos[ 'margin-' + position ] = button.hasClass( 'fl-active' ) ? this.flyoutWidth + 'px' : '0px'; 2609 $( 'body' ).animate( marginPos, 200); 2610 2611 // Fixed header 2612 if ( fixedHeader.length > 0 ) { 2613 fixedPos[ position] = button.hasClass( 'fl-active' ) ? this.flyoutWidth + 'px' : '0px'; 2614 fixedHeader.each(function(){ 2615 if ( 'fixed' == $( this ).css( 'position' ) ) { 2616 $( this ).css({ 'transition' : 'none' }); 2617 $( this ).animate( fixedPos, 200 ); 2618 } 2619 }); 2620 } 2621 } 2622 2623 if ( opacity.length > 0 && button.hasClass( 'fl-active' ) ) { 2624 opacity.show(); 2625 } 2626 else { 2627 opacity.hide(); 2628 } 2629 }, 2630 2631 /** 2632 * Resize or reposition the Flyout Menu Panel. 2633 * 2634 * @since 2.8.1 2635 * @return void 2636 */ 2637 _resizeFlyoutMenuPanel: function(){ 2638 const wrapper = $( this.wrapperClass ); 2639 const wrapFlyout = wrapper.find( '.fl-menu-mobile-flyout' ); 2640 2641 if ( wrapFlyout.length > 0 ) { 2642 wrapFlyout.css( this._getFlyoutMenuPanelPosition() ); 2643 } 2644 }, 2645 2646 /** 2647 * Compute the Flyout Menu Panel's position on the screen. 2648 * 2649 * @since 2.8.1 2650 * @return object 2651 */ 2652 _getFlyoutMenuPanelPosition: function() { 2653 var wrapper = $( this.wrapperClass ), 2654 button = wrapper.find( '.fl-menu-mobile-toggle' ), 2655 side = wrapper.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 2656 winHeight = $(window).outerHeight(), 2657 winTop = $(window).scrollTop(), 2658 adminBarHeight = $( '#wpadminbar' ).length ? $( '#wpadminbar' ).height() : 0, 2659 flyoutPosition = {}; 2660 2661 flyoutPosition[ side ] = '-' + ( parseInt( this.flyoutWidth ) + 15 ) + 'px'; 2662 if ( ! button.hasClass( 'fl-active' ) ) { 2663 return flyoutPosition; 2664 } 2665 2666 flyoutPosition[ side ] = '0px'; 2667 flyoutPosition[ 'height' ] = winHeight + 'px'; 2668 flyoutPosition[ 'top' ] = '0px'; 2669 2670 if ( adminBarHeight > 0 ) { 2671 const diff = adminBarHeight - winTop; 2672 flyoutPosition[ 'top' ] = diff <= 0 ? '0px' : (diff) + 'px'; 2673 } 2674 2675 return flyoutPosition; 2676 }, 2677 2678 /** 2679 * Shows or hides the nav search form. 2680 * 2681 * @since 2.5 2682 * @method _toggleMenuSearch 2683 */ 2684 _toggleMenuSearch: function(){ 2685 const form = $( this.wrapperClass ).find('.fl-menu-search-item .fl-search-form-input-wrap');
2686 const button = $( this.wrapperClass ).find('.fl-menu-search-item .fl-button:is(a, button:not([type="submit"]))'); 2687 form.on('submit', () => form.stop().fadeToggle('fast')); 2688 // Close the search form when focus is lost 2689 form.on('focusout', (event) => { 2690 if (form.is(':visible') && form.has($(event.relatedTarget)).length === 0) { 2691 button.trigger('click'); 2692 } 2693 }); 2694 button.attr('aria-expanded', false); 2695 // Close other open submenus when focusing on the search button 2696 button.on('focus', (event) => { 2697 const parentMenus = $(event.relatedTarget).parents('.menu-item.fl-has-submenu'); 2698 if (event.relatedTarget && parentMenus.length) { 2699 this._toggleSubmenu(parentMenus, false); 2700 } 2701 }); 2702 button.on('click', (event) => { 2703 // Prevent toggling when clicking with mouse while the form is already open 2704 if (event.detail && event.type === 'click' && form.is(':visible')) return; 2705 form.stop().fadeToggle('fast', () => { 2706 button.attr('aria-expanded', form.is(':visible')); 2707 if(form.is(':visible')) { 2708 form.find('.fl-search-text').focus(); 2709 } 2710 }); 2711 }); 2712 }, 2713 2714 /** 2715 * Adds menu node and post ID to WooCommerce ajax URL requests. 2716 * 2717 * @since 2.5 2718 * @return void 2719 */ 2720 _wooUpdateParams: function() { 2721 if ( 'undefined' !== typeof wc_cart_fragments_params ) { 2722 wc_cart_fragments_params.wc_ajax_url += '&fl-menu-node='+ this.nodeId +'&post-id='+ this.postId; 2723 } 2724 if ( 'undefined' !== typeof wc_add_to_cart_params ) { 2725 wc_add_to_cart_params.wc_ajax_url += '&fl-menu-node='+ this.nodeId +'&post-id='+ this.postId; 2726 } 2727 }, 2728 }; 2729 2730})(jQuery); 2731 2732(function($) { 2733 2734 $(function() { 2735 2736 new FLBuilderMenu({ 2737 id: '5b4e4504e6e84', 2738 type: 'horizontal', 2739 mobile: 'expanded', 2740 mobileBelowRow: false, 2741 mobileFlyout: false, 2742 breakPoints: { 2743 large: 1200, 2744 medium: 992, 2745 small: 768 }, 2746 mobileBreakpoint: 'mobile', 2747 postId : '793', 2748 mobileStacked: true, 2749 submenuIcon: 'none', 2750 flyoutWidth: '250', 2751 }); 2752 2753 }); 2754 2755})(jQuery); 2756 2757/* Start Layout Custom JS */ 2758 2759/* End Layout Custom JS */ 2760 2761 2762jQuery(function($) { 2763 2764 $(function() { 2765 $( '.fl-node-5da892bbc0b4e .fl-photo-img' ) 2766 .on( 'mouseenter', function( e ) { 2767 $( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' ); 2768 } ) 2769 .on( 'mouseleave', function( e ){ 2770 $( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null ); 2771 } ); 2772 }); 2773 window._fl_string_to_slug_regex = 'a-zA-Z0-9'; 2774}); 2775 2776(function($) { 2777 2778 $(function() { 2779 2780 new FLBuilderMenu({ 2781 id: '5da892bbc0b4a', 2782 type: 'horizontal', 2783 mobile: 'hamburger', 2784 mobileBelowRow: true, 2785 mobileFlyout: false, 2786 breakPoints: { 2787 large: 1200, 2788 medium: 992, 2789 small: 768 }, 2790 mobileBreakpoint: 'medium-mobile', 2791 postId : '15756', 2792 mobileStacked: true, 2793 submenuIcon: 'none', 2794 flyoutWidth: '250', 2795 }); 2796 2797 }); 2798 2799})(jQuery); 2800 2801/* Start Layout Custom JS */ 2802 2803/* End Layout Custom JS */ 2804 2805(function($){ 2806 2807 /** 2808 * Helper class for header layout logic. 2809 * 2810 * @since 1.0 2811 * @class FLThemeBuilderHeaderLayout 2812 */ 2813 FLThemeBuilderHeaderLayout = { 2814 2815 /** 2816 * A reference to the window object for this page. 2817 * 2818 * @since 1.0 2819 * @property {Object} win 2820 */ 2821 win : null, 2822 2823 /** 2824 * A reference to the body object for this page. 2825 * 2826 * @since 1.0 2827 * @property {Object} body 2828 */ 2829 body : null, 2830 2831 /** 2832 * A reference to the header object for this page. 2833 * 2834 * @since 1.0 2835 * @property {Object} header 2836 */ 2837 header : null, 2838 2839 /** 2840 * Whether this header overlays the content or not. 2841 * 2842 * @since 1.0 2843 * @property {Boolean} overlay 2844 */ 2845 overlay : false, 2846 2847 /** 2848 * Whether the page has the WP admin bar or not. 2849 * 2850 * @since 1.0 2851 * @property {Boolean} hasAdminBar 2852 */ 2853 hasAdminBar : false, 2854 2855 /** 2856 * Breakpoint for when the sticky header should apply. 2857 * 2858 * @since 1.4 2859 * @property {String} stickyOn 2860 */ 2861 stickyOn: '', 2862 2863 /** 2864 * A reference of the sticky and shrink header breakpoint. 2865 * 2866 * @since 1.2.5 2867 * @property {Number} breakpointWidth 2868 */ 2869 breakpointWidth: 0, 2870 2871 /** 2872 * Initializes header layout logic. 2873 * 2874 * @since 1.0 2875 * @method init 2876 */ 2877 init: function() 2878 {
2879 var editing = $( 'html.fl-builder-edit' ).length, 2880 header = $( '.fl-builder-content[data-type=header]' ), 2881 menuModule = header.find( '.fl-module-menu' ), 2882 breakpoint = null; 2883 2884 if ( ! editing && header.length ) { 2885 2886 header.imagesLoaded( $.proxy( function() { 2887 2888 this.win = $( window ); 2889 this.body = $( 'body' ); 2890 this.header = header.eq( 0 ); 2891 this.overlay = !! Number( header.attr( 'data-overlay' ) ); 2892 this.hasAdminBar = !! $( 'body.admin-bar' ).length; 2893 this.stickyOn = this.header.data( 'sticky-on' ); 2894 breakpoint = this.header.data( 'sticky-breakpoint' ); 2895 2896 if ( '' == this.stickyOn ) { 2897 if ( typeof FLBuilderLayoutConfig.breakpoints[ breakpoint ] !== undefined ) { 2898 this.breakpointWidth = FLBuilderLayoutConfig.breakpoints[ breakpoint ]; 2899 } 2900 else { 2901 this.breakpointWidth = FLBuilderLayoutConfig.breakpoints.medium; 2902 } 2903 } 2904 2905 if ( Number( header.attr( 'data-sticky' ) ) ) { 2906 2907 this.header.data( 'original-top', this.header.offset().top ); 2908 this.win.on( 'resize', $.throttle( 500, $.proxy( this._initSticky, this ) ) ); 2909 this._initSticky(); 2910 2911 } 2912 2913 }, this ) ); 2914 } 2915 }, 2916 2917 /** 2918 * Initializes sticky logic for a header. 2919 * 2920 * @since 1.0 2921 * @access private 2922 * @method _initSticky 2923 */ 2924 _initSticky: function( e ) 2925 { 2926 var header = $('.fl-builder-content[data-type=header]'), 2927 windowSize = this.win.width(), 2928 makeSticky = false; 2929 2930 makeSticky = this._makeWindowSticky( windowSize ); 2931 if ( makeSticky || ( this.breakpointWidth > 0 && windowSize >= this.breakpointWidth ) ) { 2932 this.win.on( 'scroll.fl-theme-builder-header-sticky', $.proxy( this._doSticky, this ) ); 2933 // 2934 // Check if Event Type is 'resize' then invoke this._doSticky() 2935 // only if the 'fl-theme-builder-header-sticky' class is already present. 2936 // 2937 if ( e && 'resize' === e.type ) { 2938 if ( this.header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 2939 this._doSticky( e ); 2940 } 2941 this._adjustStickyHeaderWidth(); 2942 } 2943 2944 if ( Number( header.attr( 'data-shrink' ) ) ) { 2945 this.header.data( 'original-height', this.header.outerHeight() ); 2946 this.win.on( 'resize', $.throttle( 500, $.proxy( this._initShrink, this ) ) ); 2947 this._initShrink(); 2948 } 2949 2950 this._initFlyoutMenuFix( e ); 2951 } else { 2952 this.win.off( 'scroll.fl-theme-builder-header-sticky' ); 2953 this.win.off( 'resize.fl-theme-builder-header-sticky' ); 2954 2955 this.header.removeClass( 'fl-theme-builder-header-sticky' ); 2956 this.header.removeAttr( 'style' ); 2957 this.header.parent().css( 'padding-top', '0' ); 2958 } 2959 }, 2960 2961 /** 2962 * Check if Header should be sticky at a particular Window size. 2963 * 2964 * @since 1.4 2965 * @access private 2966 * @param widowSize 2967 * @method _makeWindowSticky 2968 */ 2969 _makeWindowSticky: function ( windowSize ) 2970 { 2971 var makeSticky = false; 2972 2973 switch (this.stickyOn) { 2974 case 'xl': 2975 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['large']; 2976 break; 2977 case '': // Default 2978 case 'desktop': 2979 makeSticky = windowSize >= FLBuilderLayoutConfig.breakpoints['medium']; 2980 break; 2981 case 'desktop-medium': 2982 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['small']; 2983 break; 2984 case 'large': 2985 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['medium'] && windowSize <= FLBuilderLayoutConfig.breakpoints['large']; 2986 break; 2987 case 'large-medium': 2988 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['small'] && windowSize <= FLBuilderLayoutConfig.breakpoints['large']; 2989 break; 2990 case 'medium': 2991 makeSticky = ( windowSize <= FLBuilderLayoutConfig.breakpoints['medium'] && windowSize > FLBuilderLayoutConfig.breakpoints['small'] ); 2992 break; 2993 case 'medium-mobile': 2994 makeSticky = (windowSize <= FLBuilderLayoutConfig.breakpoints['medium']); 2995 break; 2996 case 'mobile': 2997 makeSticky = (windowSize <= FLBuilderLayoutConfig.breakpoints['small']); 2998 break; 2999 case 'all': 3000 makeSticky = true; 3001 break; 3002 } 3003 3004 return makeSticky; 3005 }, 3006 3007 /** 3008 * Sticks the header when the page is scrolled. 3009 * 3010 * @since 1.0 3011 * @access private 3012 * @method _doSticky 3013 */ 3014 _doSticky: function( e ) 3015 { 3016 var winTop = Math.floor( this.win.scrollTop() ), 3017 headerTop = Math.floor( this.header.data( 'original-top' ) ),
3018 hasStickyClass = this.header.hasClass( 'fl-theme-builder-header-sticky' ), 3019 hasScrolledClass = this.header.hasClass( 'fl-theme-builder-header-scrolled' ), 3020 beforeHeader = this.header.prevAll( '.fl-builder-content' ), 3021 bodyTopPadding = parseInt( jQuery('body').css('padding-top') ), 3022 winBarHeight = $('#wpadminbar').length ? $('#wpadminbar').outerHeight() : 0, 3023 headerHeight = 0; 3024 3025 if ( isNaN( bodyTopPadding ) ) { 3026 bodyTopPadding = 0; 3027 } 3028 3029 if ( this.hasAdminBar && this.win.width() > 600 ) { 3030 winTop += Math.floor( winBarHeight ); 3031 } 3032 3033 if ( winTop > headerTop ) { 3034 if ( ! hasStickyClass ) { 3035 if ( e && ( 'scroll' === e.type || 'smartscroll' === e.type ) ) { 3036 this.header.addClass( 'fl-theme-builder-header-sticky' ); 3037 if ( this.overlay && beforeHeader.length ) { 3038 this.header.css( 'top', winBarHeight); 3039 } 3040 } 3041 3042 if ( ! this.overlay ) { 3043 this._adjustHeaderHeight(); 3044 } 3045 } 3046 } 3047 else if ( hasStickyClass ) { 3048 this.header.removeClass( 'fl-theme-builder-header-sticky' ); 3049 this.header.removeAttr( 'style' ); 3050 this.header.parent().css( 'padding-top', '0' ); 3051 } 3052 3053 this._adjustStickyHeaderWidth(); 3054 3055 if ( winTop > headerTop ) { 3056 if ( ! hasScrolledClass ) { 3057 this.header.addClass( 'fl-theme-builder-header-scrolled' ); 3058 } 3059 } else if ( hasScrolledClass ) { 3060 this.header.removeClass( 'fl-theme-builder-header-scrolled' ); 3061 } 3062 3063 this._flyoutMenuFix( e ); 3064 }, 3065 3066 /** 3067 * Initializes flyout menu fixes on sticky header. 3068 * 3069 * @since 1.4.1 3070 * @method _initFlyoutMenuFix 3071 */ 3072 _initFlyoutMenuFix: function( e ) { 3073 var header = this.header, 3074 menuModule = header.closest( '.fl-menu' ), 3075 flyoutMenu = menuModule.find( '.fl-menu-mobile-flyout' ), 3076 isPushMenu = menuModule.hasClass( 'fl-menu-responsive-flyout-push' ) || menuModule.hasClass( 'fl-menu-responsive-flyout-push-opacity' ), 3077 isOverlay = menuModule.hasClass( 'fl-menu-responsive-flyout-overlay' ), 3078 flyoutPos = menuModule.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 3079 flyoutParent = header.parent().is( 'header' ) ? header.parent().parent() : header.parent(); 3080 isFullWidth = this.win.width() === header.width(), 3081 flyoutLayout = '', 3082 activePos = 250, 3083 headerPos = 0; 3084 3085 if ( ! flyoutMenu.length ) { 3086 return; 3087 } 3088 3089 if ( this.win.width() > header.parent().width() ) { 3090 headerPos = ( this.win.width() - header.width() ) / 2; 3091 } 3092 3093 if ( isOverlay ) { 3094 activePos = headerPos; 3095 } 3096 else if ( isPushMenu ) { 3097 activePos = activePos + headerPos; 3098 } 3099 flyoutMenu.data( 'activePos', activePos ); 3100 3101 if ( isPushMenu ) { 3102 flyoutLayout = 'push-' + flyoutPos; 3103 } 3104 else if ( isOverlay ) { 3105 flyoutLayout = 'overlay-' + flyoutPos; 3106 } 3107 3108 if ( isPushMenu && ! $( 'html' ).hasClass( 'fl-theme-builder-has-flyout-menu' ) ) { 3109 $( 'html' ).addClass( 'fl-theme-builder-has-flyout-menu' ); 3110 } 3111 3112 if ( ! flyoutParent.hasClass( 'fl-theme-builder-flyout-menu-' + flyoutLayout ) ) { 3113 flyoutParent.addClass( 'fl-theme-builder-flyout-menu-' + flyoutLayout ); 3114 } 3115 3116 if ( ! header.hasClass( 'fl-theme-builder-flyout-menu-overlay' ) && isOverlay ) { 3117 header.addClass( 'fl-theme-builder-flyout-menu-overlay' ); 3118 } 3119 3120 if ( ! header.hasClass( 'fl-theme-builder-header-full-width' ) && isFullWidth ) { 3121 header.addClass( 'fl-theme-builder-header-full-width' ); 3122 } 3123 else if ( ! isFullWidth ) { 3124 header.removeClass( 'fl-theme-builder-header-full-width' ); 3125 } 3126 3127 menuModule.on( 'click', '.fl-menu-mobile-toggle', $.proxy( function( event ){ 3128 if ( menuModule.find( '.fl-menu-mobile-toggle.fl-active' ).length ) { 3129 $( 'html' ).addClass( 'fl-theme-builder-flyout-menu-active' ); 3130 event.stopImmediatePropagation(); 3131 } 3132 else { 3133 $( 'html' ).removeClass( 'fl-theme-builder-flyout-menu-active' ); 3134 } 3135 3136 this._flyoutMenuFix( event ); 3137 }, this ) ); 3138 }, 3139 3140 /** 3141 * Fix flyout menu inside the sticky header. 3142 * 3143 * @since 1.4.1 3144 * @method _flyoutMenuFix 3145 */ 3146 _flyoutMenuFix: function( e ){ 3147 var header = this.header, 3148 menuModule = $( e.target ).closest( '.fl-menu' ), 3149 flyoutMenu = menuModule.find( '.fl-menu-mobile-flyout' ), 3150 flyoutPos = menuModule.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 3151 menuOpacity = menuModule.find( '.fl-menu-mobile-opacity' ), 3152 isScroll = 'undefined' !== typeof e && 'scroll' === e.handleObj.type, 3153 activePos = 'undefined' !== typeof flyoutMenu.data( 'activePos' ) ? flyoutMenu.data( 'activePos' ) : 0, 3154 headerPos = ( this.win.width() - header.width() ) / 2, 3155 inactivePos = headerPos > 0 ? activePos + 4 : 254; 3156 3157 if ( ! flyoutMenu.length ) { 3158 return; 3159 } 3160 3161 if ( this.overlay ) { 3162 return; 3163 } 3164
3165 if( $( '.fl-theme-builder-flyout-menu-active' ).length ) { 3166 3167 if ( isScroll && ! flyoutMenu.hasClass( 'fl-menu-disable-transition' ) ) { 3168 flyoutMenu.addClass( 'fl-menu-disable-transition' ); 3169 } 3170 3171 if ( header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 3172 if ( ! isScroll ) { 3173 setTimeout( $.proxy( function(){ 3174 flyoutMenu.css( flyoutPos, '-' + activePos + 'px' ); 3175 }, this ), 1 ); 3176 } 3177 else { 3178 flyoutMenu.css( flyoutPos, '-' + activePos + 'px' ); 3179 } 3180 } 3181 else { 3182 flyoutMenu.css( flyoutPos, '0px' ); 3183 } 3184 } 3185 else { 3186 if ( flyoutMenu.hasClass( 'fl-menu-disable-transition' ) ) { 3187 flyoutMenu.removeClass( 'fl-menu-disable-transition' ); 3188 } 3189 3190 if ( header.hasClass( 'fl-theme-builder-flyout-menu-overlay' ) && headerPos > 0 && headerPos < 250 ) { 3191 if ( header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 3192 inactivePos = headerPos + 254; 3193 } 3194 else { 3195 inactivePos = 254; 3196 } 3197 } 3198 3199 if ( e && e.type === 'resize' ) { 3200 inactivePos = headerPos + 254; 3201 } 3202 3203 flyoutMenu.css( flyoutPos, '-' + inactivePos + 'px' ); 3204 } 3205 3206 if ( e && menuModule.is('.fl-menu-responsive-flyout-overlay') && $.infinitescroll ) { 3207 e.stopImmediatePropagation(); 3208 } 3209 3210 if( menuOpacity.length ) { 3211 if ( header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 3212 if ( '0px' === menuOpacity.css( 'left' ) ) { 3213 menuOpacity.css( 'left', '-' + headerPos + 'px' ); 3214 } 3215 } 3216 else { 3217 menuOpacity.css( 'left', '' ); 3218 } 3219 } 3220 }, 3221 3222 /** 3223 * Adjust sticky header width if BB Theme Boxed Layout is used. 3224 * 3225 * @since 1.4 3226 * @access private 3227 * @method _adjustStickyHeaderWidth 3228 */ 3229 _adjustStickyHeaderWidth: function () { 3230 if ( $('body').hasClass( 'fl-fixed-width' ) ) { 3231 var parentWidth = this.header.parent().width(); 3232 3233 // Better if this is set in the stylesheet file. 3234 if ( this.win.width() >= 992 ) { 3235 this.header.css({ 3236 'margin': '0 auto', 3237 'max-width': parentWidth, 3238 }); 3239 } 3240 else { 3241 this.header.css({ 3242 'margin': '', 3243 'max-width': '', 3244 }); 3245 } 3246 } 3247 }, 3248 3249 /** 3250 * Adjust Sticky Header Height 3251 * 3252 * @since 1.4 3253 * @access private 3254 * @method _adjustHeaderHeight 3255 */ 3256 _adjustHeaderHeight: function () { 3257 var beforeHeader = this.header.prevAll('.fl-builder-content'), 3258 beforeHeaderHeight = 0, 3259 beforeHeaderFix = 0, 3260 headerHeight = Math.floor( this.header.outerHeight() ), 3261 bodyTopPadding = parseInt( $( 'body' ).css( 'padding-top' ) ), 3262 wpAdminBarHeight = 0, 3263 totalHeaderHeight = 0; 3264 3265 if ( isNaN( bodyTopPadding ) ) { 3266 bodyTopPadding = 0; 3267 } 3268 3269 if ( beforeHeader.length ) { 3270 $.each( beforeHeader, function() { 3271 beforeHeaderHeight += Math.floor( $(this).outerHeight() ); 3272 }); 3273 // Subtract this value from the header parent's top padding. 3274 beforeHeaderFix = 2; 3275 } 3276 3277 if ( this.hasAdminBar && this.win.width() <= 600 ) { 3278 wpAdminBarHeight = Math.floor( $('#wpadminbar').outerHeight() ); 3279 } 3280 3281 totalHeaderHeight = Math.floor( beforeHeaderHeight + headerHeight); 3282 3283 if ( headerHeight > 0 ) { 3284 var headerParent = this.header.parent(), 3285 headerParentTopPadding = 0; 3286 3287 // If the header's parent container is the BODY tag ignore its top padding. 3288 if ( $( headerParent ).is('body') ) { 3289 headerParentTopPadding = Math.floor( headerHeight - wpAdminBarHeight ); 3290 } else { 3291 headerParentTopPadding = Math.floor( headerHeight - bodyTopPadding - wpAdminBarHeight ); 3292 } 3293 3294 $( headerParent ).css( 'padding-top', ( headerParentTopPadding - beforeHeaderFix ) + 'px' ); 3295 3296 this.header.css({ 3297 '-webkit-transform': 'translate(0px, -' + totalHeaderHeight + 'px)', 3298 '-ms-transform': 'translate(0px, -' + totalHeaderHeight + 'px)', 3299 'transform': 'translate(0px, -' + totalHeaderHeight + 'px)' 3300 }); 3301 3302 } 3303 3304 }, 3305 3306 /** 3307 * Initializes shrink logic for a header. 3308 * 3309 * @since 1.0 3310 * @access private 3311 * @method _initShrink 3312 */ 3313 _initShrink: function( e ) 3314 { 3315 if ( this.win.width() >= this.breakpointWidth ) {
3316 this.win.on( 'scroll.fl-theme-builder-header-shrink', $.proxy( this._doShrink, this ) ); 3317 this._setImageMaxHeight(); 3318 3319 if ( this.win.scrollTop() > 0 ){ 3320 this._doShrink(); 3321 } 3322 3323 } else { 3324 this.header.parent().css( 'padding-top', '0' ); 3325 this.win.off( 'scroll.fl-theme-builder-header-shrink' ); 3326 this._removeShrink(); 3327 this._removeImageMaxHeight(); 3328 } 3329 }, 3330 3331 /** 3332 * Shrinks the header when the page is scrolled. 3333 * 3334 * @since 1.0 3335 * @access private 3336 * @method _doShrink 3337 */ 3338 _doShrink: function( e ) 3339 { 3340 var winTop = this.win.scrollTop(), 3341 headerTop = this.header.data('original-top'), 3342 headerHeight = this.header.data('original-height'), 3343 shrinkImageHeight = this.header.data('shrink-image-height'), 3344 windowSize = this.win.width(), 3345 makeSticky = this._makeWindowSticky( windowSize ), 3346 hasClass = this.header.hasClass( 'fl-theme-builder-header-shrink' ); 3347 3348 3349 if ( this.hasAdminBar ) { 3350 winTop += 32; 3351 } 3352 3353 if ( makeSticky && ( winTop > headerTop + headerHeight ) ) { 3354 if ( ! hasClass ) { 3355 3356 this.header.addClass( 'fl-theme-builder-header-shrink' ); 3357 3358 // Shrink images but don't include lightbox and menu images. 3359 this.header.find('img').each( function( i ) { 3360 var image = $( this ), 3361 maxMegaMenu = image.closest( '.max-mega-menu' ).length, 3362 imageInLightbox = image.closest( '.fl-button-lightbox-content' ).length, 3363 imageInNavMenu = image.closest( 'li.menu-item' ).length; 3364 3365 if ( ! ( imageInLightbox || imageInNavMenu || maxMegaMenu ) ) { 3366 image.css( 'max-height', shrinkImageHeight ); 3367 } 3368 3369 }); 3370 3371 this.header.find( '.fl-row-content-wrap' ).each( function() { 3372 3373 var row = $( this ); 3374 3375 if ( parseInt( row.css( 'padding-bottom' ) ) > 5 ) { 3376 row.addClass( 'fl-theme-builder-header-shrink-row-bottom' ); 3377 } 3378 3379 if ( parseInt( row.css( 'padding-top' ) ) > 5 ) { 3380 row.addClass( 'fl-theme-builder-header-shrink-row-top' ); 3381 } 3382 } ); 3383 3384 this.header.find( '.fl-module' ).each( function() { 3385 3386 var module = $( this ).find( '.fl-module-content' ).length ? $( this ).find( '.fl-module-content' ) : $( this ); 3387 3388 if ( parseInt( module.css( 'margin-bottom' ) ) > 5 ) { 3389 module.addClass( 'fl-theme-builder-header-shrink-module-bottom' ); 3390 } 3391 3392 if ( parseInt( module.css( 'margin-top' ) ) > 5 ) { 3393 module.addClass( 'fl-theme-builder-header-shrink-module-top' ); 3394 } 3395 } ); 3396 } 3397 } else if (hasClass) { 3398 this.header.find( 'img' ).css( 'max-height', '' ); 3399 this._removeShrink(); 3400 } 3401 3402 // Fixes Shrink header issue with BB Theme when window is scrolled then resized and back. 3403 if ( 'undefined' === typeof( e ) && $('body').hasClass( 'fl-fixed-width' ) ) { 3404 if ( ! this.overlay ) { 3405 this._adjustHeaderHeight(); 3406 } 3407 } 3408 3409 }, 3410 3411 /** 3412 * Removes the header shrink effect. 3413 * 3414 * @since 1.0 3415 * @access private 3416 * @method _removeShrink 3417 */ 3418 _removeShrink: function() 3419 { 3420 var rows = this.header.find( '.fl-row-content-wrap' ), 3421 modules = this.header.find('.fl-module, .fl-module-content'); 3422 3423 rows.removeClass( 'fl-theme-builder-header-shrink-row-bottom' ); 3424 rows.removeClass( 'fl-theme-builder-header-shrink-row-top' ); 3425 modules.removeClass( 'fl-theme-builder-header-shrink-module-bottom' ); 3426 modules.removeClass( 'fl-theme-builder-header-shrink-module-top' ); 3427 this.header.removeClass( 'fl-theme-builder-header-shrink' ); 3428 }, 3429 3430 /** 3431 * Adds max height to images in modules for smooth scrolling. 3432 * 3433 * @since 1.1.1 3434 * @access private 3435 * @method _setImageMaxHeight 3436 */ 3437 _setImageMaxHeight: function() 3438 { 3439 var head = $( 'head' ), 3440 stylesId = 'fl-header-styles-' + this.header.data( 'post-id' ), 3441 styles = '', 3442 images = this.header.find( '.fl-module img' ); 3443 3444 if ( $( '#' + stylesId ).length ) { 3445 return; 3446 } 3447 3448 images.each( function( i ) { 3449 var image = $( this ), 3450 height = image.height(), 3451 node = image.closest( '.fl-module' ).data( 'node' ), 3452 className = 'fl-node-' + node + '-img-' + i, 3453 maxMegaMenu = image.closest( '.max-mega-menu' ).length, 3454 imageInLightbox = image.closest( '.fl-button-lightbox-content' ).length, 3455 imageInNavMenu = image.closest( 'li.menu-item' ).length; 3456 3457 if ( ! ( imageInLightbox || imageInNavMenu || maxMegaMenu ) ) { 3458 image.addClass( className ); 3459 styles += '.' + className + ' { max-height: ' + ( height ? height : image[0].height ) + 'px }'; 3460 } 3461 3462 } ); 3463
3464 if ( '' !== styles ) { 3465 head.append( '<style id="' + stylesId + '">' + styles + '</style>' ); 3466 } 3467 }, 3468 3469 /** 3470 * Removes max height on images in modules for smooth scrolling. 3471 * 3472 * @since 1.1.1 3473 * @access private 3474 * @method _removeImageMaxHeight 3475 */ 3476 _removeImageMaxHeight: function() 3477 { 3478 $( '#fl-header-styles-' + this.header.data( 'post-id' ) ).remove(); 3479 }, 3480 }; 3481 3482 $( function() { FLThemeBuilderHeaderLayout.init(); } ); 3483 3484})(jQuery); 3485
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.