1var wpAjaxUrl = 'https://theunstuckgroup.com/wp-admin/admin-ajax.php';var flBuilderUrl = 'https://theunstuckgroup.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://theunstuckgroup.com/wp-content/plugins/bb-plugin/', 9 wpAjaxUrl : 'https://theunstuckgroup.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 i18n : { 20 opensInNewTab : '(opens in new tab)' 21 } 22}; 23(function($){ 24 25 if(typeof FLBuilderLayout != 'undefined') { 26 return; 27 } 28 29 /** 30 * Helper class with generic logic for a builder layout. 31 * 32 * @class FLBuilderLayout 33 * @since 1.0 34 */ 35 FLBuilderLayout = { 36 37 /** 38 * Initializes a builder layout. 39 * 40 * @since 1.0 41 * @method init 42 */ 43 init: function() 44 { 45 // Destroy existing layout events. 46 FLBuilderLayout._destroy(); 47 48 // Init CSS classes. 49 FLBuilderLayout._initClasses(); 50 51 // Init backgrounds.
52 FLBuilderLayout._initBackgrounds(); 53 54 // Init buttons. 55 FLBuilderLayout._initButtons(); 56 57 // Init row shape layer height. 58 FLBuilderLayout._initRowShapeLayerHeight(); 59 60 // Init the new tab notice on links opening in a new tab. 61 FLBuilderLayout._initLinkNotices(); 62 63 // Only init if the builder isn't active. 64 if ( 0 === $('.fl-builder-edit').length ) { 65 66 // Init anchor links. 67 FLBuilderLayout._initAnchorLinks(); 68 69 // Init the browser hash. 70 FLBuilderLayout._initHash(); 71 72 // Init forms. 73 FLBuilderLayout._initForms(); 74 75 FLBuilderLayout._reorderMenu(); 76 } 77 else { 78 FLBuilderLayout._initNestedColsWidth(); 79 } 80 $('body').removeClass( 'fl-no-js' ); 81 }, 82 83 /** 84 * Public method for refreshing Wookmark or MosaicFlow galleries 85 * within an element. 86 * 87 * @since 1.7.4 88 * @method refreshGalleries 89 */ 90 refreshGalleries: function( element ) 91 { 92 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 93 mfContent = $element.find( '.fl-mosaicflow-content' ), 94 wmContent = $element.find( '.fl-gallery' ), 95 mfObject = null; 96 97 if ( mfContent ) { 98 99 mfObject = mfContent.data( 'mosaicflow' ); 100 101 if ( mfObject ) { 102 mfObject.columns = $( [] ); 103 mfObject.columnsHeights = []; 104 mfContent.data( 'mosaicflow', mfObject ); 105 mfContent.mosaicflow( 'refill' ); 106 } 107 } 108 if ( wmContent ) { 109 wmContent.trigger( 'refreshWookmark' ); 110 } 111 }, 112 113 /** 114 * Public method for refreshing Masonry within an element 115 * 116 * @since 1.8.1 117 * @method refreshGridLayout 118 */ 119 refreshGridLayout: function( element ) 120 { 121 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 122 msnryContent = $element.find('.masonry'); 123 124 if ( msnryContent.length ) { 125 msnryContent.masonry('layout'); 126 } 127 }, 128 129 /** 130 * Public method for reloading BxSlider within an element 131 * 132 * @since 1.8.1 133 * @method reloadSlider 134 */ 135 reloadSlider: function( content ) 136 { 137 var $content = 'undefined' == typeof content ? $('body') : $(content); 138 139 // reload sliders. 140 if ($content.find('.bx-viewport > div').length > 0) { 141 $.each($content.find('.bx-viewport > div'), function (key, slider) { 142 setTimeout(function () { 143 $(slider).data('bxSlider').reloadSlider(); 144 }, 100); 145 }); 146 } 147 }, 148 149 /** 150 * Public method for resizing WP audio player 151 * 152 * @since 1.8.2 153 * @method resizeAudio 154 */ 155 resizeAudio: function( element ) 156 { 157 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 158 audioPlayers = $element.find('.wp-audio-shortcode.mejs-audio'), 159 player = null, 160 mejsPlayer = null, 161 rail = null, 162 railWidth = 400; 163 164 if ( audioPlayers.length && typeof mejs !== 'undefined' ) { 165 audioPlayers.each(function(){ 166 player = $(this); 167 mejsPlayer = mejs.players[player.attr('id')]; 168 rail = player.find('.mejs-controls .mejs-time-rail'); 169 var innerMejs = player.find('.mejs-inner'), 170 total = player.find('.mejs-controls .mejs-time-total'); 171 172 if ( typeof mejsPlayer !== 'undefined' ) { 173 railWidth = Math.ceil(player.width() * 0.8); 174 175 if ( innerMejs.length ) { 176 177 rail.css('width', railWidth +'px!important'); 178 //total.width(rail.width() - 10); 179 180 mejsPlayer.options.autosizeProgress = true; 181 182 // webkit has trouble doing this without a delay 183 setTimeout(function () { 184 mejsPlayer.setControlsSize(); 185 }, 50); 186 187 player.find('.mejs-inner').css({ 188 visibility: 'visible', 189 height: 'inherit' 190 }); 191 } 192 } 193 }); 194 } 195 }, 196 197 /** 198 * Public method for preloading WP audio player when it's inside the hidden element 199 * 200 * @since 1.8.2 201 * @method preloadAudio 202 */ 203 preloadAudio: function(element) 204 { 205 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 206 contentWrap = $element.closest('.fl-accordion-item'), 207 audioPlayers = $element.find('.wp-audio-shortcode.mejs-audio'); 208 209 if ( ! contentWrap.hasClass('fl-accordion-item-active') && audioPlayers.find('.mejs-inner').length ) { 210 audioPlayers.find('.mejs-inner').css({ 211 visibility : 'hidden', 212 height: 0 213 }); 214 } 215 }, 216 217 /** 218 * Public method for resizing slideshow momdule within the tab 219 * 220 * @since 1.10.5 221 * @method resizeSlideshow 222 */ 223 resizeSlideshow: function(){ 224 if(typeof YUI !== 'undefined') { 225 YUI().use('node-event-simulate', function(Y) { 226 Y.one(window).simulate("resize"); 227 }); 228 } 229 }, 230 231 /** 232 * Public method for reloading an embedded Google Map within the tabs or hidden element. 233 * 234 * @since 2.2 235 * @method reloadGoogleMap 236 */ 237 reloadGoogleMap: function(element){ 238 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ),
239 googleMap = $element.find( 'iframe[src*="google.com/maps"]' ); 240 241 if ( googleMap.length ) { 242 googleMap.attr( 'src', function(i, val) { 243 return val; 244 }); 245 } 246 }, 247 248 /** 249 * Unbinds builder layout events. 250 * 251 * @since 1.0 252 * @access private 253 * @method _destroy 254 */ 255 _destroy: function() 256 { 257 var win = $(window); 258 259 win.off('scroll.fl-bg-parallax'); 260 win.off('resize.fl-bg-video'); 261 }, 262 263 /** 264 * Checks to see if the current device has touch enabled. 265 * 266 * @since 1.0 267 * @access private 268 * @method _isTouch 269 * @return {Boolean} 270 */ 271 _isTouch: function() 272 { 273 if(('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch)) { 274 return true; 275 } 276 277 return false; 278 }, 279 280 /** 281 * Checks to see if the current device is mobile. 282 * 283 * @since 1.7 284 * @access private 285 * @method _isMobile 286 * @return {Boolean} 287 */ 288 _isMobile: function() 289 { 290 return /Mobile|Android|Silk\/|Kindle|BlackBerry|Opera Mini|Opera Mobi|webOS/i.test( navigator.userAgent ); 291 }, 292 293 /** 294 * Initializes builder body classes. 295 * 296 * @since 1.0 297 * @access private 298 * @method _initClasses 299 */ 300 _initClasses: function() 301 { 302 var body = $( 'body' ), 303 ua = navigator.userAgent; 304 305 // Add the builder body class (skip on block-only frontend to avoid layout break). 306 if ( ! body.hasClass( 'fl-builder-blocks-only' ) && ! body.hasClass( 'archive' ) && $( '.fl-builder-content-primary' ).length > 0 ) { 307 body.addClass('fl-builder'); 308 } 309 310 // Add the builder touch body class. 311 if(FLBuilderLayout._isTouch()) { 312 body.addClass('fl-builder-touch'); 313 } 314 315 // Add the builder mobile body class. 316 if(FLBuilderLayout._isMobile()) { 317 body.addClass('fl-builder-mobile'); 318 } 319 320 if ( $(window).width() < FLBuilderLayoutConfig.breakpoints.small ) { 321 body.addClass( 'fl-builder-breakpoint-small' ); 322 } 323 324 if ( $(window).width() > FLBuilderLayoutConfig.breakpoints.small && $(window).width() < FLBuilderLayoutConfig.breakpoints.medium ) { 325 body.addClass( 'fl-builder-breakpoint-medium' ); 326 } 327 328 if ( $(window).width() > FLBuilderLayoutConfig.breakpoints.medium && $(window).width() < FLBuilderLayoutConfig.breakpoints.large ) { 329 body.addClass( 'fl-builder-breakpoint-large' ); 330 } 331 332 if ( $(window).width() > FLBuilderLayoutConfig.breakpoints.large ) { 333 body.addClass( 'fl-builder-breakpoint-default' ); 334 } 335 336 // IE11 body class. 337 if ( ua.indexOf( 'Trident/7.0' ) > -1 && ua.indexOf( 'rv:11.0' ) > -1 ) { 338 body.addClass( 'fl-builder-ie-11' ); 339 } 340 }, 341 342 /** 343 * Initializes builder node backgrounds that require 344 * additional JavaScript logic such as parallax. 345 * 346 * @since 1.1.4 347 * @access private
348 * @method _initBackgrounds 349 */ 350 _initBackgrounds: function() 351 { 352 var win = $(window); 353 354 // Init parallax backgrounds. 355 if($('.fl-row-bg-parallax').length > 0 && !FLBuilderLayout._isMobile()) { 356 FLBuilderLayout._scrollParallaxBackgrounds(); 357 FLBuilderLayout._initParallaxBackgrounds(); 358 win.on('resize.fl-bg-parallax', FLBuilderLayout._initParallaxBackgrounds); 359 win.on('scroll.fl-bg-parallax', FLBuilderLayout._scrollParallaxBackgrounds); 360 } 361 362 // Init video backgrounds. 363 if($('.fl-bg-video').length > 0) { 364 FLBuilderLayout._initBgVideos(); 365 FLBuilderLayout._resizeBgVideos(); 366 367 // Ensure FLBuilderLayout._resizeBgVideos() is only called once on window resize. 368 var resizeBGTimer = null; 369 win.on('resize.fl-bg-video', function(e){ 370 clearTimeout( resizeBGTimer ); 371 resizeBGTimer = setTimeout(function() { 372 FLBuilderLayout._resizeBgVideos(e); 373 }, 100 ); 374 }); 375 } 376 }, 377 378 /** 379 * Initializes all buttons in a layout. 380 * 381 * @since 2.10 382 * @access private 383 * @method _initButtons 384 */ 385 _initButtons: function() 386 { 387 // Trigger click event on Enter or Space key press for deprecated button markup. 388 $('a.fl-button[role="button"]').on('keydown', function(event) { 389 if (event.key === 'Enter' || event.key === ' ') { 390 // Necessary to prevent the default behavior of the space key from scrolling the page 391 event.preventDefault(); 392 $(this).trigger('click'); 393 } 394 }); 395 }, 396 397 /** 398 * Initializes all parallax backgrounds in a layout. 399 * 400 * @since 1.1.4 401 * @access private 402 * @method _initParallaxBackgrounds 403 */ 404 _initParallaxBackgrounds: function() 405 { 406 $('.fl-row-bg-parallax').each(FLBuilderLayout._initParallaxBackground); 407 }, 408 409 /** 410 * Initializes a single parallax background. 411 * 412 * @since 1.1.4 413 * @access private 414 * @method _initParallaxBackgrounds 415 */ 416 _initParallaxBackground: function() 417 { 418 var row = $(this), 419 content = row.find('> .fl-row-content-wrap'), 420 winWidth = $(window).width(), 421 screenSize = '', 422 imageSrc = { 423 default: '', 424 medium: '', 425 responsive: '', 426 }; 427 428 imageSrc.default = row.data('parallax-image') || ''; 429 imageSrc.medium = row.data('parallax-image-medium') || imageSrc.default; 430 imageSrc.responsive = row.data('parallax-image-responsive') || imageSrc.medium; 431 432 if (winWidth > FLBuilderLayoutConfig.breakpoints.medium) { 433 screenSize = 'default'; 434 } else if (winWidth > FLBuilderLayoutConfig.breakpoints.small && winWidth <= FLBuilderLayoutConfig.breakpoints.medium ) { 435 screenSize = 'medium'; 436 } else if (winWidth <= FLBuilderLayoutConfig.breakpoints.small) { 437 screenSize = 'responsive'; 438 } 439 440 content.css('background-image', 'url(' + imageSrc[screenSize] + ')'); 441 row.data('current-image-loaded', screenSize ); 442 443 }, 444 445 /** 446 * Fires when the window is scrolled to adjust 447 * parallax backgrounds. 448 * 449 * @since 1.1.4 450 * @access private 451 * @method _scrollParallaxBackgrounds 452 */ 453 _scrollParallaxBackgrounds: function() 454 { 455 $('.fl-row-bg-parallax').each(FLBuilderLayout._scrollParallaxBackground); 456 }, 457 458 /** 459 * Fires when the window is scrolled to adjust 460 * a single parallax background. 461 * 462 * @since 1.1.4 463 * @access private 464 * @method _scrollParallaxBackground 465 */ 466 _scrollParallaxBackground: function() 467 { 468 var win = $(window), 469 row = $(this), 470 content = row.find('> .fl-row-content-wrap'), 471 speed = row.data('parallax-speed'), 472 offset = content.offset(), 473 yPos = -((win.scrollTop() - offset.top) / speed), 474 initialOffset = ( row.data('parallax-offset') != null ) ? row.data('parallax-offset') : 0, 475 totalOffset = yPos - initialOffset; 476 477 content.css('background-position', 'center ' + totalOffset + 'px'); 478 }, 479 480 /** 481 * Initializes all video backgrounds. 482 * 483 * @since 1.6.3.3 484 * @access private 485 * @method _initBgVideos 486 */ 487 _initBgVideos: function() 488 { 489 $('.fl-bg-video').each(FLBuilderLayout._initBgVideo); 490 }, 491 492 /** 493 * Initializes a video background. 494 * 495 * @since 1.6.3.3 496 * @access private 497 * @method _initBgVideo 498 */ 499 _initBgVideo: function() 500 { 501 var wrap = $( this ), 502 width = wrap.data( 'width' ), 503 height = wrap.data( 'height' ), 504 mp4 = wrap.data( 'mp4' ), 505 youtube = wrap.data( 'youtube'), 506 vimeo = wrap.data( 'vimeo'), 507 mp4Type = wrap.data( 'mp4-type' ), 508 webm = wrap.data( 'webm' ), 509 webmType = wrap.data( 'webm-type' ), 510 fallback = wrap.data( 'fallback' ), 511 loaded = wrap.data( 'loaded' ), 512 videoMobile = wrap.data( 'video-mobile' ), 513 playPauseButton = wrap.find('.fl-bg-video-play-pause-control'), 514 fallbackTag = '', 515 videoTag = null, 516 mp4Tag = null, 517 webmTag = null; 518 519 // Return if the video has been loaded for this row. 520 if ( loaded ) { 521 return; 522 } 523
524 videoTag = $( '<video autoplay loop muted playsinline></video>' ); 525 526 /** 527 * Add poster image (fallback image) 528 */ 529 if( 'undefined' != typeof fallback && '' != fallback ) { 530 videoTag.attr( 'poster', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' ) 531 videoTag.css({ 532 backgroundImage: 'url("' + fallback + '")', 533 backgroundColor: 'transparent', 534 backgroundRepeat: 'no-repeat', 535 backgroundSize: 'cover', 536 backgroundPosition: 'center center', 537 }) 538 } 539 540 // MP4 Source Tag 541 if ( 'undefined' != typeof mp4 && '' != mp4 ) { 542 543 mp4Tag = $( '<source />' ); 544 mp4Tag.attr( 'src', mp4 ); 545 mp4Tag.attr( 'type', mp4Type ); 546 547 videoTag.append( mp4Tag ); 548 } 549 // WebM Source Tag 550 if ( 'undefined' != typeof webm && '' != webm ) { 551 552 webmTag = $( '<source />' ); 553 webmTag.attr( 'src', webm ); 554 webmTag.attr( 'type', webmType ); 555 556 videoTag.append( webmTag ); 557 } 558 559 // This is either desktop, or mobile is enabled. 560 if ( ! FLBuilderLayout._isMobile() || ( FLBuilderLayout._isMobile() && "yes" == videoMobile ) ) { 561 if ( 'undefined' != typeof youtube ) { 562 FLBuilderLayout._initYoutubeBgVideo.apply( this ); 563 } 564 else if ( 'undefined' != typeof vimeo ) { 565 FLBuilderLayout._initVimeoBgVideo.apply( this ); 566 } 567 else { 568 wrap.append( videoTag ); 569 570 if ( playPauseButton.length > 0 ) { 571 var video = videoTag[0]; 572 573 playPauseButton.on( 'click', { video: video }, function(e) { 574 var video = e.data.video; 575 576 if ( video.paused ) { 577 video.play(); 578 } else { 579 video.pause(); 580 } 581 } ); 582 583 $( video ).on( 'play playing', function () { 584 playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' ); 585 } ); 586 587 $( video ).on( 'pause ended waiting', function () { 588 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 589 } ); 590 } 591 } 592 } 593 else { 594 // if we are here, it means we are on mobile and NO is set so remove video src and use fallback 595 videoTag.attr('src', '') 596 wrap.append( videoTag ); 597 } 598 599 // Mark this video as loaded. 600 wrap.data('loaded', true); 601 }, 602 603 /** 604 * Initializes Youtube video background 605 * 606 * @since 1.9 607 * @access private 608 * @method _initYoutubeBgVideo 609 */ 610 _initYoutubeBgVideo: function() 611 { 612 var playerWrap = $(this), 613 videoId = playerWrap.data('video-id'), 614 videoPlayer = playerWrap.find('.fl-bg-video-player'), 615 enableAudio = playerWrap.data('enable-audio'), 616 audioButton = playerWrap.find('.fl-bg-video-audio'), 617 playPauseButton = playerWrap.find('.fl-bg-video-play-pause-control'), 618 startTime = 'undefined' !== typeof playerWrap.data('start') ? playerWrap.data('start') : 0, 619 startTime = 'undefined' !== typeof playerWrap.data('t') && startTime === 0 ? playerWrap.data('t') : startTime, 620 endTime = 'undefined' !== typeof playerWrap.data('end') ? playerWrap.data('end') : 0, 621 loop = 'undefined' !== typeof playerWrap.data('loop') ? playerWrap.data('loop') : 1, 622 stateCount = 0, 623 player,fallback_showing; 624 625 if ( videoId ) { 626 fallback = playerWrap.data('fallback') || false 627 if( fallback ) { 628 playerWrap.find('iframe').remove() 629 fallbackTag = $( '<div></div>' ); 630 fallbackTag.addClass( 'fl-bg-video-fallback' ); 631 fallbackTag.css( 'background-image', 'url(' + playerWrap.data('fallback') + ')' ); 632 fallbackTag.css( 'background-size', 'cover' ); 633 fallbackTag.css( 'transition', 'background-image 1s') 634 playerWrap.append( fallbackTag ); 635 fallback_showing = true; 636 } 637 FLBuilderLayout._onYoutubeApiReady( function( YT ) { 638 setTimeout( function() { 639 640 player = new YT.Player( videoPlayer[0], { 641 videoId: videoId, 642 events: { 643 onReady: function(event) { 644 if ( "no" === enableAudio || FLBuilderLayout._isMobile() ) { 645 event.target.mute(); 646 } 647 else if ( "yes" === enableAudio && event.target.isMuted ) { 648 event.target.unMute(); 649 } 650 651 // Store an instance to a parent 652 playerWrap.data('YTPlayer', player); 653 FLBuilderLayout._resizeYoutubeBgVideo.apply(playerWrap); 654 655 // Queue the video. 656 event.target.playVideo(); 657 658 if ( audioButton.length > 0 && ! FLBuilderLayout._isMobile() ) {
659 audioButton.on( 'click', {button: audioButton, player: player}, FLBuilderLayout._toggleBgVideoAudio ); 660 } 661 662 if ( playPauseButton.length > 0 ) { 663 playPauseButton.on( 'click', {player: player}, function(e) { 664 var player = e.data.player; 665 666 if ( 1 === player.getPlayerState() ) { 667 player.pauseVideo(); 668 } else { 669 player.playVideo(); 670 } 671 }); 672 } 673 }, 674 onStateChange: function( event ) { 675 676 if ( event.data === 1 ) { 677 if ( fallback_showing ) { 678 $( '.fl-bg-video-fallback' ).css( 'background-image', 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)' ) 679 } 680 } 681 // Manual check if video is not playable in some browsers. 682 // StateChange order: [-1, 3, -1] 683 if ( stateCount < 4 ) { 684 stateCount++; 685 } 686 687 // Comply with the audio policy in some browsers like Chrome and Safari. 688 if ( stateCount > 1 && -1 === event.data && "yes" === enableAudio ) { 689 player.mute(); 690 player.playVideo(); 691 audioButton.show(); 692 } 693 694 if ( event.data === YT.PlayerState.ENDED && 1 === loop ) { 695 if ( startTime > 0 ) { 696 player.seekTo( startTime ); 697 } 698 else { 699 player.playVideo(); 700 } 701 } 702 703 if ( event.data === YT.PlayerState.PLAYING ) { 704 playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' ); 705 } else if ( event.data === YT.PlayerState.PAUSED ) { 706 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 707 } else if ( event.data === YT.PlayerState.BUFFERING ) { 708 playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' ); 709 } else if ( event.data === YT.PlayerState.CUED ) { 710 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 711 } else if ( event.data === YT.PlayerState.ENDED ) { 712 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 713 } 714 }, 715 onError: function(event) { 716 console.info('YT Error: ' + event.data) 717 FLBuilderLayout._onErrorYoutubeVimeo(playerWrap) 718 } 719 }, 720 playerVars: { 721 playsinline: FLBuilderLayout._isMobile() ? 1 : 0, 722 controls: 0, 723 showinfo: 0, 724 rel : 0, 725 start: startTime, 726 end: endTime, 727 } 728 } ); 729 }, 1 ); 730 } ); 731 } 732 }, 733 734 /** 735 * On youtube or vimeo error show the fallback image if available. 736 * @since 2.0.7 737 */ 738 _onErrorYoutubeVimeo: function(playerWrap) { 739 740 fallback = playerWrap.data('fallback') || false 741 if( ! fallback ) { 742 return false; 743 } 744 playerWrap.find('iframe').remove() 745 fallbackTag = $( '<div></div>' ); 746 fallbackTag.addClass( 'fl-bg-video-fallback' ); 747 fallbackTag.css( 'background-image', 'url(' + playerWrap.data('fallback') + ')' ); 748 playerWrap.append( fallbackTag ); 749 }, 750 751 /** 752 * Check if Youtube API has been downloaded 753 * 754 * @since 1.9 755 * @access private 756 * @method _onYoutubeApiReady 757 * @param {Function} callback Method to call when YT API has been loaded 758 */ 759 _onYoutubeApiReady: function( callback ) { 760 if ( window.YT && YT.loaded ) { 761 callback( YT ); 762 } else { 763 // If not ready check again by timeout.. 764 setTimeout( function() { 765 FLBuilderLayout._onYoutubeApiReady( callback ); 766 }, 350 ); 767 } 768 }, 769 770 /** 771 * Initializes Vimeo video background 772 * 773 * @since 1.9 774 * @access private 775 * @method _initVimeoBgVideo 776 */ 777 _initVimeoBgVideo: function() 778 { 779 var playerWrap = $(this), 780 videoId = playerWrap.data('video-id'), 781 videoHash = playerWrap.data('video-hash'), 782 videoPlayer = playerWrap.find('.fl-bg-video-player'), 783 enableAudio = playerWrap.data('enable-audio'), 784 audioButton = playerWrap.find('.fl-bg-video-audio'), 785 playPauseButton = playerWrap.find('.fl-bg-video-play-pause-control'), 786 playerState = '', 787 player, 788 width = playerWrap.outerWidth(), 789 ua = navigator.userAgent; 790 791 if ( typeof Vimeo !== 'undefined' && videoId ) { 792 793 const vimOptions = { 794 loop : true, 795 title : false, 796 portrait : false, 797 background : true, 798 autopause : false, 799 muted : true, 800 }; 801 802 if ( videoHash.length ) { 803 vimOptions.url = `https://player.vimeo.com/video/${ videoId }?h=${ videoHash }`; 804 } else { 805 vimOptions.id = videoId; 806 } 807 808 player = new Vimeo.Player(videoPlayer[0], vimOptions ); 809 playerWrap.data('VMPlayer', player); 810 if ( "no" === enableAudio ) { 811 player.setVolume(0); 812 } 813 else if ("yes" === enableAudio ) { 814 // Chrome, Safari, Firefox have audio policy restrictions for autoplay videos. 815 if ( ua.indexOf("Safari") > -1 || ua.indexOf("Chrome") > -1 || ua.indexOf("Firefox") > -1 ) { 816 player.setVolume(0); 817 audioButton.show(); 818 } 819 else { 820 player.setVolume(1); 821 } 822 } 823 824 player.play().catch(function(error) { 825 FLBuilderLayout._onErrorYoutubeVimeo(playerWrap) 826 }); 827 828 if ( audioButton.length > 0 ) {
829 audioButton.on( 'click', {button: audioButton, player: player}, FLBuilderLayout._toggleBgVideoAudio ); 830 } 831 832 player.on( 'play', function() { 833 playerState = 'play'; 834 playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' ); 835 } ); 836 player.on( 'pause', function() { 837 playerState = 'pause'; 838 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 839 } ); 840 player.on( 'ended', function() { 841 playerState = 'ended'; 842 playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' ); 843 } ); 844 player.on( 'bufferstart', function() { 845 playerState = 'bufferstart'; 846 playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' ); 847 } ); 848 849 if ( playPauseButton.length > 0 ) { 850 playPauseButton.on( 'click', { player: player }, function( e ) { 851 var player = e.data.player; 852 853 if ( playerState === 'play' ) { 854 player.pause(); 855 } else { 856 player.play(); 857 } 858 } ); 859 } 860 } 861 }, 862 863 /** 864 * Mute / unmute audio on row's video background. 865 * It works for both Youtube and Vimeo. 866 * 867 * @since 2.1.3 868 * @access private 869 * @method _toggleBgVideoAudio 870 * @param {Object} e Method arguments 871 */ 872 _toggleBgVideoAudio: function( e ) { 873 var player = e.data.player, 874 control = e.data.button.find('.fl-audio-control'); 875 876 if ( control.hasClass( 'fa-volume-off' ) ) { 877 // Unmute 878 control 879 .removeClass( 'fa-volume-off' ) 880 .addClass( 'fa-volume-up' ); 881 e.data.button.find( '.fa-times' ).hide(); 882 883 if ( 'function' === typeof player.unMute ) { 884 player.unMute(); 885 } 886 else { 887 player.setVolume( 1 ); 888 } 889 } 890 else { 891 // Mute 892 control 893 .removeClass( 'fa-volume-up' ) 894 .addClass( 'fa-volume-off' ); 895 e.data.button.find( '.fa-times' ).show(); 896 897 if ( 'function' === typeof player.unMute ) { 898 player.mute(); 899 } 900 else { 901 player.setVolume( 0 ); 902 } 903 } 904 }, 905 906 /** 907 * Fires when there is an error loading a video 908 * background source and shows the fallback. 909 * 910 * @since 1.6.3.3 911 * @access private 912 * @method _videoBgSourceError 913 * @param {Object} e An event object 914 * @deprecated 2.0.3 915 */ 916 _videoBgSourceError: function( e ) 917 { 918 var source = $( e.target ), 919 wrap = source.closest( '.fl-bg-video' ), 920 vid = wrap.find( 'video' ), 921 fallback = wrap.data( 'fallback' ), 922 fallbackTag = ''; 923 source.remove(); 924 925 if ( vid.find( 'source' ).length ) { 926 // Don't show the fallback if we still have other sources to check. 927 return; 928 } else if ( '' !== fallback ) { 929 fallbackTag = $( '<div></div>' ); 930 fallbackTag.addClass( 'fl-bg-video-fallback' ); 931 fallbackTag.css( 'background-image', 'url(' + fallback + ')' ); 932 wrap.append( fallbackTag ); 933 vid.remove(); 934 } 935 }, 936 937 /** 938 * Fires when the window is resized to resize 939 * all video backgrounds. 940 * 941 * @since 1.1.4 942 * @access private 943 * @method _resizeBgVideos 944 */ 945 _resizeBgVideos: function() 946 { 947 $('.fl-bg-video').each( function() { 948 949 FLBuilderLayout._resizeBgVideo.apply( this ); 950 951 if ( $( this ).parent().find( 'img' ).length > 0 ) { 952 $( this ).parent().imagesLoaded( $.proxy( FLBuilderLayout._resizeBgVideo, this ) ); 953 } 954 } ); 955 }, 956 957 /** 958 * Fires when the window is resized to resize 959 * a single video background. 960 * 961 * @since 1.1.4 962 * @access private 963 * @method _resizeBgVideo 964 */ 965 _resizeBgVideo: function() 966 { 967 if ( 0 === $( this ).find( 'video' ).length && 0 === $( this ).find( 'iframe' ).length ) { 968 return; 969 } 970 971 var wrap = $(this), 972 wrapHeight = wrap.outerHeight(), 973 wrapWidth = wrap.outerWidth(), 974 vid = wrap.find('video'), 975 vidHeight = wrap.data('height'), 976 vidWidth = wrap.data('width'), 977 newWidth = wrapWidth, 978 newHeight = Math.round(vidHeight * wrapWidth/vidWidth), 979 newLeft = 0, 980 newTop = 0, 981 iframe = wrap.find('iframe'), 982 isRowFullHeight = $(this).closest('.fl-row-bg-video').hasClass('fl-row-full-height'), 983 vidCSS = { 984 top: '50%', 985 left: '50%', 986 transform: 'translate(-50%,-50%)', 987 }; 988 989 if ( vid.length ) { 990 if(vidHeight === '' || typeof vidHeight === 'undefined' || vidWidth === '' || typeof vidWidth === 'undefined') { 991 vid.css({ 992 'left' : '0px', 993 'top' : '0px', 994 'width' : newWidth + 'px' 995 }); 996 997 // Try to set the actual video dimension on 'loadedmetadata' when using URL as video source 998 vid.on('loadedmetadata', FLBuilderLayout._resizeOnLoadedMeta); 999 1000 return; 1001 } 1002 1003 if ( ! isRowFullHeight ) { 1004 if ( newHeight < wrapHeight ) { 1005 newHeight = wrapHeight; 1006 newWidth = vidHeight ? Math.round(vidWidth * wrapHeight/vidHeight) : newWidth; 1007 newLeft = -((newWidth - wrapWidth) / 2); 1008 } 1009 else { 1010 newTop = -((newHeight - wrapHeight)/2); 1011 } 1012 vidCSS = { 1013 left : newLeft + 'px', 1014 top : newTop + 'px', 1015 height : newHeight + 'px', 1016 width : newWidth + 'px', 1017 } 1018 } 1019 1020 vid.css( vidCSS ); 1021 1022 } 1023 else if ( iframe.length ) { 1024 1025 // Resize Youtube video player within iframe tag 1026 if ( typeof wrap.data('youtube') !== 'undefined' ) { 1027 FLBuilderLayout._resizeYoutubeBgVideo.apply(this); 1028 } 1029 } 1030 }, 1031 1032 /** 1033 * Fires when video meta has been loaded. 1034 * This will be Triggered when width/height attributes were not specified during video background resizing. 1035 * 1036 * @since 1.8.5 1037 * @access private 1038 * @method _resizeOnLoadedMeta 1039 */ 1040 _resizeOnLoadedMeta: function(){ 1041 var video = $(this), 1042 wrapHeight = video.parent().outerHeight(), 1043 wrapWidth = video.parent().outerWidth(), 1044 vidWidth = video[0].videoWidth, 1045 vidHeight = video[0].videoHeight, 1046 newHeight = Math.round(vidHeight * wrapWidth/vidWidth), 1047 newWidth = wrapWidth, 1048 newLeft = 0, 1049 newTop = 0; 1050 1051 if(newHeight < wrapHeight) { 1052 newHeight = wrapHeight; 1053 newWidth = Math.round(vidWidth * wrapHeight/vidHeight); 1054 newLeft = -((newWidth - wrapWidth)/2); 1055 } 1056 else { 1057 newTop = -((newHeight - wrapHeight)/2); 1058 } 1059 1060 video.parent().data('width', vidWidth); 1061 video.parent().data('height', vidHeight); 1062 1063 video.css({ 1064 'left' : newLeft + 'px', 1065 'top' : newTop + 'px', 1066 'width' : newWidth + 'px', 1067 'height' : newHeight + 'px' 1068 }); 1069 }, 1070 1071 /** 1072 * Fires when the window is resized to resize 1073 * a single Youtube video background. 1074 * 1075 * @since 1.9 1076 * @access private 1077 * @method _resizeYoutubeBgVideo 1078 */ 1079 _resizeYoutubeBgVideo: function() 1080 { 1081 var wrap = $(this), 1082 wrapWidth = wrap.outerWidth(), 1083 wrapHeight = wrap.outerHeight(), 1084 player = wrap.data('YTPlayer'), 1085 video = player ? player.getIframe() : null, 1086 aspectRatioSetting = '16:9', // Medium 1087 aspectRatioArray = aspectRatioSetting.split( ':' ),
1088 aspectRatio = aspectRatioArray[0] / aspectRatioArray[1], 1089 ratioWidth = wrapWidth / aspectRatio, 1090 ratioHeight = wrapHeight * aspectRatio, 1091 isWidthFixed = wrapWidth / wrapHeight > aspectRatio, 1092 width = isWidthFixed ? wrapWidth : ratioHeight, 1093 height = isWidthFixed ? ratioWidth : wrapHeight; 1094 1095 if ( video ) { 1096 $(video).width( width ).height( height ); 1097 } 1098 }, 1099 1100 /** 1101 * Opens a tab or accordion item if the browser hash is set 1102 * to the ID of one on the page. 1103 * 1104 * @since 1.6.0 1105 * @access private 1106 * @method _initHash 1107 */ 1108 _initHash: function() 1109 { 1110 var hash = window.location.hash.replace( '#', '' ).split( '/' ).shift(), 1111 element = null, 1112 tabs = null, 1113 responsiveLabel = null, 1114 tabIndex = null, 1115 label = null; 1116 1117 if ( '' !== hash ) { 1118 1119 try { 1120 1121 element = $( '#' + hash ); 1122 1123 if ( element.length > 0 ) { 1124 1125 if ( element.hasClass( 'fl-accordion-item' ) ) { 1126 setTimeout( function() { 1127 element.find( '.fl-accordion-button' ).trigger( 'click' ); 1128 }, 100 ); 1129 } 1130 if ( element.hasClass( 'fl-tabs-panel' ) ) { 1131 setTimeout( function() { 1132 tabs = element.closest( '.fl-tabs' ); 1133 responsiveLabel = element.find( '.fl-tabs-panel-label' ); 1134 tabIndex = responsiveLabel.data( 'index' ); 1135 label = tabs.find( '.fl-tabs-labels .fl-tabs-label[data-index=' + tabIndex + ']' ); 1136 1137 label[0].click(); 1138 FLBuilderLayout._scrollToElement(element); 1139 }, 100 ); 1140 } 1141 } 1142 } 1143 catch( e ) {} 1144 } 1145 }, 1146 1147 /** 1148 * Adds a visually-hidden "(opens in new tab)" notice to links 1149 * inside a builder layout that target a new tab, and removes it 1150 * from ones that don't. Runs on the live DOM only, across every 1151 * module, so the notice is never part of any saved content. Covers 1152 * both real anchors (`target` attribute) and non-anchor elements 1153 * like the Box module's `role="link"` div, which carries the same 1154 * intent via a `data-link-target` attribute instead. 1155 * 1156 * @since 2.12 1157 * @access private 1158 * @method _initLinkNotices 1159 */ 1160 _initLinkNotices: function() 1161 { 1162 $( '.fl-builder-content a[target], .fl-builder-content [role="link"][data-link-target]' ).each( function() { 1163 var link = $( this ), 1164 notice = link.children( 'span.fl-new-tab-notice' ), 1165 target = link.attr( 'target' ) || link.attr( 'data-link-target' ); 1166 1167 if ( '_blank' === target ) { 1168 if ( 0 === notice.length ) { 1169 link.append( '<span class="fl-new-tab-notice sr-only">' + FLBuilderLayoutConfig.i18n.opensInNewTab + '</span>' ); 1170 } 1171 } else if ( notice.length > 0 ) { 1172 notice.remove(); 1173 } 1174 }); 1175 }, 1176 1177 /** 1178 * Initializes all anchor links on the page for smooth scrolling. 1179 * 1180 * @since 1.4.9 1181 * @access private 1182 * @method _initAnchorLinks 1183 */ 1184 _initAnchorLinks: function() 1185 { 1186 $( 'a, [role="link"]' ).each( FLBuilderLayout._initAnchorLink ); 1187 }, 1188 1189 /** 1190 * Initializes a single anchor link for smooth scrolling. 1191 * 1192 * @since 1.4.9 1193 * @access private 1194 * @method _initAnchorLink 1195 */ 1196 _initAnchorLink: function() 1197 { 1198 var link = $( this ), 1199 href = link.data( 'url' ) ? link.data( 'url' ) : link.attr( 'href' ), 1200 target = link.data( 'url' ) ? new URL( href, window.location.href ) : this, 1201 loc = window.location, 1202 id = null, 1203 element = null, 1204 flNode = false; 1205 1206 if ( 'undefined' != typeof href && href.indexOf( '#' ) > -1 && link.closest('svg').length < 1 ) { 1207 1208 if ( loc.pathname.replace( /^\//, '' ) == target.pathname.replace( /^\//, '' ) && loc.hostname == target.hostname ) { 1209 1210 try { 1211 1212 id = href.split( '#' ).pop(); 1213 // If there is no ID then we have nowhere to look 1214 // Fixes a quirk in jQuery and FireFox 1215 if( ! id ) { 1216 return; 1217 } 1218 element = $( '#' + id ); 1219 1220 if ( element.length > 0 ) { 1221 flNode = element.hasClass( 'fl-row' ) || element.hasClass( 'fl-col' ) || element.hasClass( 'fl-module' ); 1222 if ( !element.hasClass( 'fl-no-scroll' ) && ( link.hasClass( 'fl-scroll-link' ) || flNode ) ) { 1223 $( link ).on( 'click', FLBuilderLayout._scrollToElementOnLinkClick ); 1224 } 1225 if ( element.hasClass( 'fl-accordion-item' ) ) { 1226 $( link ).on( 'click', FLBuilderLayout._scrollToAccordionOnLinkClick ); 1227 } 1228 if ( element.hasClass( 'fl-tabs-panel' ) ) { 1229 $( link ).on( 'click', FLBuilderLayout._scrollToTabOnLinkClick ); 1230 } 1231 } 1232 } 1233 catch( e ) {} 1234 } 1235 } 1236 }, 1237 1238 /** 1239 * Scrolls to an element when an anchor link is clicked. 1240 * 1241 * @since 1.4.9 1242 * @access private 1243 * @method _scrollToElementOnLinkClick 1244 * @param {Object} e An event object. 1245 * @param {Function} callback A function to call when the scroll is complete. 1246 */ 1247 _scrollToElementOnLinkClick: function( e, callback ) 1248 { 1249 var attribute = $( this ).data( 'url' ) ? $( this ).data( 'url' ) : $( this ).attr( 'href' ); 1250 var element = $( '#' + attribute.split( '#' ).pop() ); 1251 1252 FLBuilderLayout._scrollToElement( element, callback ); 1253 1254 e.preventDefault(); 1255 }, 1256 1257 /** 1258 * Scrolls to an element. 1259 * 1260 * @since 1.6.4.5 1261 * @access private 1262 * @method _scrollToElement 1263 * @param {Object} element The element to scroll to. 1264 * @param {Function} callback A function to call when the scroll is complete. 1265 */ 1266 _scrollToElement: function( element, callback ) 1267 { 1268 var config = FLBuilderLayoutConfig.anchorLinkAnimations, 1269 dest = 0, 1270 win = $( window ), 1271 doc = $( document ); 1272 1273 if ( element.length > 0 ) { 1274 1275 if ( 'fixed' === element.css('position') || 'fixed' === element.parent().css('position') ) { 1276 dest = element.position().top; 1277 } 1278 else if ( element.offset().top > doc.height() - win.height() ) { 1279 dest = doc.height() - win.height(); 1280 } 1281 else { 1282 dest = element.offset().top - config.offset; 1283 } 1284 1285 $( 'html, body' ).stop( true ).animate( { scrollTop: dest }, config.duration, config.easing, function() { 1286 1287 if ( 'undefined' != typeof callback ) { 1288 callback(); 1289 } 1290 1291 if ( undefined != element.attr( 'id' ) && window.location.hash !== '#' + element.attr( 'id' ) ) { 1292 1293 var firefox_version = window.navigator.userAgent.match( /Firefox\/(\d+)\./ ), 1294 firefox_version = firefox_version ? parseInt( firefox_version[1], 10 ) : null; 1295 1296 if ( firefox_version !== null && firefox_version < 135 ) { 1297 window.location.hash = element.attr( 'id' ); 1298 } else { 1299 if ( history.pushState ) { 1300 history.pushState( null, null, '#' + element.attr( 'id' ) ); 1301 } else { 1302 window.location.hash = element.attr( 'id' ); 1303 } 1304 } 1305 } 1306 } ); 1307 } 1308 }, 1309 1310 /** 1311 * Scrolls to an accordion item when a link is clicked. 1312 * 1313 * @since 1.5.9 1314 * @access private 1315 * @method _scrollToAccordionOnLinkClick 1316 * @param {Object} e An event object. 1317 */ 1318 _scrollToAccordionOnLinkClick: function( e ) 1319 { 1320 var element = $( '#' + $( this ).attr( 'href' ).split( '#' ).pop() ); 1321 1322 if ( element.length > 0 ) { 1323 1324 var callback = function() { 1325 if ( element ) { 1326 element.find( '.fl-accordion-button' ).trigger( 'click' ); 1327 element = false;
1328 } 1329 }; 1330 1331 FLBuilderLayout._scrollToElementOnLinkClick.call( this, e, callback ); 1332 } 1333 }, 1334 1335 /** 1336 * Scrolls to a tab panel when a link is clicked. 1337 * 1338 * @since 1.5.9 1339 * @access private 1340 * @method _scrollToTabOnLinkClick 1341 * @param {Object} e An event object. 1342 */ 1343 _scrollToTabOnLinkClick: function( e ) 1344 { 1345 var element = $( '#' + $( this ).attr( 'href' ).split( '#' ).pop() ), 1346 tabs = null, 1347 label = null, 1348 responsiveLabel = null; 1349 1350 if ( element.length > 0 ) { 1351 1352 tabs = element.closest( '.fl-tabs' ); 1353 responsiveLabel = element.find( '.fl-tabs-panel-label' ); 1354 tabIndex = responsiveLabel.data( 'index' ); 1355 label = tabs.find( '.fl-tabs-labels .fl-tabs-label[data-index=' + tabIndex + ']' ); 1356 1357 if ( responsiveLabel.is( ':visible' ) ) { 1358 1359 var callback = function() { 1360 if ( element ) { 1361 responsiveLabel.trigger( $.Event( 'click', { which: 1 } ) ); 1362 } 1363 }; 1364 1365 FLBuilderLayout._scrollToElementOnLinkClick.call( this, e, callback ); 1366 } 1367 else { 1368 label[0].click(); 1369 FLBuilderLayout._scrollToElement( element ); 1370 } 1371 1372 e.preventDefault(); 1373 } 1374 }, 1375 1376 /** 1377 * Initializes all builder forms on a page. 1378 * 1379 * @since 1.5.4 1380 * @access private 1381 * @method _initForms 1382 */ 1383 _initForms: function() 1384 { 1385 if ( ! FLBuilderLayout._hasPlaceholderSupport ) { 1386 $( '.fl-form-field input' ).each( FLBuilderLayout._initFormFieldPlaceholderFallback ); 1387 } 1388 1389 $( '.fl-form-field input' ).on( 'focus', FLBuilderLayout._clearFormFieldError ); 1390 }, 1391 1392 /** 1393 * Checks to see if the current device has HTML5 1394 * placeholder support. 1395 * 1396 * @since 1.5.4 1397 * @access private 1398 * @method _hasPlaceholderSupport 1399 * @return {Boolean} 1400 */ 1401 _hasPlaceholderSupport: function() 1402 { 1403 var input = document.createElement( 'input' ); 1404 1405 return 'undefined' != input.placeholder; 1406 }, 1407 1408 /** 1409 * Initializes the fallback for when placeholders aren't supported. 1410 * 1411 * @since 1.5.4 1412 * @access private 1413 * @method _initFormFieldPlaceholderFallback 1414 */ 1415 _initFormFieldPlaceholderFallback: function() 1416 { 1417 var field = $( this ), 1418 val = field.val(), 1419 placeholder = field.attr( 'placeholder' ); 1420 1421 if ( 'undefined' != placeholder && '' === val ) { 1422 field.val( placeholder ); 1423 field.on( 'focus', FLBuilderLayout._hideFormFieldPlaceholderFallback ); 1424 field.on( 'blur', FLBuilderLayout._showFormFieldPlaceholderFallback ); 1425 } 1426 }, 1427 1428 /** 1429 * Hides a fallback placeholder on focus. 1430 * 1431 * @since 1.5.4 1432 * @access private 1433 * @method _hideFormFieldPlaceholderFallback 1434 */ 1435 _hideFormFieldPlaceholderFallback: function() 1436 { 1437 var field = $( this ), 1438 val = field.val(), 1439 placeholder = field.attr( 'placeholder' ); 1440 1441 if ( val == placeholder ) { 1442 field.val( '' ); 1443 } 1444 }, 1445 1446 /** 1447 * Shows a fallback placeholder on blur. 1448 * 1449 * @since 1.5.4 1450 * @access private 1451 * @method _showFormFieldPlaceholderFallback 1452 */ 1453 _showFormFieldPlaceholderFallback: function() 1454 { 1455 var field = $( this ), 1456 val = field.val(), 1457 placeholder = field.attr( 'placeholder' ); 1458 1459 if ( '' === val ) { 1460 field.val( placeholder ); 1461 } 1462 }, 1463 1464 /** 1465 * Clears a form field error message. 1466 * 1467 * @since 1.5.4 1468 * @access private 1469 * @method _clearFormFieldError 1470 */ 1471 _clearFormFieldError: function() 1472 { 1473 var field = $( this ); 1474 1475 field.removeAttr( 'aria-invalid' ); 1476 field.removeClass( 'fl-form-error' ); 1477 const message = field.attr('aria-describedby'); 1478 message ? $( '#' + message ).hide() : field.siblings( '.fl-form-error-message' ).hide(); 1479 }, 1480 1481 /** 1482 * Init Row Shape Layer's height. 1483 * 1484 * @since 2.5.3 1485 * @access private 1486 * @method _initRowShapeLayerHeight 1487 */ 1488 _initRowShapeLayerHeight: function () { 1489 FLBuilderLayout._adjustRowShapeLayerHeight(); 1490 $( window ).on( 'resize', FLBuilderLayout._adjustRowShapeLayerHeight ); 1491 }, 1492 1493 /** 1494 * Set parent column width of nested columns when it's zero or blank. 1495 * 1496 * @since 2.7.4 1497 * @access private 1498 * @method _initNestedColsWidth 1499 */ 1500 _initNestedColsWidth: function() 1501 { 1502 var nestedCols = $( '.fl-col-has-cols' ); 1503 1504 if ( nestedCols.length <= 0 ) { 1505 return; 1506 } 1507 1508 $( nestedCols ).each( function(index, col ){ 1509 if ( $( col ).width() <= 0 ) { 1510 $( col ).css( 'width', FLBuilderLayoutConfig.emptyColWidth ); 1511 } 1512 }); 1513 }, 1514 1515 /** 1516 * Adjust Row Shape Layer's height to fix to remove the fine line that appears on certain screen sizes. 1517 * 1518 * @since 2.5.3 1519 * @access private 1520 * @method _adjustRowShapeLayerHeight 1521 */
1522 _adjustRowShapeLayerHeight: function() { 1523 var rowShapeLayers = $('.fl-builder-shape-layer'); 1524 1525 $( rowShapeLayers ).each(function (index) { 1526 var rowShapeLayer = $(this), 1527 shape = $(rowShapeLayer).find('svg'), 1528 height = shape.height(), 1529 excludeShapes = '.fl-builder-shape-circle, .fl-builder-shape-dot-cluster, .fl-builder-shape-topography, .fl-builder-shape-rect'; 1530 1531 if ( ! rowShapeLayer.is( excludeShapes ) ) { 1532 $(shape).css('height', Math.ceil( height ) ); 1533 } 1534 }); 1535 }, 1536 _string_to_slug: function( str ) { 1537 str = str.replace(/^\s+|\s+$/g, ''); // trim 1538 if ( 'undefined' == typeof window._fl_string_to_slug_regex ) { 1539 regex = new RegExp('[^a-zA-Z0-9\'":() !.,-_|]', 'g'); 1540 } else { 1541 regex = new RegExp('[^' + window._fl_string_to_slug_regex + '\'":\(\) !.,-_|\\\p{Letter}]', 'ug'); 1542 } 1543 str = str.replace(regex, '') // remove invalid chars 1544 .replace(/\s+/g, ' '); // collapse whitespace and replace by a space 1545 return str; 1546 }, 1547 _reorderMenu: function() { 1548 if ( $('#wp-admin-bar-fl-builder-frontend-edit-link-default li').length > 1 ) { 1549 $( '#wp-admin-bar-fl-builder-frontend-duplicate-link' ) 1550 .appendTo('#wp-admin-bar-fl-builder-frontend-edit-link-default') 1551 .css( 'padding-top', '5px' ) 1552 .css( 'border-top', '2px solid #1D2125' ) 1553 .css( 'margin-top', '5px' ) 1554 } 1555 } 1556 }; 1557 1558 /* Initializes the builder layout. */ 1559 $(function(){ 1560 FLBuilderLayout.init(); 1561 }); 1562 1563})(jQuery); 1564(function($){ 1565 1566 if(typeof FLBuilderLayoutModules !== 'undefined') { 1567 return; 1568 } 1569 1570 /** 1571 * Helper class with generic logic for builder modules. 1572 * If generic module code needs to work in both the block 1573 * editor and builder, it should go here. Otherwise, it 1574 * should go on FLBuilderLayout. 1575 * 1576 * @since 2.9 1577 */ 1578 FLBuilderLayoutModules = { 1579 1580 /** 1581 * Initializes builder module logic. 1582 * 1583 * @since 2.9 1584 * @method init 1585 */ 1586 init: function() 1587 { 1588 // Only init if the builder isn't active. 1589 if ( 0 === $('.fl-builder-edit').length ) { 1590 1591 // Init module animations. 1592 FLBuilderLayoutModules._initModuleAnimations(); 1593 } 1594 }, 1595 1596 /** 1597 * Initializes module animations. 1598 * 1599 * @since 1.1.9 1600 * @access private 1601 * @method _initModuleAnimations 1602 */ 1603 _initModuleAnimations: function() 1604 { 1605 if(typeof jQuery.fn.waypoint !== 'undefined') { 1606 $('.fl-animation').each( function() { 1607 var node = $( this ), 1608 nodeTop = node.offset().top, 1609 winHeight = $( window ).height(), 1610 bodyHeight = $( 'body' ).height(), 1611 waypoint = FLBuilderLayoutConfig.waypoint, 1612 offset = '80%'; 1613 1614 if ( typeof waypoint.offset !== undefined ) { 1615 offset = FLBuilderLayoutConfig.waypoint.offset + '%'; 1616 } 1617 1618 if ( bodyHeight - nodeTop < winHeight * 0.2 ) { 1619 offset = '100%'; 1620 } 1621 1622 node.waypoint({ 1623 offset: offset, 1624 handler: FLBuilderLayoutModules._doModuleAnimation 1625 }); 1626 } ); 1627 } 1628 }, 1629 1630 /** 1631 * Runs a module animation. 1632 * 1633 * @since 1.1.9 1634 * @access private 1635 * @method _doModuleAnimation 1636 */ 1637 _doModuleAnimation: function() 1638 { 1639 var module = 'undefined' == typeof this.element ? $(this) : $(this.element), 1640 delay = parseFloat(module.data('animation-delay')), 1641 duration = parseFloat(module.data('animation-duration')); 1642 1643 if ( ! isNaN( duration ) ) { 1644 module.css( 'animation-duration', duration + 's' ); 1645 } 1646 1647 if(!isNaN(delay) && delay > 0) { 1648 setTimeout(function(){ 1649 module.addClass('fl-animated'); 1650 }, delay * 1000); 1651 } else { 1652 setTimeout(function(){ 1653 module.addClass('fl-animated'); 1654 }, 1); 1655 } 1656 } 1657 }; 1658 1659 /* Initializes builder module logic. */ 1660 $(function(){ 1661 FLBuilderLayoutModules.init(); 1662 }); 1663 1664})(jQuery); 1665 1666/* Start Global JS */ 1667document.addEventListener("DOMContentLoaded", function () { 1668 const menuToggle = document.getElementById("unstuckMenuToggle"); 1669 const menuOverlay = document.querySelector(".unstuck-menu-overlay-inner"); 1670 const logoImg = document.querySelector("#mobile-menu-logo img"); 1671 const overlayLinks = document.querySelectorAll(".unstuck-menu-overlay-inner a"); 1672 1673 if (!menuToggle || !menuOverlay) { 1674 return; 1675 } 1676 1677 let scrollPosition = 0; 1678 1679 // Logo sources 1680 const closedSrc = logoImg ? logoImg.getAttribute("src") : null; 1681 const openSrc = "/wp-content/uploads/2025/11/The-Unstuck-Group-Inline-FullColor-Inverse.svg"; 1682 1683 function lockBodyScroll() { 1684 scrollPosition = window.pageYOffset || document.documentElement.scrollTop; 1685 document.body.style.position = "fixed"; 1686 document.body.style.top = `-${scrollPosition}px`; 1687 document.body.style.left = "0"; 1688 document.body.style.right = "0"; 1689 document.body.style.width = "100%"; 1690 } 1691 1692 function unlockBodyScroll() { 1693 document.body.style.position = ""; 1694 document.body.style.top = ""; 1695 document.body.style.left = ""; 1696 document.body.style.right = ""; 1697 document.body.style.width = ""; 1698 window.scrollTo(0, scrollPosition); 1699 } 1700 1701 function openMenu() { 1702 if (menuOverlay.classList.contains("active")) return; 1703 1704 menuToggle.classList.add("open"); 1705 menuOverlay.classList.add("active"); 1706 document.body.classList.add("menu-open"); 1707 lockBodyScroll(); 1708 1709 if (logoImg && closedSrc && openSrc) { 1710 fadeSwapLogo(openSrc); 1711 } 1712 } 1713 1714 function closeMenu() { 1715 if (!menuOverlay.classList.contains("active")) return; 1716 1717 menuToggle.classList.remove("open"); 1718 menuOverlay.classList.remove("active"); 1719 document.body.classList.remove("menu-open"); 1720 unlockBodyScroll(); 1721 1722 if (logoImg && closedSrc) { 1723 fadeSwapLogo(closedSrc); 1724 } 1725 } 1726 1727 // Toggle menu open/close 1728 menuToggle.addEventListener("click", function (e) { 1729 e.stopPropagation(); 1730 1731 if (menuOverlay.classList.contains("active")) { 1732 closeMenu(); 1733 } else { 1734 openMenu(); 1735 } 1736 }); 1737 1738 // Close menu on link click inside overlay 1739 overlayLinks.forEach(link => { 1740 link.addEventListener("click", function () { 1741 closeMenu(); 1742 }); 1743 }); 1744 1745 // Close on ESC 1746 document.addEventListener("keydown", function (e) { 1747 if (e.key === "Escape" && document.body.classList.contains("menu-open")) { 1748 closeMenu(); 1749 } 1750 }); 1751 1752 // Optional: close if you click outside overlay & toggle 1753 document.addEventListener("click", function (e) { 1754 if (!document.body.classList.contains("menu-open")) return; 1755 1756 const clickedOverlay = menuOverlay.contains(e.target); 1757 const clickedToggle = e.target.closest("#unstuckMenuToggle"); 1758 1759 if (!clickedOverlay && !clickedToggle) { 1760 closeMenu(); 1761 } 1762 }); 1763 1764 function fadeSwapLogo(newSrc) { 1765 if (!logoImg) return; 1766 1767 // Save current transition (if any) 1768 const previousTransition = logoImg.style.transition; 1769 1770 // Disable transitions for instant swap 1771 logoImg.style.transition = "none"; 1772 1773 // Swap image immediately 1774 logoImg.setAttribute("src", newSrc); 1775 1776 // Force a reflow so browser applies change instantly 1777 void logoImg.offsetHeight; 1778 1779 // Restore old transition after the frame 1780 requestAnimationFrame(() => { 1781 logoImg.style.transition = previousTransition; 1782 }); 1783 } 1784}); 1785 1786document.addEventListener('DOMContentLoaded', function () { 1787 document.querySelectorAll('.back-button').forEach(function (btn) { 1788 btn.addEventListener('click', function (e) { 1789 const hasHistory = window.history.length > 1 && document.referrer; 1790 1791 if (hasHistory) { 1792 e.preventDefault(); 1793 window.history.back(); 1794 } 1795 // else: do nothing → browser follows the normal href set in Beaver Builder 1796 }); 1797 }); 1798}); 1799 1800 1801/* End Global JS */ 1802 1803 1804;/* eslint-disable no-undef */ 1805(function($) { 1806 1807 FLBuilderSearchForm = function(settings) { 1808 this.settings = settings; 1809 this.nodeClass = '.fl-node-' + settings.id; 1810 this.searchForm = $(this.nodeClass + ' .fl-search-form'); 1811 this.form = this.searchForm.find('form'); 1812 this.input = this.form.find('input[type=search]'); 1813 this.button = this.searchForm.find('.fl-button:is(a, button:not([type="submit"]), .fl-button:is(a, button:not([type="submit"]) *'); 1814 this.resultsEl = $(this.nodeClass + ' .fl-search-results-content'); 1815 1816 this._init(); 1817 }; 1818 1819 FLBuilderSearchForm.prototype = { 1820 1821 settings: {}, 1822 nodeClass: '', 1823 searchForm: '', 1824 form: null, 1825 input: null, 1826 button: null, 1827 resultsEl: '', 1828 searching: false, 1829 prevSearchData: {}, 1830 request: null, 1831 1832 _init: function() { 1833 this._bindEvents(); 1834 this._popupSearch(); 1835 }, 1836 1837 _bindEvents: function(){ 1838 var $this = this, 1839 keyCode = null, 1840 keyType = null, 1841 enterPressed = false, 1842 t, et; 1843 1844 this.button.on('click', $.proxy(this._buttonClick, this)); 1845 this.input.on('input', (event) => { 1846 const input = event.target; 1847 if ( input.checkValidity() ) { 1848 input.removeAttribute('aria-invalid'); 1849 } else { 1850 input.setAttribute('aria-invalid', true); 1851 } 1852 }); 1853 1854 if ( this.searchForm.hasClass('fl-search-button-expand') ) { 1855 this.input.attr({'id': 'input-' + this.settings.id, 'tabindex': -1}); 1856 this.button.closest('.fl-button').attr({'aria-controls': 'input-' + this.settings.id, 'aria-expanded': false}); 1857 } 1858 else if ( this.searchForm.hasClass('fl-search-button-reveal') ) { 1859 const form = this.searchForm.find('.fl-search-form-input-wrap'); 1860 form.attr('id', 'form-' + this.settings.id); 1861 this.button.closest('.fl-button').attr({'aria-controls': 'form-' + this.settings.id, 'aria-expanded': false, 'aria-haspopup': true}); 1862 form.on('focusout', (event) => { 1863 if (form.is(':visible') && form.has($(event.relatedTarget)).length === 0) { 1864 this._buttonClick(event); 1865 } 1866 }); 1867 this.searchForm.on('keydown', (event) => { 1868 if (event.key === 'Escape' && form.is(':visible')) { 1869 this.button.focus(); 1870 } 1871 }); 1872 } 1873 1874 if ( 'ajax' == this.settings.result ) { 1875 $(document).on('click touchend', function(e){ 1876 if( $(e.target).is('input') ) return; 1877 1878 $this._hideResults(); 1879 } ); 1880 1881 $this.resultsEl.bind("click touchend", function (e) { 1882 e.stopImmediatePropagation(); 1883 }); 1884 1885 // Disable form submit. 1886 $this.form.on( 'submit', function (e) { 1887 e.preventDefault(); 1888 }); 1889 1890 // Prevent auto-clearing errors in the FLBuilderLayout helper class. 1891 this.input.off('focus'); 1892 1893 this.input.on('keyup', function(e) { 1894 if (window.event) { 1895 keyCode = window.event.keyCode; 1896 keyType = window.event.type; 1897 } else if (e) { 1898 keyCode = e.which; 1899 keyType = e.type; 1900 } 1901 1902 // Prevent rapid enter 1903 if ( 13 == keyCode ) { 1904 clearTimeout(et); 1905 et = setTimeout(function(){ 1906 enterPressed = false;
1907 }, 300); 1908 if ( enterPressed ) { 1909 return false; 1910 } else { 1911 enterPressed = true; 1912 } 1913 } 1914 1915 if ( $this.input.val().length >= 3 && 'keyup' == keyType && 13 == keyCode ) { 1916 $this._search(e); 1917 return false; 1918 } 1919 }); 1920 1921 this.input.on('click input', function(e) { 1922 if (window.event) { 1923 keyCode = window.event.keyCode; 1924 keyType = window.event.type; 1925 } else if (e) { 1926 keyCode = e.which; 1927 keyType = e.type; 1928 } 1929 1930 // F1 to F12 1931 if ( (keyCode >= 37 && keyCode <= 40) || (keyCode >= 112 && keyCode <= 123) ) { 1932 return; 1933 } 1934 1935 if ($this.input.val().length < 3) { 1936 $this._hideLoader(); 1937 $this._hideResults(); 1938 $this.input.attr('aria-invalid', true); 1939 if ($this.post != null) $this.post.abort(); 1940 clearTimeout(t); 1941 return; 1942 } 1943 1944 if ( 'click' == keyType || keyCode == 32 ) { 1945 if ( $this.resultsEl.html().length != 0 ) { 1946 clearTimeout(t); 1947 if( $this.resultsEl.hasClass('fl-search-open') ) return; 1948 $this._showResults(); 1949 } 1950 else { 1951 $this._hideResults(); 1952 } 1953 return; 1954 } 1955 1956 if ($this.request != null) $this.request.abort(); 1957 $this._hideLoader(); 1958 1959 clearTimeout(t); 1960 t = setTimeout(function() { 1961 $this._search(e); 1962 }, 100); 1963 }); 1964 } 1965 }, 1966 1967 _search: function(e) { 1968 if ($.trim(this.input.val()).length < 1 || ('ajax' == this.settings.result && this.input.val().length < 3)) { 1969 this.input.attr('aria-invalid', true); 1970 return; 1971 } 1972 e.preventDefault(); 1973 1974 if ( 'ajax' == this.settings.result ) { 1975 this._doAjaxSearch(); 1976 } 1977 else { 1978 this.form.submit(); 1979 // TODO: _doRedirectResults() 1980 } 1981 1982 return false; 1983 }, 1984 1985 _doAjaxSearch: function() { 1986 var searchText = this.input.val(), 1987 postId = this.searchForm.closest( '.fl-builder-content' ).data( 'post-id' ), 1988 templateId = this.searchForm.data( 'template-id' ), 1989 templateNodeId = this.searchForm.data( 'template-node-id' ), 1990 ajaxData = {}, 1991 self = this; 1992 1993 if ( searchText.length < 1 ) return; 1994 1995 this.searching = true; 1996 1997 // Show loader 1998 this._showLoader(); 1999 2000 ajaxData = { 2001 action : 'fl_search_query', 2002 keyword : searchText, 2003 post_id : postId, 2004 template_id : templateId, 2005 template_node_id : templateNodeId, 2006 node_id : this.settings.id, 2007 nonce : this.settings.nonce, 2008 } 2009 2010 // Check to see if searching the same keywords. 2011 if ( JSON.stringify(ajaxData) === JSON.stringify(this.prevSearchData) ) { 2012 if ( ! this.resultsEl.hasClass('fl-search-open') ) { 2013 this._showResults(); 2014 } 2015 this._hideLoader(); 2016 return false; 2017 } 2018 2019 // Send server request. 2020 this.request = $.post( FLBuilderLayoutConfig.paths.wpAjaxUrl, ajaxData, function(response){ 2021 self._hideLoader(); 2022 2023 self.resultsEl.html(""); 2024 self.resultsEl.html(response); 2025 self._showResults(); 2026 2027 self.prevSearchData = ajaxData; 2028 }); 2029 }, 2030 2031 _popupSearch: function() { 2032 var inputWrap = this.searchForm.find('.fl-search-form-input-wrap'), 2033 $this = this; 2034 2035 if ('button' !== this.settings.layout || ( 'fullscreen' !== this.settings.btnAction && 'reveal' !== this.settings.btnAction )) { 2036 return; 2037 } 2038 2039 this.button.off('click'); 2040 this.button.magnificPopup({ 2041 type: 'inline', 2042 mainClass: 'fl-node-' + this.settings.id, 2043 items: { 2044 src: inputWrap[0], 2045 }, 2046 alignTop: true, 2047 showCloseBtn: $this.settings.showCloseBtn, 2048 closeBtnInside: false, 2049 enableEscapeKey: true, 2050 closeOnBgClick: false, 2051 focus: 'input[type=search]', 2052 tLoading: '<i class="fas fa-spinner fa-spin fa-3x fa-fw"></i>', 2053 callbacks: { 2054 open: function(){ 2055 $this.input.trigger('click'); 2056 } 2057 } 2058 }); 2059 this.resultsEl.appendTo( inputWrap ); 2060 }, 2061 2062 _buttonClick: function(e) { 2063 e.stopImmediatePropagation(); 2064 if (this.searchForm.hasClass('fl-search-button-expand')) { 2065 this.searchForm.find('.fl-search-form-wrap').toggleClass('fl-search-expanded'); 2066 2067 if (this.searchForm.find('.fl-search-form-wrap').hasClass('fl-search-expanded')) { 2068 this.button.closest('.fl-button').attr('aria-expanded', true); 2069 this.input.attr('tabindex', 0); 2070 this.input.focus(); 2071 } 2072 else { 2073 this._hideResults(); 2074 this.input.attr('tabindex', -1); 2075 this.button.closest('.fl-button').attr('aria-expanded', false); 2076 } 2077 2078 return false;
2079 } else if (this.searchForm.hasClass('fl-search-button-reveal') && $(e.target).closest('.fl-button:not([type="submit"])')) { 2080 const form = this.searchForm.find('.fl-search-form-input-wrap'); 2081 if (e.detail && e.type === 'click' && form.is(':visible')) return false; 2082 form.fadeToggle('fast', () => { 2083 this.button.attr('aria-expanded', form.is(':visible')); 2084 if (form.is(':visible')) { 2085 this.input.focus(); 2086 } 2087 }); 2088 2089 return false; 2090 } else { 2091 this._search(e); 2092 } 2093 }, 2094 2095 _showResults: function(){ 2096 // Close any search results in a page. 2097 this._hideResults(); 2098 this.resultsEl.addClass('fl-search-open'); 2099 2100 if ('button' == this.settings.layout && 'expand' == this.settings.btnAction) { 2101 this.searchForm.find('.fl-search-form-input-wrap').css('overflow', 'visible'); 2102 } 2103 }, 2104 2105 _hideResults: function(){ 2106 $('.fl-search-results-content').removeClass('fl-search-open'); 2107 2108 if ('button' == this.settings.layout && 'expand' == this.settings.btnAction) { 2109 this.searchForm.find('.fl-search-form-input-wrap').removeAttr('style'); 2110 } 2111 }, 2112 2113 _doRedirectResults: function(){ 2114 // TODO 2115 }, 2116 2117 _showLoader: function(){ 2118 $(this.nodeClass + ' .fl-search-loader-wrap').show(); 2119 }, 2120 2121 _hideLoader: function(){ 2122 this.searching = false; 2123 $(this.nodeClass + ' .fl-search-loader-wrap').hide(); 2124 }, 2125 2126 _cleanInput: function(s) { 2127 return encodeURIComponent(s).replace(/%20/g, '+'); 2128 } 2129 2130 } 2131 2132})(jQuery); 2133(function($) { 2134 2135 $(function() { 2136 new FLBuilderSearchForm({ 2137 id: '613a41840bafb', 2138 layout: 'input', 2139 btnAction: 'expand', 2140 result: 'ajax', 2141 showCloseBtn: true, 2142 nonce: '3bd9264a44', 2143 }); 2144 }); 2145 2146})(jQuery); 2147 2148;/* eslint-disable no-undef */ 2149(function($) { 2150 2151 FLBuilderPostGrid = function(settings) 2152 { 2153 this.settings = settings; 2154 this.nodeClass = '.fl-node-' + settings.id; 2155 this.matchHeight = settings.matchHeight; 2156 2157 if ( 'columns' == this.settings.layout ) { 2158 this.wrapperClass = this.nodeClass + ' .fl-post-grid'; 2159 this.postClass = this.nodeClass + ' .fl-post-column'; 2160 } 2161 else { 2162 this.wrapperClass = this.nodeClass + ' .fl-post-' + this.settings.layout; 2163 this.postClass = this.wrapperClass + '-post'; 2164 } 2165 2166 if(this._hasPosts()) { 2167 this._initLayout(); 2168 this._initInfiniteScroll(); 2169 } 2170 }; 2171 2172 FLBuilderPostGrid.prototype = { 2173 2174 settings : {}, 2175 nodeClass : '', 2176 wrapperClass : '', 2177 postClass : '', 2178 gallery : null, 2179 currPage : 1, 2180 totalPages : 1, 2181 2182 _hasPosts: function() 2183 { 2184 return $(this.postClass).length > 0; 2185 }, 2186 2187 _initLayout: function() 2188 { 2189 switch(this.settings.layout) { 2190 2191 case 'columns': 2192 this._columnsLayout(); 2193 break; 2194 2195 case 'grid': 2196 this._gridLayout(); 2197 break; 2198 2199 case 'gallery': 2200 this._galleryLayout(); 2201 break; 2202 } 2203 2204 $(this.postClass).css('visibility', 'visible'); 2205 2206 FLBuilderLayout._scrollToElement( $( this.nodeClass + ' .fl-paged-scroll-to' ) ); 2207 }, 2208 2209 _columnsLayout: function() 2210 { 2211 $(this.wrapperClass).imagesLoaded( $.proxy( function() { 2212 this._gridLayoutMatchHeight(); 2213 }, this ) ); 2214 2215 $( window ).on( 'resize', $.proxy( function(){ 2216 $(this.wrapperClass).imagesLoaded( $.proxy( function() { 2217 this._gridLayoutMatchHeight(); 2218 }, this ) ); 2219 }, this ) ); 2220 }, 2221 2222 _gridLayout: function() 2223 { 2224 var wrap = $(this.wrapperClass); 2225 2226 wrap.masonry({ 2227 columnWidth : this.nodeClass + ' .fl-post-grid-sizer', 2228 gutter : parseInt(this.settings.postSpacing), 2229 isFitWidth : true, 2230 itemSelector : this.postClass, 2231 transitionDuration : 0, 2232 isRTL : this.settings.isRTL 2233 }); 2234 2235 wrap.imagesLoaded( $.proxy( function() { 2236 this._gridLayoutMatchHeight(); 2237 wrap.masonry(); 2238 }, this ) ); 2239 2240 $(window).scroll($.debounce( 25, function(){ 2241 wrap.masonry() 2242 })); 2243 2244 }, 2245 2246 _gridLayoutMatchHeight: function() 2247 { 2248 var highestBox = 0; 2249 2250 if ( ! this._isMatchHeight() ) { 2251 $(this.nodeClass + ' .fl-post-grid-post').css('height', ''); 2252 return; 2253 } 2254 2255 $(this.nodeClass + ' .fl-post-grid-post').css('height', '').each(function(){ 2256 2257 if($(this).height() > highestBox) {
2258 highestBox = $(this).height(); 2259 } 2260 }); 2261 2262 $(this.nodeClass + ' .fl-post-grid-post').height(highestBox); 2263 }, 2264 2265 _isMatchHeight: function(){ 2266 var width = $( window ).width(), 2267 breakpoints = FLBuilderLayoutConfig.breakpoints, 2268 matchLarge = '' != this.matchHeight.large ? this.matchHeight.large : this.matchHeight.default, 2269 matchMedium = '' != this.matchHeight.medium ? this.matchHeight.medium : this.matchHeight.default, 2270 matchSmall = '' != this.matchHeight.responsive ? this.matchHeight.responsive : this.matchHeight.default; 2271 2272 return (width > breakpoints.medium && 1 == this.matchHeight.default) // Must check medium here for backwards compat 2273 || (width > breakpoints.medium && width <= breakpoints.large && 1 == matchLarge) 2274 || (width > breakpoints.small && width <= breakpoints.medium && 1 == matchMedium) 2275 || (width <= breakpoints.small && 1 == matchSmall); 2276 }, 2277 2278 _galleryLayout: function() 2279 { 2280 this.gallery = new FLBuilderGalleryGrid({ 2281 'wrapSelector' : this.wrapperClass, 2282 'itemSelector' : '.fl-post-gallery-post', 2283 'isRTL' : this.settings.isRTL 2284 }); 2285 }, 2286 2287 _initInfiniteScroll: function() 2288 { 2289 var isScroll = 'scroll' == this.settings.pagination || 'load_more' == this.settings.pagination, 2290 pages = $( this.nodeClass + ' .fl-builder-pagination' ).find( 'li .page-numbers:not(.next)' ); 2291 2292 if( pages.length > 1) { 2293 total = pages.last().text().replace( /\D/g, '' ) 2294 this.totalPages = parseInt( total ); 2295 } 2296 2297 if( isScroll && this.totalPages > 1 && 'undefined' === typeof FLBuilder ) { 2298 this._infiniteScroll(); 2299 2300 if( 'load_more' == this.settings.pagination ) { 2301 this._infiniteScrollLoadMore(); 2302 } 2303 } 2304 }, 2305 2306 _infiniteScroll: function() 2307 { 2308 var path = $(this.nodeClass + ' .fl-builder-pagination a.next').attr('href'), 2309 pagePattern = /(.*?(\/|&|\?)paged-[0-9]{1,}(\/|=))([0-9]{1,})+(.*)/, 2310 wpPattern = /^(.*?\/?page\/?)(?:\d+)(.*?$)/, 2311 pageMatched = null, 2312 scrollData = { 2313 navSelector : this.nodeClass + ' .fl-builder-pagination', 2314 nextSelector : this.nodeClass + ' .fl-builder-pagination a.next', 2315 itemSelector : this.postClass, 2316 prefill : true, 2317 bufferPx : 200, 2318 loading : { 2319 msgText : this.settings.loadingText, 2320 finishedMsg : '', 2321 img : FLBuilderLayoutConfig.paths.pluginUrl + 'img/ajax-loader-grey.gif', 2322 speed : 1 2323 } 2324 }; 2325 // Define path since Infinitescroll incremented our custom pagination '/paged-2/2/' to '/paged-3/2/'. 2326 if ( pagePattern.test( path ) ) { 2327 scrollData.path = function( currPage ){ 2328 pageMatched = path.match( pagePattern ); 2329 path = pageMatched[1] + currPage + pageMatched[5]; 2330 return path; 2331 } 2332 } 2333 else if ( wpPattern.test( path ) ) { 2334 scrollData.path = path.match( wpPattern ).slice( 1 ); 2335 } 2336 2337 $(this.postClass).first().parent().infinitescroll( scrollData, $.proxy(this._infiniteScrollComplete, this) ); 2338 2339 setTimeout(function(){ 2340 $(window).trigger('resize'); 2341 }, 100); 2342 }, 2343 2344 _infiniteScrollComplete: function(elements) 2345 { 2346 var wrap = $(this.wrapperClass); 2347 2348 elements = $(elements); 2349 2350 if(this.settings.layout == 'columns') { 2351 wrap.imagesLoaded( $.proxy( function() { 2352 $( '#infscr-loading' ).remove(); 2353 this._gridLayoutMatchHeight(); 2354 elements.css('visibility', 'visible'); 2355 }, this ) ); 2356 } 2357 else if(this.settings.layout == 'grid') { 2358 wrap.imagesLoaded( $.proxy( function() { 2359 this._gridLayoutMatchHeight(); 2360 wrap.masonry('appended', elements); 2361 wrap.masonry(); 2362 elements.css('visibility', 'visible'); 2363 }, this ) ); 2364 } 2365 else if(this.settings.layout == 'gallery') { 2366 this.gallery.resize(); 2367 elements.css('visibility', 'visible'); 2368 } 2369 2370 if( 'load_more' == this.settings.pagination ) { 2371 $( this.wrapperClass + ' .fl-post-grid-sizer.masonry-brick' ).appendTo( this.wrapperClass ); 2372 $( '#infscr-loading' ).appendTo( this.wrapperClass ); 2373 } 2374 2375 this.currPage++; 2376 2377 this._removeLoadMoreButton(); 2378 2379 // trigger an event 2380 node = $(wrap).closest('.fl-module-post-grid').data('node') 2381 $('.fl-node-' + node).trigger( 'gridScrollComplete', this ); 2382 }, 2383 2384 _infiniteScrollLoadMore: function() 2385 { 2386 var wrap = $( this.postClass ).first().parent(); 2387 2388 $( window ).unbind( '.infscr' ); 2389
2390 $(this.nodeClass + ' .fl-builder-pagination-load-more .fl-button').on( 'click', function(){ 2391 if( $( '#infscr-loading' ).length ) { 2392 $( '#infscr-loading' ).remove(); 2393 } 2394 2395 wrap.infinitescroll( 'retrieve' ); 2396 return false; 2397 }); 2398 }, 2399 2400 _removeLoadMoreButton: function() 2401 { 2402 if ( 'load_more' == this.settings.pagination && this.totalPages == this.currPage ) { 2403 $( this.nodeClass + ' .fl-builder-pagination-load-more' ).remove(); 2404 } 2405 } 2406 }; 2407 2408})(jQuery); 2409(function($) { 2410 2411 $(function() { 2412 2413 new FLBuilderPostGrid({ 2414 id: '613a41840bb03', 2415 layout: 'columns', 2416 pagination: 'none', 2417 postSpacing: '30', 2418 postWidth: '300', 2419 matchHeight: { 2420 default : '0', 2421 large : '', 2422 medium : '', 2423 responsive : '' 2424 }, 2425 isRTL: false, 2426 loadingText: 'Loading...' 2427 }); 2428 2429 }); 2430 2431})(jQuery); 2432(function($) { 2433 2434 $(function() { 2435 2436 new FLBuilderPostGrid({ 2437 id: '613a41840bb0f', 2438 layout: 'columns', 2439 pagination: 'none', 2440 postSpacing: '30', 2441 postWidth: '300', 2442 matchHeight: { 2443 default : '0', 2444 large : '', 2445 medium : '', 2446 responsive : '' 2447 }, 2448 isRTL: false, 2449 loadingText: 'Loading...' 2450 }); 2451 2452 }); 2453 2454})(jQuery); 2455(function($) { 2456 2457 $(function() { 2458 2459 new FLBuilderPostGrid({ 2460 id: '613a41840bb16', 2461 layout: 'columns', 2462 pagination: 'none', 2463 postSpacing: '30', 2464 postWidth: '300', 2465 matchHeight: { 2466 default : '0', 2467 large : '', 2468 medium : '', 2469 responsive : '' 2470 }, 2471 isRTL: false, 2472 loadingText: 'Loading...' 2473 }); 2474 2475 }); 2476 2477})(jQuery); 2478(function($){ 2479 })(jQuery); 2480(function($){ 2481 })(jQuery); 2482(function($){ 2483 })(jQuery); 2484(function($){ 2485 $('.fl-node-613a41840baf3').each(function(){ 2486 var $this = $(this); 2487 $this.find('.fl-button-lightbox').magnificPopup({ 2488 2489 type: 'inline', 2490 items: { 2491 src: $this.find('.fl-button-lightbox-content')[0] 2492 }, 2493 callbacks: { 2494 open: function() { 2495 var content = $(this.content), 2496 divWrap = $(content[0]).find('> div'); 2497 2498 divWrap.css('display', 'block'); 2499 2500 // Triggers select change in we have multiple forms in a page 2501 if ( divWrap.find('form select').length > 0 ) { 2502 divWrap.find('form select').trigger('change'); 2503 } 2504 2505 // reload sliders. 2506 FLBuilderLayout.reloadSlider(content); 2507 FLBuilderLayout.resizeSlideshow(); 2508 }, 2509 }, 2510 closeBtnInside: true, 2511 fixedContentPos: true, 2512 tLoading: '<i class="fas fa-spinner fa-spin fa-3x fa-fw"></i>', 2513 }); 2514 }); 2515 })(jQuery); 2516 2517/* Start Global Node Custom JS */ 2518 2519/* End Global Node Custom JS */ 2520 2521 2522/* Start Layout Custom JS */ 2523document.addEventListener('DOMContentLoaded', function () { 2524 2525 // Blog post slider (single slide, dots) 2526 const blogPostSwiperEl = document.querySelector('.blog-post-swiper'); 2527 if (blogPostSwiperEl) { 2528 new Swiper('.blog-post-swiper', { 2529 loop: true, 2530 slidesPerView: 1, 2531 spaceBetween: 0, 2532 pagination: { 2533 el: '.blog-post-swiper .swiper-pagination', 2534 clickable: true, 2535 }, 2536 }); 2537 } 2538 2539 // Blog category grid slider (2-row grid, custom nav icons) 2540 const categorySwiperEl = document.querySelector('.unstuck-blog-category-swiper'); 2541 if (categorySwiperEl) { 2542 new Swiper('.unstuck-blog-category-swiper', { 2543 slidesPerView: 1, 2544 loop: true, 2545 grid: { 2546 rows: 2, 2547 fill: 'row', 2548 }, 2549 spaceBetween: 30, 2550 navigation: { 2551 // Beaver Builder icon modules with these extra classes 2552 nextEl: '.unstuck-blog-category-next', 2553 prevEl: '.unstuck-blog-category-prev', 2554 }, 2555 2556 breakpoints: { 2557 640: { 2558 slidesPerView: 2, 2559 grid: { 2560 rows: 2, 2561 fill: 'row', 2562 }, 2563 }, 2564 1024: { 2565 slidesPerView: 2, 2566 grid: { 2567 rows: 2, 2568 fill: 'row', 2569 }, 2570 }, 2571 }, 2572 }); 2573 } 2574 2575}); 2576 2577/* End Layout Custom JS */ 2578 2579 2580 (function($){ 2581 $('.fl-node-6166f998914b5').each(function(){ 2582 var $this = $(this); 2583 $this.find('#fl-node-6166f998914b5-1 .fl-button-lightbox').magnificPopup({ 2584 2585 type: 'inline', 2586 callbacks: { 2587 elementParse: function(item) { 2588 item.src = $( item.el ).closest('.fl-button-wrap').find('.fl-button-lightbox-content'); 2589 }, 2590 open: function() { 2591 var divWrap = $( $(this.content)[0] ).find('> div'); 2592 divWrap.css('display', 'block'); 2593 2594 // Triggers select change in we have multiple forms in a page 2595 if ( divWrap.find('form select').length > 0 ) { 2596 divWrap.find('form select').trigger('change'); 2597 } 2598 } 2599 }, 2600 closeBtnInside: true, 2601 fixedContentPos: true, 2602 tLoading: '<i class="fas fa-spinner fa-spin fa-3x fa-fw"></i>' 2603 }); 2604 }); 2605 })(jQuery); 2606 jQuery(function($) { 2607 2608 $(function() { 2609 $( '.fl-node-xnztwfgjbr49 .fl-photo-img' ) 2610 .on( 'mouseenter', function( e ) { 2611 $( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' ); 2612 } ) 2613 .on( 'mouseleave', function( e ){ 2614 $( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null ); 2615 } ); 2616 }); 2617 window._fl_string_to_slug_regex = 'a-zA-Z0-9'; 2618}); 2619 2620;/* eslint-disable no-undef */ 2621(function($) { 2622 2623 /** 2624 * Class for Menu Module 2625 * 2626 * @since 1.6.1 2627 */ 2628 FLBuilderMenu = function( settings ){ 2629 2630 // set params 2631 this.nodeId = settings.id; 2632 this.nodeClass = '.fl-node-' + settings.id; 2633 this.wrapperClass = this.nodeClass + ' .fl-menu'; 2634 this.type = settings.type; 2635 this.mobileToggle = settings.mobile; 2636 this.mobileBelowRow = settings.mobileBelowRow; 2637 this.mobileFlyout = settings.mobileFlyout; 2638 this.breakPoints = settings.breakPoints; 2639 this.mobileBreakpoint = settings.mobileBreakpoint; 2640 this.currentBrowserWidth = $( window ).width(); 2641 this.postId = settings.postId; 2642 this.mobileStacked = settings.mobileStacked; 2643 this.submenuIcon = settings.submenuIcon; 2644 this.flyoutWidth = settings.flyoutWidth; 2645 2646 // initialize the menu 2647 this._initMenu(); 2648 2649 // check if viewport is resizing 2650 $( window ).on( 'resize', $.proxy( function() { 2651 2652 var width = $( window ).width(); 2653 2654 // if screen width is resized, reload the menu 2655 if( width != this.currentBrowserWidth ){ 2656 2657 this.currentBrowserWidth = width; 2658 this._initMenu( true ); 2659 this._clickOrHover( true ); 2660 } 2661 2662 this._resizeFlyoutMenuPanel(); 2663 }, this ) ); 2664 2665 $( window ).on( 'scroll', $.proxy( function() { 2666 this._resizeFlyoutMenuPanel(); 2667 }, this ) ); 2668 2669 // Move focus to the first menu item if the layout is set to BelowRow 2670 $( this.wrapperClass ).on( 'keydown', '.fl-menu-mobile-toggle, .menu-item:first a:first', $.proxy( function ( event ) { 2671 // Make sure it is a tab key with both the menu toggle enabled & below row layout set 2672 if ( event.key !== 'Tab' || ! this._isMenuToggle() || ! this._isMobileBelowRowEnabled() ) { 2673 return; 2674 } 2675 // Trigger focus on the mobile toggle button if the user is shift tabbing from the first menu item 2676 if ( event.shiftKey && $( event.currentTarget ).is( 'a' ) ) { 2677 event.preventDefault(); 2678 $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ).trigger( 'focus' ); 2679 } 2680 // Trigger focus on the first menu item if the user is tabbing from the mobile toggle button 2681 else if ( ! event.shiftKey && $( event.currentTarget ).hasClass( 'fl-menu-mobile-toggle' ) && $( event.currentTarget ).hasClass( 'fl-active' ) ) { 2682 event.preventDefault(); 2683 $( this.wrapperClass ).find( '.menu-item' ).first().find( 'a' ).first().trigger( 'focus' ); 2684 } 2685 }, this ) ); 2686 2687 // Close mobile menu or accordion when tabbing out from the first or last menu item 2688 $( this.wrapperClass ).on( 'focusout', $.proxy( function ( event ) { 2689 if ( $( this.wrapperClass + ' nav' ).has( $( event.relatedTarget ) ).length === 0 ) { 2690 if ( this.type === 'accordion' ) { 2691 this._toggleSubmenu( $( event.target ).parents( '.fl-has-submenu' ).last(), false ); 2692 } 2693 else { 2694 this._clickOrHover( true ); 2695 } 2696 const mobileToggle = $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ); 2697 if ( this._isMenuToggle() && mobileToggle.hasClass( 'fl-active' ) && ! $( event.relatedTarget ).is( mobileToggle ) && 'expanded' !== this.mobileToggle ) { 2698 mobileToggle.trigger( 'click' ); 2699 } 2700 } 2701 }, this ) ); 2702 2703 }; 2704 2705 FLBuilderMenu.prototype = { 2706 nodeClass : '', 2707 wrapperClass: '', 2708 type : '', 2709 breakPoints : {}, 2710 $submenus : null, 2711 2712 /** 2713 * Check if the screen size fits a mobile viewport. 2714 * 2715 * @since 1.6.1 2716 * @return bool 2717 */ 2718 _isMobile: function(){ 2719 return this.currentBrowserWidth <= this.breakPoints.small ? true : false;
2720 }, 2721 2722 /** 2723 * Check if the device is connected to a mouse. 2724 * 2725 * @since 2.9.0 2726 * @return bool 2727 */ 2728 _isMouseAvailable: function(){ 2729 return window.matchMedia("(pointer: fine) and (hover: hover)").matches; 2730 }, 2731 2732 /** 2733 * Check if the screen size fits a medium viewport. 2734 * 2735 * @since 1.10.5 2736 * @return bool 2737 */ 2738 _isMedium: function(){ 2739 return this.currentBrowserWidth <= this.breakPoints.medium ? true : false; 2740 }, 2741 2742 /** 2743 * Check if the screen size fits a large viewport. 2744 * 2745 * @since 1.10.5 2746 * @return bool 2747 */ 2748 _isLarge: function(){ 2749 return this.currentBrowserWidth <= this.breakPoints.large ? true : false; 2750 }, 2751 2752 /** 2753 * Check if the menu should toggle for the current viewport base on the selected breakpoint. 2754 * 2755 * @see this._isMobile() 2756 * @see this._isMedium() 2757 * @since 1.10.5 2758 * @return bool 2759 */ 2760 _isMenuToggle: function(){ 2761 if ( ( 'always' == this.mobileBreakpoint 2762 || ( this._isMobile() && 'mobile' == this.mobileBreakpoint ) 2763 || ( this._isMedium() && 'medium-mobile' == this.mobileBreakpoint ) 2764 || ( this._isLarge() && 'large-mobile' == this.mobileBreakpoint ) 2765 ) && ( $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ).is(':visible') || 'expanded' == this.mobileToggle ) ) { 2766 return true; 2767 } 2768 2769 return false; 2770 }, 2771 2772 /** 2773 * Initialize the toggle logic for the menu. 2774 * 2775 * @see this._isMenuToggle() 2776 * @see this._menuOnCLick() 2777 * @see this._clickOrHover() 2778 * @see this._submenuOnRight() 2779 * @see this._submenuRowZindexFix() 2780 * @see this._toggleForMobile() 2781 * @since 1.6.1 2782 * @return void 2783 */ 2784 _initMenu: function( resized = false ){ 2785 this._setupSubmenu(); 2786 if ( ! resized ) { 2787 this._menuOnFocus(); 2788 this._menuOnClick(); 2789 this._menuOnEscape(); 2790 } 2791 if ( $( this.nodeClass ).length && this.type == 'horizontal' ) { 2792 this._initMegaMenus(); 2793 } 2794 2795 if( this._isMenuToggle() || this.type == 'accordion' ){ 2796 2797 $( this.wrapperClass ).off( 'mouseenter mouseleave' ); 2798 this._clickOrHover(); 2799 2800 } else { 2801 this._submenuOnRight(); 2802 this._submenuRowZindexFix(); 2803 } 2804 2805 if( this.mobileToggle != 'expanded' ){ 2806 this._toggleForMobile(); 2807 } 2808 2809 if( $( this.wrapperClass ).find( '.fl-menu-search-item' ).length ){ 2810 this._toggleMenuSearch(); 2811 } 2812 2813 if( $( this.wrapperClass ).find( '.fl-menu-cart-item').length ){ 2814 this._wooUpdateParams(); 2815 } 2816 }, 2817 2818 /** 2819 * Whether the toggle behavior for submenus is through hovering or not. 2820 * 2821 * @since 2.5 2822 * @return void 2823 */ 2824 _setupSubmenu: function() { 2825 if ( ! this._isMouseAvailable() || this._isMenuToggle() || this.type === 'accordion' ) { 2826 $( this.wrapperClass ).addClass( 'no-hover' ); 2827 } 2828 else { 2829 $( this.wrapperClass ).removeClass( 'no-hover' ); 2830 } 2831 }, 2832 2833 /** 2834 * Logic for menu items when focused. 2835 * 2836 * @since 1.9.0 2837 * @return void 2838 */ 2839 _menuOnFocus: function(){ 2840 $( this.wrapperClass ).on( 'focus', 'a:not(.fl-button), .fl-menu-toggle', $.proxy( function( event ) { 2841 const focusedMenuItem = $( event.currentTarget ).closest( '.menu-item' ); 2842 const blurredMenuItem = $( event.relatedTarget ).closest( '.menu-item' ); 2843 // In case the blurred & focused items are siblings 2844 if ( focusedMenuItem.closest( 'ul' ).is( blurredMenuItem.closest( 'ul' ) ) ) { 2845 // Check if the focus event is not between the link & submenu icon of the same menu item 2846 if ( ! focusedMenuItem.is( blurredMenuItem ) ) { 2847 this._toggleSubmenu( blurredMenuItem, false ); 2848 } 2849 } 2850 // In case neither items are child & parent nor vice versa 2851 else if ( focusedMenuItem.has( blurredMenuItem ).length === blurredMenuItem.has( focusedMenuItem ).length ) { 2852 // Get the blurred item parent & is the sibling of the focused item 2853 blurredMenuItem.parents( '.fl-has-submenu' ).each( ( _, parent ) => { 2854 if ( focusedMenuItem.closest( 'ul' ).is( $( parent ).closest( 'ul' ) ) ) { 2855 this._toggleSubmenu( $( parent ), false ); 2856 return false; 2857 } 2858 } ); 2859 } 2860 // In case the blurred item is a child of the focused item
2861 else if ( focusedMenuItem.has( blurredMenuItem ).length ) { 2862 if ( this.type !== 'accordion' && this.submenuIcon === 'none' ) { 2863 this._toggleSubmenu( blurredMenuItem, false ); 2864 } 2865 } 2866 }, this ) ); 2867 }, 2868 2869 /** 2870 * Logic for menu items when Escape key is pressed. 2871 * 2872 * @since 2.7.1 2873 * @return void 2874 */ 2875 _menuOnEscape: function(){ 2876 $( this.wrapperClass ).on( 'keydown', $.proxy( function( event ){ 2877 if ( event.key !== 'Escape' ) return; 2878 const menuItem = $( event.target ).closest( '.menu-item' ); 2879 const mobileToggle = $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ); 2880 // In case there is a focused menu item 2881 if ( menuItem.length || $( event.target ).hasClass( 'fl-menu-mobile-close' ) ) { 2882 // Close the search form if it is open 2883 if ( menuItem.hasClass( 'fl-menu-search-item' ) && $( this.wrapperClass ).find('.fl-search-form-input-wrap').is( ':visible' ) ) { 2884 $( this.wrapperClass ).find('.fl-menu-search-item .fl-button:is(a, button:not([type="submit"]))').trigger( 'focus' ); 2885 } 2886 // Close all the submenus of the focused menu item 2887 else if ( menuItem.hasClass( 'fl-has-submenu' ) && menuItem.find( '.sub-menu' ).first().is( ':visible' ) ) { 2888 this._toggleSubmenu( menuItem, false ); 2889 } 2890 else { 2891 // Shift focus to the parent while closing all its submenus 2892 const parentMenuItem = menuItem.parents( '.fl-has-submenu' ).not( '.mega-menu.hide-heading, .mega-menu-disabled.hide-heading' ).first(); 2893 // Close the mobile menu if there is no parent menu item and the mobile toggle is active 2894 if ( parentMenuItem.length === 0 && this._isMenuToggle() && mobileToggle.hasClass( 'fl-active' ) ) { 2895 // Blur to trigger the focus-out event for closing the menu 2896 mobileToggle.trigger( 'blur' ).trigger( 'focus' ); 2897 } 2898 // Close the parent submenu and shift focus to its link 2899 else if ( parentMenuItem.length !== 0 ) { 2900 this._toggleSubmenu( parentMenuItem, false ); 2901 parentMenuItem.find( 'a' ).first().trigger( 'focus' ); 2902 } 2903 } 2904 } 2905 // Close the mobile menu if the mobile toggle is active 2906 else if ( $( event.target ).hasClass( 'fl-menu-mobile-toggle' ) && mobileToggle.hasClass( 'fl-active' ) ) { 2907 // Blur to trigger the focus-out event for closing the menu 2908 mobileToggle.trigger( 'blur' ).trigger( 'focus' ); 2909 } 2910 }, this ) ); 2911 }, 2912 2913 /** 2914 * Logic for menu items when clicked. 2915 * 2916 * @since 1.6.1 2917 * @return void 2918 */ 2919 _menuOnClick: function(){ 2920 // Fallback for span elements and fake links to interact with the keyboard 2921 $( this.wrapperClass ).on( 'keydown', 'a:not(.fl-button, [href]), span.fl-menu-toggle', $.proxy( function( event ) { 2922 if ( event.key === 'Enter' || event.key === ' ' ) { 2923 event.preventDefault(); 2924 $( event.currentTarget ).trigger( 'click' ); 2925 } 2926 }, this ) ); 2927 $( this.wrapperClass ).on( 'click', 'a, .fl-menu-toggle', $.proxy( function( event ) { 2928 // Only allow mouse clicks with accordion & mobile menus 2929 if ( this._isMouseAvailable() && ! this._isMenuToggle() && event.detail && this.type !== 'accordion' ) return; 2930 // Links only open & do not toggle submenus if there is either a submenu icon or an accordion layout 2931 if ( $( event.currentTarget ).is( 'a' ) && ( this.submenuIcon !== 'none' || this.type === 'accordion' ) ) return; 2932 const menuItem = $( event.currentTarget ).closest( '.menu-item, .fl-menu-logo' ); 2933 const menuLink = menuItem.find( 'a' ).first().attr( 'href' ); 2934 const submenuHidden = menuItem.find( '.sub-menu' ).first().is( ':hidden' ); 2935 if ( typeof menuLink === 'undefined' || menuLink === '#' || submenuHidden ) { 2936 event.preventDefault(); 2937 } 2938 if ( $( event.currentTarget ).hasClass( 'fl-menu-toggle' ) || submenuHidden ) { 2939 this._toggleSubmenu( menuItem, submenuHidden ); 2940 } 2941 }, this ) ); 2942 }, 2943 2944 /** 2945 * Controls toggling the visibility of all submenu items for desktop & mobile. 2946 * 2947 * @since 2.10 2948 * @return void 2949 */ 2950 _toggleSubmenu: function ( menuItem, opened ) { 2951 const togglingClass = this._isMenuToggle() || this.type === 'accordion' ? 'fl-active' : 'focus'; 2952 const toggleElement = this.submenuIcon === 'none' ? 'a' : '.fl-menu-toggle'; 2953 const hiddenMenu = '.mega-menu.hide-heading, .mega-menu-disabled.hide-heading'; 2954 if ( opened && menuItem.hasClass( 'fl-has-submenu' ) && ! menuItem.is( hiddenMenu ) ) { 2955 menuItem.addClass( togglingClass ); 2956 menuItem.find( toggleElement ).first().attr( 'aria-expanded', true ); 2957 if ( this._isMenuToggle() || this.type === 'accordion' ) { 2958 menuItem.find( '.sub-menu' ).first().filter( ':hidden' ).slideDown(); 2959 } 2960 } 2961 else { 2962 menuItem.parent().find( '.menu-item' ).removeClass( togglingClass ); 2963 menuItem.parent().find( '.fl-has-submenu' ).not( hiddenMenu ).find( toggleElement ).attr( 'aria-expanded', false ); 2964 if ( this._isMenuToggle() || this.type === 'accordion' ) {
2965 menuItem.find( '.sub-menu' ).filter( ':visible' ).slideUp(); 2966 } 2967 } 2968 }, 2969 2970 /** 2971 * Changes general styling and behavior of menus based on mobile / desktop viewport. 2972 * 2973 * @see this._isMenuToggle() 2974 * @since 1.6.1 2975 * @return void 2976 */ 2977 _clickOrHover: function( clear = false ){ 2978 const selector = this._isMobileBelowRowEnabled() ? this.nodeClass + '-clone' : this.nodeClass; 2979 this.$submenus = this.$submenus || $( selector ).find( '.sub-menu' ); 2980 2981 const className = this._isMenuToggle() || this.type === 'accordion' ? 'fl-active' : 'focus'; 2982 const toggleElement = this.submenuIcon === 'none' ? 'a' : '.fl-menu-toggle'; 2983 const hiddenMenu = '.mega-menu.hide-heading, .mega-menu-disabled.hide-heading'; 2984 $( selector ).find( '.fl-has-submenu' ).not( hiddenMenu ).each( function() { 2985 if( clear || ! $ ( this ).hasClass( className ) ){ 2986 if ( clear ) $( this ).removeClass( className ); 2987 $( this ).find( toggleElement ).first().attr( 'aria-expanded', false ); 2988 if ( className === 'fl-active' ) { 2989 $( this ).find( '.sub-menu' ).fadeOut(); 2990 } else if ( className === 'focus' ) { 2991 $( this ).find( '.sub-menu' ).css( { 2992 'display' : '', 2993 'opacity' : '' 2994 } ); 2995 } 2996 } 2997 } ); 2998 }, 2999 3000 /** 3001 * Logic to prevent submenus to go outside viewport boundaries. 3002 * 3003 * @since 1.6.1 3004 * @return void 3005 */ 3006 _submenuOnRight: function(){ 3007 3008 $( this.wrapperClass ) 3009 .on( 'mouseenter focus', '.fl-has-submenu', $.proxy( function( e ){ 3010 3011 if( $ ( e.currentTarget ).find('.sub-menu').length === 0 ) { 3012 return; 3013 } 3014 3015 var $link = $( e.currentTarget ), 3016 $parent = $link.parent(), 3017 $subMenu = $link.find( '.sub-menu' ), 3018 subMenuWidth = $subMenu.width(), 3019 subMenuPos = 0, 3020 bodyWidth = $( 'body' ).width(); 3021 3022 if( $link.closest( '.fl-menu-submenu-right' ).length !== 0) { 3023 3024 $link.addClass( 'fl-menu-submenu-right' ); 3025 3026 } else if( $( 'body' ).hasClass( 'rtl' ) ) { 3027 3028 subMenuPos = $parent.is( '.sub-menu' ) ? 3029 $parent.offset().left - subMenuWidth: 3030 $link.offset().left - $link.width() - subMenuWidth; 3031 3032 if( subMenuPos <= 0 ) { 3033 $link.addClass( 'fl-menu-submenu-right' ); 3034 } 3035 3036 } else { 3037 3038 subMenuPos = $parent.is( '.sub-menu' ) ? 3039 $parent.offset().left + $parent.width() + subMenuWidth : 3040 $link.offset().left + $link.width() + subMenuWidth; 3041 3042 if( subMenuPos > bodyWidth ) { 3043 $link.addClass('fl-menu-submenu-right'); 3044 } 3045 } 3046 }, this ) ) 3047 .on( 'mouseleave', '.fl-has-submenu', $.proxy( function( e ){ 3048 $( e.currentTarget ).removeClass( 'fl-menu-submenu-right' ); 3049 }, this ) ); 3050 3051 }, 3052 3053 /** 3054 * Logic to prevent submenus to go behind the next overlay row. 3055 * 3056 * @since 1.10.9 3057 * @return void 3058 */ 3059 _submenuRowZindexFix: function(){ 3060 3061 $( this.wrapperClass ) 3062 .on( 'mouseenter', 'ul.menu > .fl-has-submenu', $.proxy( function( e ){ 3063 3064 if( $ ( e.currentTarget ).find('.sub-menu').length === 0 ) { 3065 return; 3066 } 3067 3068 $( this.nodeClass ) 3069 .closest( '.fl-row' ) 3070 .find( '.fl-row-content' ) 3071 .css( 'z-index', '10' ); 3072 3073 }, this ) ) 3074 .on( 'mouseleave', 'ul.menu > .fl-has-submenu', $.proxy( function(){ 3075 3076 $( this.nodeClass ) 3077 .closest( '.fl-row' ) 3078 .find( '.fl-row-content' ) 3079 .css( 'z-index', '' ); 3080 3081 }, this ) ); 3082 }, 3083 3084 /** 3085 * Logic for the mobile menu button. 3086 * 3087 * @since 1.6.1 3088 * @return void 3089 */ 3090 _toggleForMobile: function(){ 3091 3092 var $wrapper = null, 3093 $menu = null, 3094 self = this; 3095 3096 $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ).attr('aria-controls', $( this.wrapperClass ).find( 'ul.menu' ).attr('id')); 3097 3098 if( this._isMenuToggle() ){ 3099 3100 if ( this._isMobileBelowRowEnabled() ) { 3101 this._placeMobileMenuBelowRow(); 3102 $wrapper = $( this.wrapperClass ); 3103 $menu = $( this.nodeClass + '-clone' ); 3104 $menu.find( 'ul.menu' ).show(); 3105 } 3106 else { 3107 $wrapper = $( this.wrapperClass ); 3108 $menu = $wrapper.find( '.menu' ); 3109 } 3110 3111 if( !$wrapper.find( '.fl-menu-mobile-toggle' ).hasClass( 'fl-active' ) && ! self.mobileFlyout ){ 3112 $menu.css({ display: 'none' }); 3113 } 3114 3115 // Flayout Menu 3116 if ( self.mobileFlyout ) { 3117 this._initFlyoutMenu(); 3118 } 3119 3120 $wrapper.on( 'click', '.fl-menu-mobile-toggle', function( e ){ 3121 e.stopImmediatePropagation(); 3122 3123 $( this ).toggleClass( 'fl-active' ); 3124 3125 if ( self.mobileFlyout ) { 3126 self._toggleFlyoutMenu(); 3127 const flyoutWrapper = $( '.fl-menu-mobile-flyout' ); 3128 if ( $( this ).hasClass( 'fl-active' ) ) { 3129 flyoutWrapper.attr( 'aria-hidden', false ); 3130 flyoutWrapper.find( 'a[href], button, input, select, textarea, span.fl-menu-toggle, [tabindex="-1"]' ).attr( 'tabindex', 0 ); 3131 } 3132 else { 3133 flyoutWrapper.attr( 'aria-hidden', true ); 3134 flyoutWrapper.find( 'a[href], button, input, select, textarea, span.fl-menu-toggle, [tabindex]:not
3134([tabindex="-1"])' ).attr( 'tabindex', -1 ); 3135 } 3136 } 3137 else { 3138 var targetMenu = null; 3139 3140 if ( self.mobileBelowRow ) { 3141 var $closestCol = $( this ).parents( '.fl-col, .fl-module-box' ), 3142 $closestColGroup = $closestCol.length ? $closestCol.parent( '.fl-col-group' ) : null; 3143 targetMenu = $closestCol.length ? $closestCol.last().next( '.fl-menu-mobile-clone' ) : null; 3144 3145 if ( $closestColGroup.length ) { 3146 if ( $closestColGroup.hasClass( 'fl-col-group-responsive-reversed' ) ) { 3147 $closestColGroup.find( '.fl-menu-mobile-clone' ).css( 'order', -1 ); 3148 } else if ( $closestColGroup ) { 3149 $closestColGroup.find( '.fl-menu-mobile-clone' ).css( 'order', 2 ); 3150 } 3151 } 3152 } else { 3153 targetMenu = $( this ).closest( '.fl-menu' ).find( 'ul.menu' ); 3154 } 3155 3156 if ( targetMenu.length ) { 3157 $menu = $( targetMenu ); 3158 } 3159 3160 $menu.slideToggle(); 3161 } 3162 3163 e.stopPropagation(); 3164 } ); 3165 3166 // Hide active menu when click on anchor link ID that exists on a page 3167 $menu.off().on( 'click', '.menu-item > a[href*="#"]:not([href="#"])', function(){ 3168 var $href = $(this).attr('href'), 3169 $targetID = $href.split('#')[1], 3170 element = $('#' + $targetID); 3171 if ( $('body').find(element).length > 0 ) { 3172 $( this ).toggleClass( 'fl-active' ); 3173 FLBuilderLayout._scrollToElement( element ); 3174 if ( ! self._isMenuToggle() ) { 3175 $menu.slideToggle(); 3176 } 3177 } 3178 }); 3179 } 3180 else { 3181 3182 if ( this._isMobileBelowRowEnabled() ) { 3183 this._removeMenuFromBelowRow(); 3184 } 3185 3186 $wrapper = $( this.wrapperClass ), 3187 $menu = $wrapper.find( 'ul.menu' ); 3188 $wrapper.find( '.fl-menu-mobile-toggle' ).removeClass( 'fl-active' ); 3189 $menu.css({ display: '' }); 3190 3191 if ( ! this._isMobileBelowRowEnabled() ) { 3192 $menu.off( 'click', '.menu-item > a[href*="#"]:not([href="#"])' ); 3193 } 3194 3195 if ( this.mobileFlyout && $wrapper.find( '.fl-menu-mobile-flyout' ).length > 0 ) { 3196 $( 'body' ).css( 'margin', '' ); 3197 $( '.fl-builder-ui-pinned-content-transform' ).css( 'transform', '' ); 3198 $menu.unwrap(); 3199 $wrapper.find( '.fl-menu-mobile-close' ).remove(); 3200 $wrapper.find( '.fl-menu-mobile-opacity' ).remove(); 3201 } 3202 } 3203 }, 3204 3205 /** 3206 * Init any mega menus that exist. 3207 * 3208 * @see this._isMenuToggle() 3209 * @since 1.10.4 3210 * @return void 3211 */ 3212 _initMegaMenus: function(){ 3213 3214 var module = $( this.nodeClass ), 3215 rowContent = module.closest( '.fl-row-content' ), 3216 rowWidth = rowContent.width(), 3217 megas = module.find( '.mega-menu' ), 3218 disabled = module.find( '.mega-menu-disabled' ), 3219 isToggle = this._isMenuToggle(); 3220 3221 if ( isToggle ) { 3222 megas.removeClass( 'mega-menu' ).addClass( 'mega-menu-disabled' ); 3223 module.find( 'li.mega-menu-disabled > ul.sub-menu' ).css( 'width', '' ); 3224 rowContent.css( 'position', '' ); 3225 } else { 3226 disabled.removeClass( 'mega-menu-disabled' ).addClass( 'mega-menu' ); 3227 module.find( 'li.mega-menu > ul.sub-menu' ).css( 'width', rowWidth + 'px' ); 3228 rowContent.css( 'position', 'relative' ); 3229 } 3230 }, 3231 3232 /** 3233 * Check to see if Below Row should be enabled. 3234 * 3235 * @since 1.11 3236 * @return boolean 3237 */ 3238 _isMobileBelowRowEnabled: function() { 3239 return this.mobileBelowRow && ( $( this.nodeClass ).parents( '.fl-col, .fl-module-box' ).length ); 3240 }, 3241 3242 /** 3243 * Logic for putting the mobile menu below the menu's 3244 * column so it spans the full width of the page. 3245 * 3246 * @since 1.10 3247 * @return void 3248 */ 3249 _placeMobileMenuBelowRow: function(){ 3250 3251 if ( $( this.nodeClass + '-clone' ).length ) { 3252 return; 3253 } 3254 3255 var module = $( this.nodeClass ), 3256 clone = null, 3257 col = module.parents( '.fl-col, .fl-module-box' ).last(); 3258 3259 if ( module.length < 1 ) { 3260 return; 3261 } 3262 3263 clone = ( module.length > 1 ) ? $( module[0] ).clone() : module.clone(); 3264 module.find( 'ul.menu' ).remove(); 3265 clone.addClass( ( this.nodeClass + '-clone' ).replace( '.', '' ) ); 3266 clone.addClass( 'fl-menu-mobile-clone' ); 3267 clone.find( '.fl-menu-mobile-toggle' ).remove(); 3268 col.after( clone ); 3269 3270 // Removes animation when enabled 3271 if ( module.hasClass( 'fl-animation' ) ) { 3272 clone.removeClass( 'fl-animation' ); 3273 } 3274 3275 this._menuOnFocus(); 3276 this._menuOnClick(); 3277 this._menuOnEscape(); 3278 }, 3279 3280 /** 3281 * Logic for removing the mobile menu from below the menu's 3282 * column and putting it back in the main wrapper. 3283 * 3284 * @since 1.10 3285 * @return void 3286 */ 3287 _removeMenuFromBelowRow: function(){ 3288 3289 if ( ! $( this.nodeClass + '-clone' ).length ) { 3290 return; 3291 } 3292 3293 var module = $( this.nodeClass ), 3294 clone = $( this.nodeClass + '-clone' ), 3295 menu = clone.find( 'ul.menu' ), 3296 nav = module.find( 'nav' ); 3297
3298 if ( nav.length ) { 3299 nav.append( menu ); 3300 } else { 3301 module.find( '.fl-menu-mobile-toggle' ).after( menu ); 3302 } 3303 clone.remove(); 3304 menu.find( 'a' ).each( FLBuilderLayout._initAnchorLink ); 3305 }, 3306 3307 /** 3308 * Logic for Flyout responsive menu. 3309 * 3310 * @since 2.2 3311 * @return void 3312 */ 3313 _initFlyoutMenu: function(){ 3314 var wrapper = $( this.wrapperClass ), 3315 menu = wrapper.find( 'ul.menu' ), 3316 button = wrapper.find( '.fl-menu-mobile-toggle' ); 3317 3318 if ( 0 === wrapper.find( '.fl-menu-mobile-flyout' ).length ) { 3319 menu.wrap( '<div class="fl-menu-mobile-flyout" aria-hidden="true"></div>' ); 3320 } 3321 3322 if ( 0 === wrapper.find( '.fl-menu-mobile-close' ).length ) { 3323 var close = window.fl_responsive_close || 'Close' 3324 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>' ); 3325 } 3326 3327 // Push with opacity 3328 if ( wrapper.hasClass( 'fl-menu-responsive-flyout-push-opacity' ) && 0 === wrapper.find( '.fl-menu-mobile-opacity' ).length ) { 3329 wrapper.append( '<div class="fl-menu-mobile-opacity"></div>' ); 3330 } 3331 3332 wrapper.off( 'click', '.fl-menu-mobile-opacity, .fl-menu-mobile-close' ).on( 'click', '.fl-menu-mobile-opacity, .fl-menu-mobile-close', function(e){ 3333 button.trigger('focus').trigger( 'click' ); 3334 e.stopPropagation(); 3335 }); 3336 3337 if ( 'undefined' !== typeof FLBuilder ) { 3338 FLBuilder.addHook('restartEditingSession', function(){ 3339 $( '.fl-builder-ui-pinned-content-transform' ).css( 'transform', '' ); 3340 3341 // Toggle active menu 3342 if ( button.hasClass( 'fl-active' ) ) { 3343 button.trigger( 'click' ); 3344 } 3345 }); 3346 } 3347 $( '.fl-menu-mobile-flyout' ).find( 'a[href], button, input, select, textarea, span.fl-menu-toggle, [tabindex]:not([tabindex="-1"])' ).attr( 'tabindex', -1 ); 3348 }, 3349 3350 /** 3351 * Logic to enable/disable the Flyout menu on button click. 3352 * 3353 * @since 2.2 3354 * @return void 3355 */ 3356 _toggleFlyoutMenu: function(){ 3357 var wrapper = $( this.wrapperClass ), 3358 button = wrapper.find( '.fl-menu-mobile-toggle' ), 3359 position = wrapper.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 3360 pushMenu = wrapper.hasClass( 'fl-menu-responsive-flyout-push' ) || wrapper.hasClass( 'fl-menu-responsive-flyout-push-opacity' ), 3361 opacity = wrapper.find( '.fl-menu-mobile-opacity' ), 3362 marginPos = {}, 3363 fixedPos = {}, 3364 fixedHeader = $('header, header > div'); 3365 3366 this._resizeFlyoutMenuPanel(); 3367 3368 // Fix the push menu when builder ui panel is pinned 3369 if ( $( '.fl-builder-ui-pinned-content-transform' ).length > 0 && ! $( 'body' ).hasClass( 'fl-builder-edit' ) ) { 3370 $( '.fl-builder-ui-pinned-content-transform' ).css( 'transform', 'none' ); 3371 } 3372 3373 if ( pushMenu ) { 3374 marginPos[ 'margin-' + position ] = button.hasClass( 'fl-active' ) ? this.flyoutWidth + 'px' : '0px'; 3375 $( 'body' ).animate( marginPos, 200); 3376 3377 // Fixed header 3378 if ( fixedHeader.length > 0 ) { 3379 fixedPos[ position] = button.hasClass( 'fl-active' ) ? this.flyoutWidth + 'px' : '0px'; 3380 fixedHeader.each(function(){ 3381 if ( 'fixed' == $( this ).css( 'position' ) ) { 3382 $( this ).css({ 'transition' : 'none' }); 3383 $( this ).animate( fixedPos, 200 ); 3384 } 3385 }); 3386 } 3387 } 3388 3389 if ( opacity.length > 0 && button.hasClass( 'fl-active' ) ) { 3390 opacity.show(); 3391 } 3392 else { 3393 opacity.hide(); 3394 } 3395 }, 3396 3397 /** 3398 * Resize or reposition the Flyout Menu Panel. 3399 * 3400 * @since 2.8.1 3401 * @return void 3402 */ 3403 _resizeFlyoutMenuPanel: function(){ 3404 const wrapper = $( this.wrapperClass ); 3405 const wrapFlyout = wrapper.find( '.fl-menu-mobile-flyout' ); 3406 3407 if ( wrapFlyout.length > 0 ) { 3408 wrapFlyout.css( this._getFlyoutMenuPanelPosition() ); 3409 } 3410 }, 3411 3412 /** 3413 * Compute the Flyout Menu Panel's position on the screen. 3414 * 3415 * @since 2.8.1 3416 * @return object 3417 */ 3418 _getFlyoutMenuPanelPosition: function() { 3419 var wrapper = $( this.wrapperClass ), 3420 button = wrapper.find( '.fl-menu-mobile-toggle' ), 3421 side = wrapper.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 3422 winHeight = $(window).outerHeight(), 3423 winTop = $(window).scrollTop(), 3424 adminBarHeight = $( '#wpadminbar' ).length ? $( '#wpadminbar' ).height() : 0, 3425 flyoutPosition = {}; 3426 3427 flyoutPosition[ side ] = '-' + ( parseInt( this.flyoutWidth ) + 15 ) + 'px'; 3428 if ( ! button.hasClass( 'fl-active' ) ) { 3429 return flyoutPosition; 3430 } 3431 3432 flyoutPosition[ side ] = '0px'; 3433 flyoutPosition[ 'height' ] = winHeight + 'px'; 3434 flyoutPosition[ 'top' ] = '0px'; 3435 3436 if ( adminBarHeight > 0 ) { 3437 const diff = adminBarHeight - winTop; 3438 flyoutPosition[ 'top' ] = diff <= 0 ? '0px' : (diff) + 'px'; 3439 } 3440 3441 return flyoutPosition; 3442 }, 3443 3444 /** 3445 * Shows or hides the nav search form. 3446 * 3447 * @since 2.5 3448 * @method _toggleMenuSearch 3449 */ 3450 _toggleMenuSearch: function(){ 3451 const form = $( this.wrapperClass ).find('.fl-menu-search-item .fl-search-form-input-wrap');
3452 const button = $( this.wrapperClass ).find('.fl-menu-search-item .fl-button:is(a, button:not([type="submit"]))'); 3453 form.on('submit', () => form.stop().fadeToggle('fast')); 3454 // Close the search form when focus is lost 3455 form.on('focusout', (event) => { 3456 if (form.is(':visible') && form.has($(event.relatedTarget)).length === 0) { 3457 button.trigger('click'); 3458 } 3459 }); 3460 button.attr('aria-expanded', false); 3461 // Close other open submenus when focusing on the search button 3462 button.on('focus', (event) => { 3463 const parentMenus = $(event.relatedTarget).parents('.menu-item.fl-has-submenu'); 3464 if (event.relatedTarget && parentMenus.length) { 3465 this._toggleSubmenu(parentMenus, false); 3466 } 3467 }); 3468 button.on('click', (event) => { 3469 // Prevent toggling when clicking with mouse while the form is already open 3470 if (event.detail && event.type === 'click' && form.is(':visible')) return; 3471 form.stop().fadeToggle('fast', () => { 3472 button.attr('aria-expanded', form.is(':visible')); 3473 if(form.is(':visible')) { 3474 form.find('.fl-search-text').focus(); 3475 } 3476 }); 3477 }); 3478 }, 3479 3480 /** 3481 * Adds menu node and post ID to WooCommerce ajax URL requests. 3482 * 3483 * @since 2.5 3484 * @return void 3485 */ 3486 _wooUpdateParams: function() { 3487 if ( 'undefined' !== typeof wc_cart_fragments_params ) { 3488 wc_cart_fragments_params.wc_ajax_url += '&fl-menu-node='+ this.nodeId +'&post-id='+ this.postId; 3489 } 3490 if ( 'undefined' !== typeof wc_add_to_cart_params ) { 3491 wc_add_to_cart_params.wc_ajax_url += '&fl-menu-node='+ this.nodeId +'&post-id='+ this.postId; 3492 } 3493 }, 3494 }; 3495 3496})(jQuery); 3497 3498(function($) { 3499 3500 $(function() { 3501 3502 new FLBuilderMenu({ 3503 id: '6166f42df0bd2', 3504 type: 'vertical', 3505 mobile: 'expanded', 3506 mobileBelowRow: false, 3507 mobileFlyout: false, 3508 breakPoints: { 3509 large: 1200, 3510 medium: 992, 3511 small: 768 }, 3512 mobileBreakpoint: 'mobile', 3513 postId : '27335', 3514 mobileStacked: true, 3515 submenuIcon: 'none', 3516 flyoutWidth: '250', 3517 }); 3518 3519 }); 3520 3521})(jQuery); 3522 3523(function($) { 3524 3525 $(function() { 3526 3527 new FLBuilderMenu({ 3528 id: '6166f49fcad45', 3529 type: 'vertical', 3530 mobile: 'expanded', 3531 mobileBelowRow: false, 3532 mobileFlyout: false, 3533 breakPoints: { 3534 large: 1200, 3535 medium: 992, 3536 small: 768 }, 3537 mobileBreakpoint: 'mobile', 3538 postId : '27335', 3539 mobileStacked: true, 3540 submenuIcon: 'none', 3541 flyoutWidth: '250', 3542 }); 3543 3544 }); 3545 3546})(jQuery); 3547 3548(function($) { 3549 3550 $(function() { 3551 3552 new FLBuilderMenu({ 3553 id: '6166f4b81fcaf', 3554 type: 'vertical', 3555 mobile: 'expanded', 3556 mobileBelowRow: false, 3557 mobileFlyout: false, 3558 breakPoints: { 3559 large: 1200, 3560 medium: 992, 3561 small: 768 }, 3562 mobileBreakpoint: 'mobile', 3563 postId : '27335', 3564 mobileStacked: true, 3565 submenuIcon: 'none', 3566 flyoutWidth: '250', 3567 }); 3568 3569 }); 3570 3571})(jQuery); 3572 3573(function($) { 3574 3575 $(function() { 3576 3577 new FLBuilderMenu({ 3578 id: '6166f60bec55f', 3579 type: 'vertical', 3580 mobile: 'expanded', 3581 mobileBelowRow: false, 3582 mobileFlyout: false, 3583 breakPoints: { 3584 large: 1200, 3585 medium: 992, 3586 small: 768 }, 3587 mobileBreakpoint: 'mobile', 3588 postId : '27335', 3589 mobileStacked: true, 3590 submenuIcon: 'none', 3591 flyoutWidth: '250', 3592 }); 3593 3594 }); 3595 3596})(jQuery); 3597 3598/* Start Layout Custom JS */ 3599 3600/* End Layout Custom JS */ 3601 3602 3603jQuery(function($) { 3604 3605 $(function() { 3606 $( '.fl-node-izhljdrm4vo8 .fl-photo-img' ) 3607 .on( 'mouseenter', function( e ) { 3608 $( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' ); 3609 } ) 3610 .on( 'mouseleave', function( e ){ 3611 $( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null ); 3612 } ); 3613 }); 3614 window._fl_string_to_slug_regex = 'a-zA-Z0-9'; 3615}); 3616(function($){ 3617 })(jQuery); 3618jQuery(function($) { 3619 3620 $(function() { 3621 $( '.fl-node-6jio1mt2wnkz .fl-photo-img' ) 3622 .on( 'mouseenter', function( e ) { 3623 $( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' ); 3624 } ) 3625 .on( 'mouseleave', function( e ){ 3626 $( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null ); 3627 } ); 3628 }); 3629 window._fl_string_to_slug_regex = 'a-zA-Z0-9'; 3630}); 3631 3632(function($) { 3633 3634 $(function() { 3635 3636 new FLBuilderMenu({ 3637 id: 'u8pveyc47g30', 3638 type: 'horizontal', 3639 mobile: 'expanded', 3640 mobileBelowRow: true, 3641 mobileFlyout: false, 3642 breakPoints: { 3643 large: 1200, 3644 medium: 992, 3645 small: 768 }, 3646 mobileBreakpoint: 'mobile', 3647 postId : '230550', 3648 mobileStacked: true, 3649 submenuIcon: 'arrows', 3650 flyoutWidth: '250', 3651 }); 3652 3653 }); 3654 3655})(jQuery); 3656 3657/* Start Layout Custom JS */ 3658 3659/* End Layout Custom JS */ 3660 3661(function($){ 3662 3663 /** 3664 * Helper class for header layout logic. 3665 * 3666 * @since 1.0 3667 * @class FLThemeBuilderHeaderLayout 3668 */ 3669 FLThemeBuilderHeaderLayout = { 3670 3671 /** 3672 * A reference to the window object for this page. 3673 * 3674 * @since 1.0 3675 * @property {Object} win 3676 */ 3677 win : null, 3678 3679 /** 3680 * A reference to the body object for this page. 3681 * 3682 * @since 1.0 3683 * @property {Object} body 3684 */ 3685 body : null, 3686 3687 /** 3688 * A reference to the header object for this page. 3689 * 3690 * @since 1.0 3691 * @property {Object} header 3692 */ 3693 header : null, 3694 3695 /** 3696 * Whether this header overlays the content or not. 3697 * 3698 * @since 1.0 3699 * @property {Boolean} overlay 3700 */ 3701 overlay : false, 3702 3703 /** 3704 * Whether the page has the WP admin bar or not. 3705 * 3706 * @since 1.0 3707 * @property {Boolean} hasAdminBar 3708 */ 3709 hasAdminBar : false, 3710 3711 /** 3712 * Breakpoint for when the sticky header should apply. 3713 * 3714 * @since 1.4 3715 * @property {String} stickyOn 3716 */ 3717 stickyOn: '', 3718 3719 /** 3720 * A reference of the sticky and shrink header breakpoint. 3721 * 3722 * @since 1.2.5 3723 * @property {Number} breakpointWidth 3724 */ 3725 breakpointWidth: 0, 3726 3727 /** 3728 * Initializes header layout logic. 3729 * 3730 * @since 1.0 3731 * @method init 3732 */ 3733 init: function() 3734 {
3735 var editing = $( 'html.fl-builder-edit' ).length, 3736 header = $( '.fl-builder-content[data-type=header]' ), 3737 menuModule = header.find( '.fl-module-menu' ), 3738 breakpoint = null; 3739 3740 if ( ! editing && header.length ) { 3741 3742 header.imagesLoaded( $.proxy( function() { 3743 3744 this.win = $( window ); 3745 this.body = $( 'body' ); 3746 this.header = header.eq( 0 ); 3747 this.overlay = !! Number( header.attr( 'data-overlay' ) ); 3748 this.hasAdminBar = !! $( 'body.admin-bar' ).length; 3749 this.stickyOn = this.header.data( 'sticky-on' ); 3750 breakpoint = this.header.data( 'sticky-breakpoint' ); 3751 3752 if ( '' == this.stickyOn ) { 3753 if ( typeof FLBuilderLayoutConfig.breakpoints[ breakpoint ] !== undefined ) { 3754 this.breakpointWidth = FLBuilderLayoutConfig.breakpoints[ breakpoint ]; 3755 } 3756 else { 3757 this.breakpointWidth = FLBuilderLayoutConfig.breakpoints.medium; 3758 } 3759 } 3760 3761 if ( Number( header.attr( 'data-sticky' ) ) ) { 3762 3763 this.header.data( 'original-top', this.header.offset().top ); 3764 this.win.on( 'resize', $.throttle( 500, $.proxy( this._initSticky, this ) ) ); 3765 this._initSticky(); 3766 3767 } 3768 3769 }, this ) ); 3770 } 3771 }, 3772 3773 /** 3774 * Initializes sticky logic for a header. 3775 * 3776 * @since 1.0 3777 * @access private 3778 * @method _initSticky 3779 */ 3780 _initSticky: function( e ) 3781 { 3782 var header = $('.fl-builder-content[data-type=header]'), 3783 windowSize = this.win.width(), 3784 makeSticky = false; 3785 3786 makeSticky = this._makeWindowSticky( windowSize ); 3787 if ( makeSticky || ( this.breakpointWidth > 0 && windowSize >= this.breakpointWidth ) ) { 3788 this.win.on( 'scroll.fl-theme-builder-header-sticky', $.proxy( this._doSticky, this ) ); 3789 // 3790 // Check if Event Type is 'resize' then invoke this._doSticky() 3791 // only if the 'fl-theme-builder-header-sticky' class is already present. 3792 // 3793 if ( e && 'resize' === e.type ) { 3794 if ( this.header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 3795 this._doSticky( e ); 3796 } 3797 this._adjustStickyHeaderWidth(); 3798 } 3799 3800 if ( Number( header.attr( 'data-shrink' ) ) ) { 3801 this.header.data( 'original-height', this.header.outerHeight() ); 3802 this.win.on( 'resize', $.throttle( 500, $.proxy( this._initShrink, this ) ) ); 3803 this._initShrink(); 3804 } 3805 3806 this._initFlyoutMenuFix( e ); 3807 } else { 3808 this.win.off( 'scroll.fl-theme-builder-header-sticky' ); 3809 this.win.off( 'resize.fl-theme-builder-header-sticky' ); 3810 3811 this.header.removeClass( 'fl-theme-builder-header-sticky' ); 3812 this.header.removeAttr( 'style' ); 3813 this.header.parent().css( 'padding-top', '0' ); 3814 } 3815 }, 3816 3817 /** 3818 * Check if Header should be sticky at a particular Window size. 3819 * 3820 * @since 1.4 3821 * @access private 3822 * @param widowSize 3823 * @method _makeWindowSticky 3824 */ 3825 _makeWindowSticky: function ( windowSize ) 3826 { 3827 var makeSticky = false; 3828 3829 switch (this.stickyOn) { 3830 case 'xl': 3831 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['large']; 3832 break; 3833 case '': // Default 3834 case 'desktop': 3835 makeSticky = windowSize >= FLBuilderLayoutConfig.breakpoints['medium']; 3836 break; 3837 case 'desktop-medium': 3838 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['small']; 3839 break; 3840 case 'large': 3841 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['medium'] && windowSize <= FLBuilderLayoutConfig.breakpoints['large']; 3842 break; 3843 case 'large-medium': 3844 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['small'] && windowSize <= FLBuilderLayoutConfig.breakpoints['large']; 3845 break; 3846 case 'medium': 3847 makeSticky = ( windowSize <= FLBuilderLayoutConfig.breakpoints['medium'] && windowSize > FLBuilderLayoutConfig.breakpoints['small'] ); 3848 break; 3849 case 'medium-mobile': 3850 makeSticky = (windowSize <= FLBuilderLayoutConfig.breakpoints['medium']); 3851 break; 3852 case 'mobile': 3853 makeSticky = (windowSize <= FLBuilderLayoutConfig.breakpoints['small']); 3854 break; 3855 case 'all': 3856 makeSticky = true; 3857 break; 3858 } 3859 3860 return makeSticky; 3861 }, 3862 3863 /** 3864 * Sticks the header when the page is scrolled. 3865 * 3866 * @since 1.0 3867 * @access private 3868 * @method _doSticky 3869 */ 3870 _doSticky: function( e ) 3871 { 3872 var winTop = Math.floor( this.win.scrollTop() ), 3873 headerTop = Math.floor( this.header.data( 'original-top' ) ),
3874 hasStickyClass = this.header.hasClass( 'fl-theme-builder-header-sticky' ), 3875 hasScrolledClass = this.header.hasClass( 'fl-theme-builder-header-scrolled' ), 3876 beforeHeader = this.header.prevAll( '.fl-builder-content' ), 3877 bodyTopPadding = parseInt( jQuery('body').css('padding-top') ), 3878 winBarHeight = $('#wpadminbar').length ? $('#wpadminbar').outerHeight() : 0, 3879 headerHeight = 0; 3880 3881 if ( isNaN( bodyTopPadding ) ) { 3882 bodyTopPadding = 0; 3883 } 3884 3885 if ( this.hasAdminBar && this.win.width() > 600 ) { 3886 winTop += Math.floor( winBarHeight ); 3887 } 3888 3889 if ( winTop > headerTop ) { 3890 if ( ! hasStickyClass ) { 3891 if ( e && ( 'scroll' === e.type || 'smartscroll' === e.type ) ) { 3892 this.header.addClass( 'fl-theme-builder-header-sticky' ); 3893 if ( this.overlay && beforeHeader.length ) { 3894 this.header.css( 'top', winBarHeight); 3895 } 3896 } 3897 3898 if ( ! this.overlay ) { 3899 this._adjustHeaderHeight(); 3900 } 3901 } 3902 } 3903 else if ( hasStickyClass ) { 3904 this.header.removeClass( 'fl-theme-builder-header-sticky' ); 3905 this.header.removeAttr( 'style' ); 3906 this.header.parent().css( 'padding-top', '0' ); 3907 } 3908 3909 this._adjustStickyHeaderWidth(); 3910 3911 if ( winTop > headerTop ) { 3912 if ( ! hasScrolledClass ) { 3913 this.header.addClass( 'fl-theme-builder-header-scrolled' ); 3914 } 3915 } else if ( hasScrolledClass ) { 3916 this.header.removeClass( 'fl-theme-builder-header-scrolled' ); 3917 } 3918 3919 this._flyoutMenuFix( e ); 3920 }, 3921 3922 /** 3923 * Initializes flyout menu fixes on sticky header. 3924 * 3925 * @since 1.4.1 3926 * @method _initFlyoutMenuFix 3927 */ 3928 _initFlyoutMenuFix: function( e ) { 3929 var header = this.header, 3930 menuModule = header.closest( '.fl-menu' ), 3931 flyoutMenu = menuModule.find( '.fl-menu-mobile-flyout' ), 3932 isPushMenu = menuModule.hasClass( 'fl-menu-responsive-flyout-push' ) || menuModule.hasClass( 'fl-menu-responsive-flyout-push-opacity' ), 3933 isOverlay = menuModule.hasClass( 'fl-menu-responsive-flyout-overlay' ), 3934 flyoutPos = menuModule.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 3935 flyoutParent = header.parent().is( 'header' ) ? header.parent().parent() : header.parent(); 3936 isFullWidth = this.win.width() === header.width(), 3937 flyoutLayout = '', 3938 activePos = 250, 3939 headerPos = 0; 3940 3941 if ( ! flyoutMenu.length ) { 3942 return; 3943 } 3944 3945 if ( this.win.width() > header.parent().width() ) { 3946 headerPos = ( this.win.width() - header.width() ) / 2; 3947 } 3948 3949 if ( isOverlay ) { 3950 activePos = headerPos; 3951 } 3952 else if ( isPushMenu ) { 3953 activePos = activePos + headerPos; 3954 } 3955 flyoutMenu.data( 'activePos', activePos ); 3956 3957 if ( isPushMenu ) { 3958 flyoutLayout = 'push-' + flyoutPos; 3959 } 3960 else if ( isOverlay ) { 3961 flyoutLayout = 'overlay-' + flyoutPos; 3962 } 3963 3964 if ( isPushMenu && ! $( 'html' ).hasClass( 'fl-theme-builder-has-flyout-menu' ) ) { 3965 $( 'html' ).addClass( 'fl-theme-builder-has-flyout-menu' ); 3966 } 3967 3968 if ( ! flyoutParent.hasClass( 'fl-theme-builder-flyout-menu-' + flyoutLayout ) ) { 3969 flyoutParent.addClass( 'fl-theme-builder-flyout-menu-' + flyoutLayout ); 3970 } 3971 3972 if ( ! header.hasClass( 'fl-theme-builder-flyout-menu-overlay' ) && isOverlay ) { 3973 header.addClass( 'fl-theme-builder-flyout-menu-overlay' ); 3974 } 3975 3976 if ( ! header.hasClass( 'fl-theme-builder-header-full-width' ) && isFullWidth ) { 3977 header.addClass( 'fl-theme-builder-header-full-width' ); 3978 } 3979 else if ( ! isFullWidth ) { 3980 header.removeClass( 'fl-theme-builder-header-full-width' ); 3981 } 3982 3983 menuModule.on( 'click', '.fl-menu-mobile-toggle', $.proxy( function( event ){ 3984 if ( menuModule.find( '.fl-menu-mobile-toggle.fl-active' ).length ) { 3985 $( 'html' ).addClass( 'fl-theme-builder-flyout-menu-active' ); 3986 event.stopImmediatePropagation(); 3987 } 3988 else { 3989 $( 'html' ).removeClass( 'fl-theme-builder-flyout-menu-active' ); 3990 } 3991 3992 this._flyoutMenuFix( event ); 3993 }, this ) ); 3994 }, 3995 3996 /** 3997 * Fix flyout menu inside the sticky header. 3998 * 3999 * @since 1.4.1 4000 * @method _flyoutMenuFix 4001 */ 4002 _flyoutMenuFix: function( e ){ 4003 var header = this.header, 4004 menuModule = $( e.target ).closest( '.fl-menu' ), 4005 flyoutMenu = menuModule.find( '.fl-menu-mobile-flyout' ), 4006 flyoutPos = menuModule.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 4007 menuOpacity = menuModule.find( '.fl-menu-mobile-opacity' ), 4008 isScroll = 'undefined' !== typeof e && 'scroll' === e.handleObj.type, 4009 activePos = 'undefined' !== typeof flyoutMenu.data( 'activePos' ) ? flyoutMenu.data( 'activePos' ) : 0, 4010 headerPos = ( this.win.width() - header.width() ) / 2, 4011 inactivePos = headerPos > 0 ? activePos + 4 : 254; 4012 4013 if ( ! flyoutMenu.length ) { 4014 return; 4015 } 4016 4017 if ( this.overlay ) { 4018 return; 4019 } 4020
4021 if( $( '.fl-theme-builder-flyout-menu-active' ).length ) { 4022 4023 if ( isScroll && ! flyoutMenu.hasClass( 'fl-menu-disable-transition' ) ) { 4024 flyoutMenu.addClass( 'fl-menu-disable-transition' ); 4025 } 4026 4027 if ( header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 4028 if ( ! isScroll ) { 4029 setTimeout( $.proxy( function(){ 4030 flyoutMenu.css( flyoutPos, '-' + activePos + 'px' ); 4031 }, this ), 1 ); 4032 } 4033 else { 4034 flyoutMenu.css( flyoutPos, '-' + activePos + 'px' ); 4035 } 4036 } 4037 else { 4038 flyoutMenu.css( flyoutPos, '0px' ); 4039 } 4040 } 4041 else { 4042 if ( flyoutMenu.hasClass( 'fl-menu-disable-transition' ) ) { 4043 flyoutMenu.removeClass( 'fl-menu-disable-transition' ); 4044 } 4045 4046 if ( header.hasClass( 'fl-theme-builder-flyout-menu-overlay' ) && headerPos > 0 && headerPos < 250 ) { 4047 if ( header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 4048 inactivePos = headerPos + 254; 4049 } 4050 else { 4051 inactivePos = 254; 4052 } 4053 } 4054 4055 if ( e && e.type === 'resize' ) { 4056 inactivePos = headerPos + 254; 4057 } 4058 4059 flyoutMenu.css( flyoutPos, '-' + inactivePos + 'px' ); 4060 } 4061 4062 if ( e && menuModule.is('.fl-menu-responsive-flyout-overlay') && $.infinitescroll ) { 4063 e.stopImmediatePropagation(); 4064 } 4065 4066 if( menuOpacity.length ) { 4067 if ( header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 4068 if ( '0px' === menuOpacity.css( 'left' ) ) { 4069 menuOpacity.css( 'left', '-' + headerPos + 'px' ); 4070 } 4071 } 4072 else { 4073 menuOpacity.css( 'left', '' ); 4074 } 4075 } 4076 }, 4077 4078 /** 4079 * Adjust sticky header width if BB Theme Boxed Layout is used. 4080 * 4081 * @since 1.4 4082 * @access private 4083 * @method _adjustStickyHeaderWidth 4084 */ 4085 _adjustStickyHeaderWidth: function () { 4086 if ( $('body').hasClass( 'fl-fixed-width' ) ) { 4087 var parentWidth = this.header.parent().width(); 4088 4089 // Better if this is set in the stylesheet file. 4090 if ( this.win.width() >= 992 ) { 4091 this.header.css({ 4092 'margin': '0 auto', 4093 'max-width': parentWidth, 4094 }); 4095 } 4096 else { 4097 this.header.css({ 4098 'margin': '', 4099 'max-width': '', 4100 }); 4101 } 4102 } 4103 }, 4104 4105 /** 4106 * Adjust Sticky Header Height 4107 * 4108 * @since 1.4 4109 * @access private 4110 * @method _adjustHeaderHeight 4111 */ 4112 _adjustHeaderHeight: function () { 4113 var beforeHeader = this.header.prevAll('.fl-builder-content'), 4114 beforeHeaderHeight = 0, 4115 beforeHeaderFix = 0, 4116 headerHeight = Math.floor( this.header.outerHeight() ), 4117 bodyTopPadding = parseInt( $( 'body' ).css( 'padding-top' ) ), 4118 wpAdminBarHeight = 0, 4119 totalHeaderHeight = 0; 4120 4121 if ( isNaN( bodyTopPadding ) ) { 4122 bodyTopPadding = 0; 4123 } 4124 4125 if ( beforeHeader.length ) { 4126 $.each( beforeHeader, function() { 4127 beforeHeaderHeight += Math.floor( $(this).outerHeight() ); 4128 }); 4129 // Subtract this value from the header parent's top padding. 4130 beforeHeaderFix = 2; 4131 } 4132 4133 if ( this.hasAdminBar && this.win.width() <= 600 ) { 4134 wpAdminBarHeight = Math.floor( $('#wpadminbar').outerHeight() ); 4135 } 4136 4137 totalHeaderHeight = Math.floor( beforeHeaderHeight + headerHeight); 4138 4139 if ( headerHeight > 0 ) { 4140 var headerParent = this.header.parent(), 4141 headerParentTopPadding = 0; 4142 4143 // If the header's parent container is the BODY tag ignore its top padding. 4144 if ( $( headerParent ).is('body') ) { 4145 headerParentTopPadding = Math.floor( headerHeight - wpAdminBarHeight ); 4146 } else { 4147 headerParentTopPadding = Math.floor( headerHeight - bodyTopPadding - wpAdminBarHeight ); 4148 } 4149 4150 $( headerParent ).css( 'padding-top', ( headerParentTopPadding - beforeHeaderFix ) + 'px' ); 4151 4152 this.header.css({ 4153 '-webkit-transform': 'translate(0px, -' + totalHeaderHeight + 'px)', 4154 '-ms-transform': 'translate(0px, -' + totalHeaderHeight + 'px)', 4155 'transform': 'translate(0px, -' + totalHeaderHeight + 'px)' 4156 }); 4157 4158 } 4159 4160 }, 4161 4162 /** 4163 * Initializes shrink logic for a header. 4164 * 4165 * @since 1.0 4166 * @access private 4167 * @method _initShrink 4168 */ 4169 _initShrink: function( e ) 4170 { 4171 if ( this.win.width() >= this.breakpointWidth ) {
4172 this.win.on( 'scroll.fl-theme-builder-header-shrink', $.proxy( this._doShrink, this ) ); 4173 this._setImageMaxHeight(); 4174 4175 if ( this.win.scrollTop() > 0 ){ 4176 this._doShrink(); 4177 } 4178 4179 } else { 4180 this.header.parent().css( 'padding-top', '0' ); 4181 this.win.off( 'scroll.fl-theme-builder-header-shrink' ); 4182 this._removeShrink(); 4183 this._removeImageMaxHeight(); 4184 } 4185 }, 4186 4187 /** 4188 * Shrinks the header when the page is scrolled. 4189 * 4190 * @since 1.0 4191 * @access private 4192 * @method _doShrink 4193 */ 4194 _doShrink: function( e ) 4195 { 4196 var winTop = this.win.scrollTop(), 4197 headerTop = this.header.data('original-top'), 4198 headerHeight = this.header.data('original-height'), 4199 shrinkImageHeight = this.header.data('shrink-image-height'), 4200 windowSize = this.win.width(), 4201 makeSticky = this._makeWindowSticky( windowSize ), 4202 hasClass = this.header.hasClass( 'fl-theme-builder-header-shrink' ); 4203 4204 4205 if ( this.hasAdminBar ) { 4206 winTop += 32; 4207 } 4208 4209 if ( makeSticky && ( winTop > headerTop + headerHeight ) ) { 4210 if ( ! hasClass ) { 4211 4212 this.header.addClass( 'fl-theme-builder-header-shrink' ); 4213 4214 // Shrink images but don't include lightbox and menu images. 4215 this.header.find('img').each( function( i ) { 4216 var image = $( this ), 4217 maxMegaMenu = image.closest( '.max-mega-menu' ).length, 4218 imageInLightbox = image.closest( '.fl-button-lightbox-content' ).length, 4219 imageInNavMenu = image.closest( 'li.menu-item' ).length; 4220 4221 if ( ! ( imageInLightbox || imageInNavMenu || maxMegaMenu ) ) { 4222 image.css( 'max-height', shrinkImageHeight ); 4223 } 4224 4225 }); 4226 4227 this.header.find( '.fl-row-content-wrap' ).each( function() { 4228 4229 var row = $( this ); 4230 4231 if ( parseInt( row.css( 'padding-bottom' ) ) > 5 ) { 4232 row.addClass( 'fl-theme-builder-header-shrink-row-bottom' ); 4233 } 4234 4235 if ( parseInt( row.css( 'padding-top' ) ) > 5 ) { 4236 row.addClass( 'fl-theme-builder-header-shrink-row-top' ); 4237 } 4238 } ); 4239 4240 this.header.find( '.fl-module' ).each( function() { 4241 4242 var module = $( this ).find( '.fl-module-content' ).length ? $( this ).find( '.fl-module-content' ) : $( this ); 4243 4244 if ( parseInt( module.css( 'margin-bottom' ) ) > 5 ) { 4245 module.addClass( 'fl-theme-builder-header-shrink-module-bottom' ); 4246 } 4247 4248 if ( parseInt( module.css( 'margin-top' ) ) > 5 ) { 4249 module.addClass( 'fl-theme-builder-header-shrink-module-top' ); 4250 } 4251 } ); 4252 } 4253 } else if (hasClass) { 4254 this.header.find( 'img' ).css( 'max-height', '' ); 4255 this._removeShrink(); 4256 } 4257 4258 // Fixes Shrink header issue with BB Theme when window is scrolled then resized and back. 4259 if ( 'undefined' === typeof( e ) && $('body').hasClass( 'fl-fixed-width' ) ) { 4260 if ( ! this.overlay ) { 4261 this._adjustHeaderHeight(); 4262 } 4263 } 4264 4265 }, 4266 4267 /** 4268 * Removes the header shrink effect. 4269 * 4270 * @since 1.0 4271 * @access private 4272 * @method _removeShrink 4273 */ 4274 _removeShrink: function() 4275 { 4276 var rows = this.header.find( '.fl-row-content-wrap' ), 4277 modules = this.header.find('.fl-module, .fl-module-content'); 4278 4279 rows.removeClass( 'fl-theme-builder-header-shrink-row-bottom' ); 4280 rows.removeClass( 'fl-theme-builder-header-shrink-row-top' ); 4281 modules.removeClass( 'fl-theme-builder-header-shrink-module-bottom' ); 4282 modules.removeClass( 'fl-theme-builder-header-shrink-module-top' ); 4283 this.header.removeClass( 'fl-theme-builder-header-shrink' ); 4284 }, 4285 4286 /** 4287 * Adds max height to images in modules for smooth scrolling. 4288 * 4289 * @since 1.1.1 4290 * @access private 4291 * @method _setImageMaxHeight 4292 */ 4293 _setImageMaxHeight: function() 4294 { 4295 var head = $( 'head' ), 4296 stylesId = 'fl-header-styles-' + this.header.data( 'post-id' ), 4297 styles = '', 4298 images = this.header.find( '.fl-module img' ); 4299 4300 if ( $( '#' + stylesId ).length ) { 4301 return; 4302 } 4303 4304 images.each( function( i ) { 4305 var image = $( this ), 4306 height = image.height(), 4307 node = image.closest( '.fl-module' ).data( 'node' ), 4308 className = 'fl-node-' + node + '-img-' + i, 4309 maxMegaMenu = image.closest( '.max-mega-menu' ).length, 4310 imageInLightbox = image.closest( '.fl-button-lightbox-content' ).length, 4311 imageInNavMenu = image.closest( 'li.menu-item' ).length; 4312 4313 if ( ! ( imageInLightbox || imageInNavMenu || maxMegaMenu ) ) { 4314 image.addClass( className ); 4315 styles += '.' + className + ' { max-height: ' + ( height ? height : image[0].height ) + 'px }'; 4316 } 4317 4318 } ); 4319
4320 if ( '' !== styles ) { 4321 head.append( '<style id="' + stylesId + '">' + styles + '</style>' ); 4322 } 4323 }, 4324 4325 /** 4326 * Removes max height on images in modules for smooth scrolling. 4327 * 4328 * @since 1.1.1 4329 * @access private 4330 * @method _removeImageMaxHeight 4331 */ 4332 _removeImageMaxHeight: function() 4333 { 4334 $( '#fl-header-styles-' + this.header.data( 'post-id' ) ).remove(); 4335 }, 4336 }; 4337 4338 $( function() { FLThemeBuilderHeaderLayout.init(); } ); 4339 4340})(jQuery); 4341
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.