1var wpAjaxUrl = 'https://www.obdlink.com/wp-admin/admin-ajax.php';var flBuilderUrl = 'https://www.obdlink.com/wp-content/plugins/bb-plugin/';var FLBuilderLayoutConfig = { 2 anchorLinkAnimations : { 3 duration : 1000, 4 easing : 'swing', 5 offset : 100 6 }, 7 paths : { 8 pluginUrl : 'https://www.obdlink.com/wp-content/plugins/bb-plugin/', 9 wpAjaxUrl : 'https://www.obdlink.com/wp-admin/admin-ajax.php' 10 }, 11 breakpoints : { 12 small : 768, 13 medium : 992, 14 large : 1200 }, 15 waypoint: { 16 offset: 80 17 }, 18 emptyColWidth: '0%', 19 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 */ 1667const addToCart = document.querySelector('#singleCartBTN .add_to_cart_button'); 1668 1669if (addToCart) { 1670 addToCart.innerHTML = "buy now"; 1671 addToCart.style.color = 'rgba(255, 255, 255, 1)'; 1672 addToCart.addEventListener('mouseover', function(e) { 1673 e.target.style.color = '#ffca08'; 1674 }); 1675 addToCart.addEventListener('mouseout', function(e) { 1676 e.target.style.color = '#ffffff'; 1677 }); 1678} 1679/* End Global JS */ 1680 1681 1682;/* eslint-disable no-undef */ 1683(function($) { 1684 1685 /** 1686 * Class for Menu Module 1687 * 1688 * @since 1.6.1 1689 */ 1690 FLBuilderMenu = function( settings ){ 1691 1692 // set params 1693 this.nodeId = settings.id; 1694 this.nodeClass = '.fl-node-' + settings.id; 1695 this.wrapperClass = this.nodeClass + ' .fl-menu'; 1696 this.type = settings.type; 1697 this.mobileToggle = settings.mobile; 1698 this.mobileBelowRow = settings.mobileBelowRow; 1699 this.mobileFlyout = settings.mobileFlyout; 1700 this.breakPoints = settings.breakPoints; 1701 this.mobileBreakpoint = settings.mobileBreakpoint; 1702 this.currentBrowserWidth = $( window ).width(); 1703 this.postId = settings.postId; 1704 this.mobileStacked = settings.mobileStacked; 1705 this.submenuIcon = settings.submenuIcon; 1706 this.flyoutWidth = settings.flyoutWidth; 1707 1708 // initialize the menu 1709 this._initMenu(); 1710 1711 // check if viewport is resizing 1712 $( window ).on( 'resize', $.proxy( function() { 1713 1714 var width = $( window ).width(); 1715 1716 // if screen width is resized, reload the menu 1717 if( width != this.currentBrowserWidth ){ 1718 1719 this.currentBrowserWidth = width; 1720 this._initMenu( true ); 1721 this._clickOrHover( true ); 1722 } 1723 1724 this._resizeFlyoutMenuPanel(); 1725 }, this ) ); 1726 1727 $( window ).on( 'scroll', $.proxy( function() { 1728 this._resizeFlyoutMenuPanel(); 1729 }, this ) ); 1730 1731 // Move focus to the first menu item if the layout is set to BelowRow 1732 $( this.wrapperClass ).on( 'keydown', '.fl-menu-mobile-toggle, .menu-item:first a:first', $.proxy( function ( event ) { 1733 // Make sure it is a tab key with both the menu toggle enabled & below row layout set 1734 if ( event.key !== 'Tab' || ! this._isMenuToggle() || ! this._isMobileBelowRowEnabled() ) { 1735 return; 1736 } 1737 // Trigger focus on the mobile toggle button if the user is shift tabbing from the first menu item 1738 if ( event.shiftKey && $( event.currentTarget ).is( 'a' ) ) { 1739 event.preventDefault(); 1740 $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ).trigger( 'focus' ); 1741 } 1742 // Trigger focus on the first menu item if the user is tabbing from the mobile toggle button 1743 else if ( ! event.shiftKey && $( event.currentTarget ).hasClass( 'fl-menu-mobile-toggle' ) && $( event.currentTarget ).hasClass( 'fl-active' ) ) { 1744 event.preventDefault(); 1745 $( this.wrapperClass ).find( '.menu-item' ).first().find( 'a' ).first().trigger( 'focus' ); 1746 } 1747 }, this ) ); 1748 1749 // Close mobile menu or accordion when tabbing out from the first or last menu item 1750 $( this.wrapperClass ).on( 'focusout', $.proxy( function ( event ) { 1751 if ( $( this.wrapperClass + ' nav' ).has( $( event.relatedTarget ) ).length === 0 ) { 1752 if ( this.type === 'accordion' ) { 1753 this._toggleSubmenu( $( event.target ).parents( '.fl-has-submenu' ).last(), false ); 1754 } 1755 else { 1756 this._clickOrHover( true ); 1757 } 1758 const mobileToggle = $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ); 1759 if ( this._isMenuToggle() && mobileToggle.hasClass( 'fl-active' ) && ! $( event.relatedTarget ).is( mobileToggle ) && 'expanded' !== this.mobileToggle ) { 1760 mobileToggle.trigger( 'click' ); 1761 } 1762 } 1763 }, this ) ); 1764 1765 }; 1766 1767 FLBuilderMenu.prototype = { 1768 nodeClass : '', 1769 wrapperClass: '', 1770 type : '', 1771 breakPoints : {}, 1772 $submenus : null, 1773 1774 /** 1775 * Check if the screen size fits a mobile viewport. 1776 * 1777 * @since 1.6.1 1778 * @return bool 1779 */ 1780 _isMobile: function(){ 1781 return this.currentBrowserWidth <= this.breakPoints.small ? true : false;
1782 }, 1783 1784 /** 1785 * Check if the device is connected to a mouse. 1786 * 1787 * @since 2.9.0 1788 * @return bool 1789 */ 1790 _isMouseAvailable: function(){ 1791 return window.matchMedia("(pointer: fine) and (hover: hover)").matches; 1792 }, 1793 1794 /** 1795 * Check if the screen size fits a medium viewport. 1796 * 1797 * @since 1.10.5 1798 * @return bool 1799 */ 1800 _isMedium: function(){ 1801 return this.currentBrowserWidth <= this.breakPoints.medium ? true : false; 1802 }, 1803 1804 /** 1805 * Check if the screen size fits a large viewport. 1806 * 1807 * @since 1.10.5 1808 * @return bool 1809 */ 1810 _isLarge: function(){ 1811 return this.currentBrowserWidth <= this.breakPoints.large ? true : false; 1812 }, 1813 1814 /** 1815 * Check if the menu should toggle for the current viewport base on the selected breakpoint. 1816 * 1817 * @see this._isMobile() 1818 * @see this._isMedium() 1819 * @since 1.10.5 1820 * @return bool 1821 */ 1822 _isMenuToggle: function(){ 1823 if ( ( 'always' == this.mobileBreakpoint 1824 || ( this._isMobile() && 'mobile' == this.mobileBreakpoint ) 1825 || ( this._isMedium() && 'medium-mobile' == this.mobileBreakpoint ) 1826 || ( this._isLarge() && 'large-mobile' == this.mobileBreakpoint ) 1827 ) && ( $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ).is(':visible') || 'expanded' == this.mobileToggle ) ) { 1828 return true; 1829 } 1830 1831 return false; 1832 }, 1833 1834 /** 1835 * Initialize the toggle logic for the menu. 1836 * 1837 * @see this._isMenuToggle() 1838 * @see this._menuOnCLick() 1839 * @see this._clickOrHover() 1840 * @see this._submenuOnRight() 1841 * @see this._submenuRowZindexFix() 1842 * @see this._toggleForMobile() 1843 * @since 1.6.1 1844 * @return void 1845 */ 1846 _initMenu: function( resized = false ){ 1847 this._setupSubmenu(); 1848 if ( ! resized ) { 1849 this._menuOnFocus(); 1850 this._menuOnClick(); 1851 this._menuOnEscape(); 1852 } 1853 if ( $( this.nodeClass ).length && this.type == 'horizontal' ) { 1854 this._initMegaMenus(); 1855 } 1856 1857 if( this._isMenuToggle() || this.type == 'accordion' ){ 1858 1859 $( this.wrapperClass ).off( 'mouseenter mouseleave' ); 1860 this._clickOrHover(); 1861 1862 } else { 1863 this._submenuOnRight(); 1864 this._submenuRowZindexFix(); 1865 } 1866 1867 if( this.mobileToggle != 'expanded' ){ 1868 this._toggleForMobile(); 1869 } 1870 1871 if( $( this.wrapperClass ).find( '.fl-menu-search-item' ).length ){ 1872 this._toggleMenuSearch(); 1873 } 1874 1875 if( $( this.wrapperClass ).find( '.fl-menu-cart-item').length ){ 1876 this._wooUpdateParams(); 1877 } 1878 }, 1879 1880 /** 1881 * Whether the toggle behavior for submenus is through hovering or not. 1882 * 1883 * @since 2.5 1884 * @return void 1885 */ 1886 _setupSubmenu: function() { 1887 if ( ! this._isMouseAvailable() || this._isMenuToggle() || this.type === 'accordion' ) { 1888 $( this.wrapperClass ).addClass( 'no-hover' ); 1889 } 1890 else { 1891 $( this.wrapperClass ).removeClass( 'no-hover' ); 1892 } 1893 }, 1894 1895 /** 1896 * Logic for menu items when focused. 1897 * 1898 * @since 1.9.0 1899 * @return void 1900 */ 1901 _menuOnFocus: function(){ 1902 $( this.wrapperClass ).on( 'focus', 'a:not(.fl-button), .fl-menu-toggle', $.proxy( function( event ) { 1903 const focusedMenuItem = $( event.currentTarget ).closest( '.menu-item' ); 1904 const blurredMenuItem = $( event.relatedTarget ).closest( '.menu-item' ); 1905 // In case the blurred & focused items are siblings 1906 if ( focusedMenuItem.closest( 'ul' ).is( blurredMenuItem.closest( 'ul' ) ) ) { 1907 // Check if the focus event is not between the link & submenu icon of the same menu item 1908 if ( ! focusedMenuItem.is( blurredMenuItem ) ) { 1909 this._toggleSubmenu( blurredMenuItem, false ); 1910 } 1911 } 1912 // In case neither items are child & parent nor vice versa 1913 else if ( focusedMenuItem.has( blurredMenuItem ).length === blurredMenuItem.has( focusedMenuItem ).length ) { 1914 // Get the blurred item parent & is the sibling of the focused item 1915 blurredMenuItem.parents( '.fl-has-submenu' ).each( ( _, parent ) => { 1916 if ( focusedMenuItem.closest( 'ul' ).is( $( parent ).closest( 'ul' ) ) ) { 1917 this._toggleSubmenu( $( parent ), false ); 1918 return false; 1919 } 1920 } ); 1921 } 1922 // In case the blurred item is a child of the focused item
1923 else if ( focusedMenuItem.has( blurredMenuItem ).length ) { 1924 if ( this.type !== 'accordion' && this.submenuIcon === 'none' ) { 1925 this._toggleSubmenu( blurredMenuItem, false ); 1926 } 1927 } 1928 }, this ) ); 1929 }, 1930 1931 /** 1932 * Logic for menu items when Escape key is pressed. 1933 * 1934 * @since 2.7.1 1935 * @return void 1936 */ 1937 _menuOnEscape: function(){ 1938 $( this.wrapperClass ).on( 'keydown', $.proxy( function( event ){ 1939 if ( event.key !== 'Escape' ) return; 1940 const menuItem = $( event.target ).closest( '.menu-item' ); 1941 const mobileToggle = $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ); 1942 // In case there is a focused menu item 1943 if ( menuItem.length || $( event.target ).hasClass( 'fl-menu-mobile-close' ) ) { 1944 // Close the search form if it is open 1945 if ( menuItem.hasClass( 'fl-menu-search-item' ) && $( this.wrapperClass ).find('.fl-search-form-input-wrap').is( ':visible' ) ) { 1946 $( this.wrapperClass ).find('.fl-menu-search-item .fl-button:is(a, button:not([type="submit"]))').trigger( 'focus' ); 1947 } 1948 // Close all the submenus of the focused menu item 1949 else if ( menuItem.hasClass( 'fl-has-submenu' ) && menuItem.find( '.sub-menu' ).first().is( ':visible' ) ) { 1950 this._toggleSubmenu( menuItem, false ); 1951 } 1952 else { 1953 // Shift focus to the parent while closing all its submenus 1954 const parentMenuItem = menuItem.parents( '.fl-has-submenu' ).not( '.mega-menu.hide-heading, .mega-menu-disabled.hide-heading' ).first(); 1955 // Close the mobile menu if there is no parent menu item and the mobile toggle is active 1956 if ( parentMenuItem.length === 0 && this._isMenuToggle() && mobileToggle.hasClass( 'fl-active' ) ) { 1957 // Blur to trigger the focus-out event for closing the menu 1958 mobileToggle.trigger( 'blur' ).trigger( 'focus' ); 1959 } 1960 // Close the parent submenu and shift focus to its link 1961 else if ( parentMenuItem.length !== 0 ) { 1962 this._toggleSubmenu( parentMenuItem, false ); 1963 parentMenuItem.find( 'a' ).first().trigger( 'focus' ); 1964 } 1965 } 1966 } 1967 // Close the mobile menu if the mobile toggle is active 1968 else if ( $( event.target ).hasClass( 'fl-menu-mobile-toggle' ) && mobileToggle.hasClass( 'fl-active' ) ) { 1969 // Blur to trigger the focus-out event for closing the menu 1970 mobileToggle.trigger( 'blur' ).trigger( 'focus' ); 1971 } 1972 }, this ) ); 1973 }, 1974 1975 /** 1976 * Logic for menu items when clicked. 1977 * 1978 * @since 1.6.1 1979 * @return void 1980 */ 1981 _menuOnClick: function(){ 1982 // Fallback for span elements and fake links to interact with the keyboard 1983 $( this.wrapperClass ).on( 'keydown', 'a:not(.fl-button, [href]), span.fl-menu-toggle', $.proxy( function( event ) { 1984 if ( event.key === 'Enter' || event.key === ' ' ) { 1985 event.preventDefault(); 1986 $( event.currentTarget ).trigger( 'click' ); 1987 } 1988 }, this ) ); 1989 $( this.wrapperClass ).on( 'click', 'a, .fl-menu-toggle', $.proxy( function( event ) { 1990 // Only allow mouse clicks with accordion & mobile menus 1991 if ( this._isMouseAvailable() && ! this._isMenuToggle() && event.detail && this.type !== 'accordion' ) return; 1992 // Links only open & do not toggle submenus if there is either a submenu icon or an accordion layout 1993 if ( $( event.currentTarget ).is( 'a' ) && ( this.submenuIcon !== 'none' || this.type === 'accordion' ) ) return; 1994 const menuItem = $( event.currentTarget ).closest( '.menu-item, .fl-menu-logo' ); 1995 const menuLink = menuItem.find( 'a' ).first().attr( 'href' ); 1996 const submenuHidden = menuItem.find( '.sub-menu' ).first().is( ':hidden' ); 1997 if ( typeof menuLink === 'undefined' || menuLink === '#' || submenuHidden ) { 1998 event.preventDefault(); 1999 } 2000 if ( $( event.currentTarget ).hasClass( 'fl-menu-toggle' ) || submenuHidden ) { 2001 this._toggleSubmenu( menuItem, submenuHidden ); 2002 } 2003 }, this ) ); 2004 }, 2005 2006 /** 2007 * Controls toggling the visibility of all submenu items for desktop & mobile. 2008 * 2009 * @since 2.10 2010 * @return void 2011 */ 2012 _toggleSubmenu: function ( menuItem, opened ) { 2013 const togglingClass = this._isMenuToggle() || this.type === 'accordion' ? 'fl-active' : 'focus'; 2014 const toggleElement = this.submenuIcon === 'none' ? 'a' : '.fl-menu-toggle'; 2015 const hiddenMenu = '.mega-menu.hide-heading, .mega-menu-disabled.hide-heading'; 2016 if ( opened && menuItem.hasClass( 'fl-has-submenu' ) && ! menuItem.is( hiddenMenu ) ) { 2017 menuItem.addClass( togglingClass ); 2018 menuItem.find( toggleElement ).first().attr( 'aria-expanded', true ); 2019 if ( this._isMenuToggle() || this.type === 'accordion' ) { 2020 menuItem.find( '.sub-menu' ).first().filter( ':hidden' ).slideDown(); 2021 } 2022 } 2023 else { 2024 menuItem.parent().find( '.menu-item' ).removeClass( togglingClass ); 2025 menuItem.parent().find( '.fl-has-submenu' ).not( hiddenMenu ).find( toggleElement ).attr( 'aria-expanded', false ); 2026 if ( this._isMenuToggle() || this.type === 'accordion' ) {
2027 menuItem.find( '.sub-menu' ).filter( ':visible' ).slideUp(); 2028 } 2029 } 2030 }, 2031 2032 /** 2033 * Changes general styling and behavior of menus based on mobile / desktop viewport. 2034 * 2035 * @see this._isMenuToggle() 2036 * @since 1.6.1 2037 * @return void 2038 */ 2039 _clickOrHover: function( clear = false ){ 2040 const selector = this._isMobileBelowRowEnabled() ? this.nodeClass + '-clone' : this.nodeClass; 2041 this.$submenus = this.$submenus || $( selector ).find( '.sub-menu' ); 2042 2043 const className = this._isMenuToggle() || this.type === 'accordion' ? 'fl-active' : 'focus'; 2044 const toggleElement = this.submenuIcon === 'none' ? 'a' : '.fl-menu-toggle'; 2045 const hiddenMenu = '.mega-menu.hide-heading, .mega-menu-disabled.hide-heading'; 2046 $( selector ).find( '.fl-has-submenu' ).not( hiddenMenu ).each( function() { 2047 if( clear || ! $ ( this ).hasClass( className ) ){ 2048 if ( clear ) $( this ).removeClass( className ); 2049 $( this ).find( toggleElement ).first().attr( 'aria-expanded', false ); 2050 if ( className === 'fl-active' ) { 2051 $( this ).find( '.sub-menu' ).fadeOut(); 2052 } else if ( className === 'focus' ) { 2053 $( this ).find( '.sub-menu' ).css( { 2054 'display' : '', 2055 'opacity' : '' 2056 } ); 2057 } 2058 } 2059 } ); 2060 }, 2061 2062 /** 2063 * Logic to prevent submenus to go outside viewport boundaries. 2064 * 2065 * @since 1.6.1 2066 * @return void 2067 */ 2068 _submenuOnRight: function(){ 2069 2070 $( this.wrapperClass ) 2071 .on( 'mouseenter focus', '.fl-has-submenu', $.proxy( function( e ){ 2072 2073 if( $ ( e.currentTarget ).find('.sub-menu').length === 0 ) { 2074 return; 2075 } 2076 2077 var $link = $( e.currentTarget ), 2078 $parent = $link.parent(), 2079 $subMenu = $link.find( '.sub-menu' ), 2080 subMenuWidth = $subMenu.width(), 2081 subMenuPos = 0, 2082 bodyWidth = $( 'body' ).width(); 2083 2084 if( $link.closest( '.fl-menu-submenu-right' ).length !== 0) { 2085 2086 $link.addClass( 'fl-menu-submenu-right' ); 2087 2088 } else if( $( 'body' ).hasClass( 'rtl' ) ) { 2089 2090 subMenuPos = $parent.is( '.sub-menu' ) ? 2091 $parent.offset().left - subMenuWidth: 2092 $link.offset().left - $link.width() - subMenuWidth; 2093 2094 if( subMenuPos <= 0 ) { 2095 $link.addClass( 'fl-menu-submenu-right' ); 2096 } 2097 2098 } else { 2099 2100 subMenuPos = $parent.is( '.sub-menu' ) ? 2101 $parent.offset().left + $parent.width() + subMenuWidth : 2102 $link.offset().left + $link.width() + subMenuWidth; 2103 2104 if( subMenuPos > bodyWidth ) { 2105 $link.addClass('fl-menu-submenu-right'); 2106 } 2107 } 2108 }, this ) ) 2109 .on( 'mouseleave', '.fl-has-submenu', $.proxy( function( e ){ 2110 $( e.currentTarget ).removeClass( 'fl-menu-submenu-right' ); 2111 }, this ) ); 2112 2113 }, 2114 2115 /** 2116 * Logic to prevent submenus to go behind the next overlay row. 2117 * 2118 * @since 1.10.9 2119 * @return void 2120 */ 2121 _submenuRowZindexFix: function(){ 2122 2123 $( this.wrapperClass ) 2124 .on( 'mouseenter', 'ul.menu > .fl-has-submenu', $.proxy( function( e ){ 2125 2126 if( $ ( e.currentTarget ).find('.sub-menu').length === 0 ) { 2127 return; 2128 } 2129 2130 $( this.nodeClass ) 2131 .closest( '.fl-row' ) 2132 .find( '.fl-row-content' ) 2133 .css( 'z-index', '10' ); 2134 2135 }, this ) ) 2136 .on( 'mouseleave', 'ul.menu > .fl-has-submenu', $.proxy( function(){ 2137 2138 $( this.nodeClass ) 2139 .closest( '.fl-row' ) 2140 .find( '.fl-row-content' ) 2141 .css( 'z-index', '' ); 2142 2143 }, this ) ); 2144 }, 2145 2146 /** 2147 * Logic for the mobile menu button. 2148 * 2149 * @since 1.6.1 2150 * @return void 2151 */ 2152 _toggleForMobile: function(){ 2153 2154 var $wrapper = null, 2155 $menu = null, 2156 self = this; 2157 2158 $( this.wrapperClass ).find( '.fl-menu-mobile-toggle' ).attr('aria-controls', $( this.wrapperClass ).find( 'ul.menu' ).attr('id')); 2159 2160 if( this._isMenuToggle() ){ 2161 2162 if ( this._isMobileBelowRowEnabled() ) { 2163 this._placeMobileMenuBelowRow(); 2164 $wrapper = $( this.wrapperClass ); 2165 $menu = $( this.nodeClass + '-clone' ); 2166 $menu.find( 'ul.menu' ).show(); 2167 } 2168 else { 2169 $wrapper = $( this.wrapperClass ); 2170 $menu = $wrapper.find( '.menu' ); 2171 } 2172 2173 if( !$wrapper.find( '.fl-menu-mobile-toggle' ).hasClass( 'fl-active' ) && ! self.mobileFlyout ){ 2174 $menu.css({ display: 'none' }); 2175 } 2176 2177 // Flayout Menu 2178 if ( self.mobileFlyout ) { 2179 this._initFlyoutMenu(); 2180 } 2181 2182 $wrapper.on( 'click', '.fl-menu-mobile-toggle', function( e ){ 2183 e.stopImmediatePropagation(); 2184 2185 $( this ).toggleClass( 'fl-active' ); 2186 2187 if ( self.mobileFlyout ) { 2188 self._toggleFlyoutMenu(); 2189 const flyoutWrapper = $( '.fl-menu-mobile-flyout' ); 2190 if ( $( this ).hasClass( 'fl-active' ) ) { 2191 flyoutWrapper.attr( 'aria-hidden', false ); 2192 flyoutWrapper.find( 'a[href], button, input, select, textarea, span.fl-menu-toggle, [tabindex="-1"]' ).attr( 'tabindex', 0 ); 2193 } 2194 else { 2195 flyoutWrapper.attr( 'aria-hidden', true ); 2196 flyoutWrapper.find( 'a[href], button, input, select, textarea, span.fl-menu-toggle, [tabindex]:not
2196([tabindex="-1"])' ).attr( 'tabindex', -1 ); 2197 } 2198 } 2199 else { 2200 var targetMenu = null; 2201 2202 if ( self.mobileBelowRow ) { 2203 var $closestCol = $( this ).parents( '.fl-col, .fl-module-box' ), 2204 $closestColGroup = $closestCol.length ? $closestCol.parent( '.fl-col-group' ) : null; 2205 targetMenu = $closestCol.length ? $closestCol.last().next( '.fl-menu-mobile-clone' ) : null; 2206 2207 if ( $closestColGroup.length ) { 2208 if ( $closestColGroup.hasClass( 'fl-col-group-responsive-reversed' ) ) { 2209 $closestColGroup.find( '.fl-menu-mobile-clone' ).css( 'order', -1 ); 2210 } else if ( $closestColGroup ) { 2211 $closestColGroup.find( '.fl-menu-mobile-clone' ).css( 'order', 2 ); 2212 } 2213 } 2214 } else { 2215 targetMenu = $( this ).closest( '.fl-menu' ).find( 'ul.menu' ); 2216 } 2217 2218 if ( targetMenu.length ) { 2219 $menu = $( targetMenu ); 2220 } 2221 2222 $menu.slideToggle(); 2223 } 2224 2225 e.stopPropagation(); 2226 } ); 2227 2228 // Hide active menu when click on anchor link ID that exists on a page 2229 $menu.off().on( 'click', '.menu-item > a[href*="#"]:not([href="#"])', function(){ 2230 var $href = $(this).attr('href'), 2231 $targetID = $href.split('#')[1], 2232 element = $('#' + $targetID); 2233 if ( $('body').find(element).length > 0 ) { 2234 $( this ).toggleClass( 'fl-active' ); 2235 FLBuilderLayout._scrollToElement( element ); 2236 if ( ! self._isMenuToggle() ) { 2237 $menu.slideToggle(); 2238 } 2239 } 2240 }); 2241 } 2242 else { 2243 2244 if ( this._isMobileBelowRowEnabled() ) { 2245 this._removeMenuFromBelowRow(); 2246 } 2247 2248 $wrapper = $( this.wrapperClass ), 2249 $menu = $wrapper.find( 'ul.menu' ); 2250 $wrapper.find( '.fl-menu-mobile-toggle' ).removeClass( 'fl-active' ); 2251 $menu.css({ display: '' }); 2252 2253 if ( ! this._isMobileBelowRowEnabled() ) { 2254 $menu.off( 'click', '.menu-item > a[href*="#"]:not([href="#"])' ); 2255 } 2256 2257 if ( this.mobileFlyout && $wrapper.find( '.fl-menu-mobile-flyout' ).length > 0 ) { 2258 $( 'body' ).css( 'margin', '' ); 2259 $( '.fl-builder-ui-pinned-content-transform' ).css( 'transform', '' ); 2260 $menu.unwrap(); 2261 $wrapper.find( '.fl-menu-mobile-close' ).remove(); 2262 $wrapper.find( '.fl-menu-mobile-opacity' ).remove(); 2263 } 2264 } 2265 }, 2266 2267 /** 2268 * Init any mega menus that exist. 2269 * 2270 * @see this._isMenuToggle() 2271 * @since 1.10.4 2272 * @return void 2273 */ 2274 _initMegaMenus: function(){ 2275 2276 var module = $( this.nodeClass ), 2277 rowContent = module.closest( '.fl-row-content' ), 2278 rowWidth = rowContent.width(), 2279 megas = module.find( '.mega-menu' ), 2280 disabled = module.find( '.mega-menu-disabled' ), 2281 isToggle = this._isMenuToggle(); 2282 2283 if ( isToggle ) { 2284 megas.removeClass( 'mega-menu' ).addClass( 'mega-menu-disabled' ); 2285 module.find( 'li.mega-menu-disabled > ul.sub-menu' ).css( 'width', '' ); 2286 rowContent.css( 'position', '' ); 2287 } else { 2288 disabled.removeClass( 'mega-menu-disabled' ).addClass( 'mega-menu' ); 2289 module.find( 'li.mega-menu > ul.sub-menu' ).css( 'width', rowWidth + 'px' ); 2290 rowContent.css( 'position', 'relative' ); 2291 } 2292 }, 2293 2294 /** 2295 * Check to see if Below Row should be enabled. 2296 * 2297 * @since 1.11 2298 * @return boolean 2299 */ 2300 _isMobileBelowRowEnabled: function() { 2301 return this.mobileBelowRow && ( $( this.nodeClass ).parents( '.fl-col, .fl-module-box' ).length ); 2302 }, 2303 2304 /** 2305 * Logic for putting the mobile menu below the menu's 2306 * column so it spans the full width of the page. 2307 * 2308 * @since 1.10 2309 * @return void 2310 */ 2311 _placeMobileMenuBelowRow: function(){ 2312 2313 if ( $( this.nodeClass + '-clone' ).length ) { 2314 return; 2315 } 2316 2317 var module = $( this.nodeClass ), 2318 clone = null, 2319 col = module.parents( '.fl-col, .fl-module-box' ).last(); 2320 2321 if ( module.length < 1 ) { 2322 return; 2323 } 2324 2325 clone = ( module.length > 1 ) ? $( module[0] ).clone() : module.clone(); 2326 module.find( 'ul.menu' ).remove(); 2327 clone.addClass( ( this.nodeClass + '-clone' ).replace( '.', '' ) ); 2328 clone.addClass( 'fl-menu-mobile-clone' ); 2329 clone.find( '.fl-menu-mobile-toggle' ).remove(); 2330 col.after( clone ); 2331 2332 // Removes animation when enabled 2333 if ( module.hasClass( 'fl-animation' ) ) { 2334 clone.removeClass( 'fl-animation' ); 2335 } 2336 2337 this._menuOnFocus(); 2338 this._menuOnClick(); 2339 this._menuOnEscape(); 2340 }, 2341 2342 /** 2343 * Logic for removing the mobile menu from below the menu's 2344 * column and putting it back in the main wrapper. 2345 * 2346 * @since 1.10 2347 * @return void 2348 */ 2349 _removeMenuFromBelowRow: function(){ 2350 2351 if ( ! $( this.nodeClass + '-clone' ).length ) { 2352 return; 2353 } 2354 2355 var module = $( this.nodeClass ), 2356 clone = $( this.nodeClass + '-clone' ), 2357 menu = clone.find( 'ul.menu' ), 2358 nav = module.find( 'nav' ); 2359
2360 if ( nav.length ) { 2361 nav.append( menu ); 2362 } else { 2363 module.find( '.fl-menu-mobile-toggle' ).after( menu ); 2364 } 2365 clone.remove(); 2366 menu.find( 'a' ).each( FLBuilderLayout._initAnchorLink ); 2367 }, 2368 2369 /** 2370 * Logic for Flyout responsive menu. 2371 * 2372 * @since 2.2 2373 * @return void 2374 */ 2375 _initFlyoutMenu: function(){ 2376 var wrapper = $( this.wrapperClass ), 2377 menu = wrapper.find( 'ul.menu' ), 2378 button = wrapper.find( '.fl-menu-mobile-toggle' ); 2379 2380 if ( 0 === wrapper.find( '.fl-menu-mobile-flyout' ).length ) { 2381 menu.wrap( '<div class="fl-menu-mobile-flyout" aria-hidden="true"></div>' ); 2382 } 2383 2384 if ( 0 === wrapper.find( '.fl-menu-mobile-close' ).length ) { 2385 var close = window.fl_responsive_close || 'Close' 2386 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>' ); 2387 } 2388 2389 // Push with opacity 2390 if ( wrapper.hasClass( 'fl-menu-responsive-flyout-push-opacity' ) && 0 === wrapper.find( '.fl-menu-mobile-opacity' ).length ) { 2391 wrapper.append( '<div class="fl-menu-mobile-opacity"></div>' ); 2392 } 2393 2394 wrapper.off( 'click', '.fl-menu-mobile-opacity, .fl-menu-mobile-close' ).on( 'click', '.fl-menu-mobile-opacity, .fl-menu-mobile-close', function(e){ 2395 button.trigger('focus').trigger( 'click' ); 2396 e.stopPropagation(); 2397 }); 2398 2399 if ( 'undefined' !== typeof FLBuilder ) { 2400 FLBuilder.addHook('restartEditingSession', function(){ 2401 $( '.fl-builder-ui-pinned-content-transform' ).css( 'transform', '' ); 2402 2403 // Toggle active menu 2404 if ( button.hasClass( 'fl-active' ) ) { 2405 button.trigger( 'click' ); 2406 } 2407 }); 2408 } 2409 $( '.fl-menu-mobile-flyout' ).find( 'a[href], button, input, select, textarea, span.fl-menu-toggle, [tabindex]:not([tabindex="-1"])' ).attr( 'tabindex', -1 ); 2410 }, 2411 2412 /** 2413 * Logic to enable/disable the Flyout menu on button click. 2414 * 2415 * @since 2.2 2416 * @return void 2417 */ 2418 _toggleFlyoutMenu: function(){ 2419 var wrapper = $( this.wrapperClass ), 2420 button = wrapper.find( '.fl-menu-mobile-toggle' ), 2421 position = wrapper.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 2422 pushMenu = wrapper.hasClass( 'fl-menu-responsive-flyout-push' ) || wrapper.hasClass( 'fl-menu-responsive-flyout-push-opacity' ), 2423 opacity = wrapper.find( '.fl-menu-mobile-opacity' ), 2424 marginPos = {}, 2425 fixedPos = {}, 2426 fixedHeader = $('header, header > div'); 2427 2428 this._resizeFlyoutMenuPanel(); 2429 2430 // Fix the push menu when builder ui panel is pinned 2431 if ( $( '.fl-builder-ui-pinned-content-transform' ).length > 0 && ! $( 'body' ).hasClass( 'fl-builder-edit' ) ) { 2432 $( '.fl-builder-ui-pinned-content-transform' ).css( 'transform', 'none' ); 2433 } 2434 2435 if ( pushMenu ) { 2436 marginPos[ 'margin-' + position ] = button.hasClass( 'fl-active' ) ? this.flyoutWidth + 'px' : '0px'; 2437 $( 'body' ).animate( marginPos, 200); 2438 2439 // Fixed header 2440 if ( fixedHeader.length > 0 ) { 2441 fixedPos[ position] = button.hasClass( 'fl-active' ) ? this.flyoutWidth + 'px' : '0px'; 2442 fixedHeader.each(function(){ 2443 if ( 'fixed' == $( this ).css( 'position' ) ) { 2444 $( this ).css({ 'transition' : 'none' }); 2445 $( this ).animate( fixedPos, 200 ); 2446 } 2447 }); 2448 } 2449 } 2450 2451 if ( opacity.length > 0 && button.hasClass( 'fl-active' ) ) { 2452 opacity.show(); 2453 } 2454 else { 2455 opacity.hide(); 2456 } 2457 }, 2458 2459 /** 2460 * Resize or reposition the Flyout Menu Panel. 2461 * 2462 * @since 2.8.1 2463 * @return void 2464 */ 2465 _resizeFlyoutMenuPanel: function(){ 2466 const wrapper = $( this.wrapperClass ); 2467 const wrapFlyout = wrapper.find( '.fl-menu-mobile-flyout' ); 2468 2469 if ( wrapFlyout.length > 0 ) { 2470 wrapFlyout.css( this._getFlyoutMenuPanelPosition() ); 2471 } 2472 }, 2473 2474 /** 2475 * Compute the Flyout Menu Panel's position on the screen. 2476 * 2477 * @since 2.8.1 2478 * @return object 2479 */ 2480 _getFlyoutMenuPanelPosition: function() { 2481 var wrapper = $( this.wrapperClass ), 2482 button = wrapper.find( '.fl-menu-mobile-toggle' ), 2483 side = wrapper.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 2484 winHeight = $(window).outerHeight(), 2485 winTop = $(window).scrollTop(), 2486 adminBarHeight = $( '#wpadminbar' ).length ? $( '#wpadminbar' ).height() : 0, 2487 flyoutPosition = {}; 2488 2489 flyoutPosition[ side ] = '-' + ( parseInt( this.flyoutWidth ) + 15 ) + 'px'; 2490 if ( ! button.hasClass( 'fl-active' ) ) { 2491 return flyoutPosition; 2492 } 2493 2494 flyoutPosition[ side ] = '0px'; 2495 flyoutPosition[ 'height' ] = winHeight + 'px'; 2496 flyoutPosition[ 'top' ] = '0px'; 2497 2498 if ( adminBarHeight > 0 ) { 2499 const diff = adminBarHeight - winTop; 2500 flyoutPosition[ 'top' ] = diff <= 0 ? '0px' : (diff) + 'px'; 2501 } 2502 2503 return flyoutPosition; 2504 }, 2505 2506 /** 2507 * Shows or hides the nav search form. 2508 * 2509 * @since 2.5 2510 * @method _toggleMenuSearch 2511 */ 2512 _toggleMenuSearch: function(){ 2513 const form = $( this.wrapperClass ).find('.fl-menu-search-item .fl-search-form-input-wrap');
2514 const button = $( this.wrapperClass ).find('.fl-menu-search-item .fl-button:is(a, button:not([type="submit"]))'); 2515 form.on('submit', () => form.stop().fadeToggle('fast')); 2516 // Close the search form when focus is lost 2517 form.on('focusout', (event) => { 2518 if (form.is(':visible') && form.has($(event.relatedTarget)).length === 0) { 2519 button.trigger('click'); 2520 } 2521 }); 2522 button.attr('aria-expanded', false); 2523 // Close other open submenus when focusing on the search button 2524 button.on('focus', (event) => { 2525 const parentMenus = $(event.relatedTarget).parents('.menu-item.fl-has-submenu'); 2526 if (event.relatedTarget && parentMenus.length) { 2527 this._toggleSubmenu(parentMenus, false); 2528 } 2529 }); 2530 button.on('click', (event) => { 2531 // Prevent toggling when clicking with mouse while the form is already open 2532 if (event.detail && event.type === 'click' && form.is(':visible')) return; 2533 form.stop().fadeToggle('fast', () => { 2534 button.attr('aria-expanded', form.is(':visible')); 2535 if(form.is(':visible')) { 2536 form.find('.fl-search-text').focus(); 2537 } 2538 }); 2539 }); 2540 }, 2541 2542 /** 2543 * Adds menu node and post ID to WooCommerce ajax URL requests. 2544 * 2545 * @since 2.5 2546 * @return void 2547 */ 2548 _wooUpdateParams: function() { 2549 if ( 'undefined' !== typeof wc_cart_fragments_params ) { 2550 wc_cart_fragments_params.wc_ajax_url += '&fl-menu-node='+ this.nodeId +'&post-id='+ this.postId; 2551 } 2552 if ( 'undefined' !== typeof wc_add_to_cart_params ) { 2553 wc_add_to_cart_params.wc_ajax_url += '&fl-menu-node='+ this.nodeId +'&post-id='+ this.postId; 2554 } 2555 }, 2556 }; 2557 2558})(jQuery); 2559 2560(function($) { 2561 2562 $(function() { 2563 2564 new FLBuilderMenu({ 2565 id: '5b4e4504e6e84', 2566 type: 'horizontal', 2567 mobile: 'expanded', 2568 mobileBelowRow: false, 2569 mobileFlyout: false, 2570 breakPoints: { 2571 large: 1200, 2572 medium: 992, 2573 small: 768 }, 2574 mobileBreakpoint: 'mobile', 2575 postId : '793', 2576 mobileStacked: true, 2577 submenuIcon: 'none', 2578 flyoutWidth: '250', 2579 }); 2580 2581 }); 2582 2583})(jQuery); 2584 2585/* Start Global Node Custom JS */ 2586 2587/* End Global Node Custom JS */ 2588 2589 2590/* Start Layout Custom JS */ 2591 2592/* End Layout Custom JS */ 2593 2594 2595jQuery(function($) { 2596 2597 $(function() { 2598 $( '.fl-node-5f8f08a94844b .fl-photo-img' ) 2599 .on( 'mouseenter', function( e ) { 2600 $( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' ); 2601 } ) 2602 .on( 'mouseleave', function( e ){ 2603 $( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null ); 2604 } ); 2605 }); 2606 window._fl_string_to_slug_regex = 'a-zA-Z0-9'; 2607}); 2608jQuery(function($) { 2609 2610 $(function() { 2611 $( '.fl-node-5f8f088a0bbca .fl-photo-img' ) 2612 .on( 'mouseenter', function( e ) { 2613 $( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' ); 2614 } ) 2615 .on( 'mouseleave', function( e ){ 2616 $( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null ); 2617 } ); 2618 }); 2619 window._fl_string_to_slug_regex = 'a-zA-Z0-9'; 2620}); 2621jQuery(function($) { 2622 2623 $(function() { 2624 $( '.fl-node-5f8f0872a5094 .fl-photo-img' ) 2625 .on( 'mouseenter', function( e ) { 2626 $( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' ); 2627 } ) 2628 .on( 'mouseleave', function( e ){ 2629 $( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null ); 2630 } ); 2631 }); 2632 window._fl_string_to_slug_regex = 'a-zA-Z0-9'; 2633}); 2634 2635;/* eslint-disable no-undef */ 2636(function($) { 2637 2638 FLBuilderPostGrid = function(settings) 2639 { 2640 this.settings = settings; 2641 this.nodeClass = '.fl-node-' + settings.id; 2642 this.matchHeight = settings.matchHeight; 2643 2644 if ( 'columns' == this.settings.layout ) { 2645 this.wrapperClass = this.nodeClass + ' .fl-post-grid'; 2646 this.postClass = this.nodeClass + ' .fl-post-column'; 2647 } 2648 else { 2649 this.wrapperClass = this.nodeClass + ' .fl-post-' + this.settings.layout; 2650 this.postClass = this.wrapperClass + '-post'; 2651 } 2652 2653 if(this._hasPosts()) { 2654 this._initLayout(); 2655 this._initInfiniteScroll(); 2656 } 2657 }; 2658 2659 FLBuilderPostGrid.prototype = { 2660 2661 settings : {}, 2662 nodeClass : '', 2663 wrapperClass : '', 2664 postClass : '', 2665 gallery : null, 2666 currPage : 1, 2667 totalPages : 1, 2668 2669 _hasPosts: function() 2670 { 2671 return $(this.postClass).length > 0; 2672 }, 2673 2674 _initLayout: function() 2675 { 2676 switch(this.settings.layout) { 2677 2678 case 'columns': 2679 this._columnsLayout(); 2680 break; 2681
2682 case 'grid': 2683 this._gridLayout(); 2684 break; 2685 2686 case 'gallery': 2687 this._galleryLayout(); 2688 break; 2689 } 2690 2691 $(this.postClass).css('visibility', 'visible'); 2692 2693 FLBuilderLayout._scrollToElement( $( this.nodeClass + ' .fl-paged-scroll-to' ) ); 2694 }, 2695 2696 _columnsLayout: function() 2697 { 2698 $(this.wrapperClass).imagesLoaded( $.proxy( function() { 2699 this._gridLayoutMatchHeight(); 2700 }, this ) ); 2701 2702 $( window ).on( 'resize', $.proxy( function(){ 2703 $(this.wrapperClass).imagesLoaded( $.proxy( function() { 2704 this._gridLayoutMatchHeight(); 2705 }, this ) ); 2706 }, this ) ); 2707 }, 2708 2709 _gridLayout: function() 2710 { 2711 var wrap = $(this.wrapperClass); 2712 2713 wrap.masonry({ 2714 columnWidth : this.nodeClass + ' .fl-post-grid-sizer', 2715 gutter : parseInt(this.settings.postSpacing), 2716 isFitWidth : true, 2717 itemSelector : this.postClass, 2718 transitionDuration : 0, 2719 isRTL : this.settings.isRTL 2720 }); 2721 2722 wrap.imagesLoaded( $.proxy( function() { 2723 this._gridLayoutMatchHeight(); 2724 wrap.masonry(); 2725 }, this ) ); 2726 2727 $(window).scroll($.debounce( 25, function(){ 2728 wrap.masonry() 2729 })); 2730 2731 }, 2732 2733 _gridLayoutMatchHeight: function() 2734 { 2735 var highestBox = 0; 2736 2737 if ( ! this._isMatchHeight() ) { 2738 $(this.nodeClass + ' .fl-post-grid-post').css('height', ''); 2739 return; 2740 } 2741 2742 $(this.nodeClass + ' .fl-post-grid-post').css('height', '').each(function(){ 2743 2744 if($(this).height() > highestBox) { 2745 highestBox = $(this).height(); 2746 } 2747 }); 2748 2749 $(this.nodeClass + ' .fl-post-grid-post').height(highestBox); 2750 }, 2751 2752 _isMatchHeight: function(){ 2753 var width = $( window ).width(), 2754 breakpoints = FLBuilderLayoutConfig.breakpoints, 2755 matchLarge = '' != this.matchHeight.large ? this.matchHeight.large : this.matchHeight.default, 2756 matchMedium = '' != this.matchHeight.medium ? this.matchHeight.medium : this.matchHeight.default, 2757 matchSmall = '' != this.matchHeight.responsive ? this.matchHeight.responsive : this.matchHeight.default; 2758 2759 return (width > breakpoints.medium && 1 == this.matchHeight.default) // Must check medium here for backwards compat 2760 || (width > breakpoints.medium && width <= breakpoints.large && 1 == matchLarge) 2761 || (width > breakpoints.small && width <= breakpoints.medium && 1 == matchMedium) 2762 || (width <= breakpoints.small && 1 == matchSmall); 2763 }, 2764 2765 _galleryLayout: function() 2766 { 2767 this.gallery = new FLBuilderGalleryGrid({ 2768 'wrapSelector' : this.wrapperClass, 2769 'itemSelector' : '.fl-post-gallery-post', 2770 'isRTL' : this.settings.isRTL 2771 }); 2772 }, 2773 2774 _initInfiniteScroll: function() 2775 { 2776 var isScroll = 'scroll' == this.settings.pagination || 'load_more' == this.settings.pagination, 2777 pages = $( this.nodeClass + ' .fl-builder-pagination' ).find( 'li .page-numbers:not(.next)' ); 2778 2779 if( pages.length > 1) { 2780 total = pages.last().text().replace( /\D/g, '' ) 2781 this.totalPages = parseInt( total ); 2782 } 2783 2784 if( isScroll && this.totalPages > 1 && 'undefined' === typeof FLBuilder ) { 2785 this._infiniteScroll(); 2786 2787 if( 'load_more' == this.settings.pagination ) { 2788 this._infiniteScrollLoadMore(); 2789 } 2790 } 2791 }, 2792 2793 _infiniteScroll: function() 2794 { 2795 var path = $(this.nodeClass + ' .fl-builder-pagination a.next').attr('href'), 2796 pagePattern = /(.*?(\/|&|\?)paged-[0-9]{1,}(\/|=))([0-9]{1,})+(.*)/, 2797 wpPattern = /^(.*?\/?page\/?)(?:\d+)(.*?$)/, 2798 pageMatched = null, 2799 scrollData = { 2800 navSelector : this.nodeClass + ' .fl-builder-pagination', 2801 nextSelector : this.nodeClass + ' .fl-builder-pagination a.next', 2802 itemSelector : this.postClass, 2803 prefill : true, 2804 bufferPx : 200, 2805 loading : { 2806 msgText : this.settings.loadingText, 2807 finishedMsg : '', 2808 img : FLBuilderLayoutConfig.paths.pluginUrl + 'img/ajax-loader-grey.gif', 2809 speed : 1 2810 } 2811 }; 2812 // Define path since Infinitescroll incremented our custom pagination '/paged-2/2/' to '/paged-3/2/'. 2813 if ( pagePattern.test( path ) ) { 2814 scrollData.path = function( currPage ){ 2815 pageMatched = path.match( pagePattern ); 2816 path = pageMatched[1] + currPage + pageMatched[5]; 2817 return path; 2818 } 2819 } 2820 else if ( wpPattern.test( path ) ) { 2821 scrollData.path = path.match( wpPattern ).slice( 1 ); 2822 } 2823 2824 $(this.postClass).first().parent().infinitescroll( scrollData, $.proxy(this._infiniteScrollComplete, this) ); 2825 2826 setTimeout(function(){ 2827 $(window).trigger('resize'); 2828 }, 100); 2829 }, 2830 2831 _infiniteScrollComplete: function(elements) 2832 { 2833 var wrap = $(this.wrapperClass); 2834 2835 elements = $(elements); 2836 2837 if(this.settings.layout == 'columns') { 2838 wrap.imagesLoaded( $.proxy( function() { 2839 $( '#infscr-loading' ).remove(); 2840 this._gridLayoutMatchHeight(); 2841 elements.css('visibility', 'visible'); 2842 }, this ) ); 2843 } 2844 else if(this.settings.layout == 'grid') { 2845 wrap.imagesLoaded( $.proxy( function() { 2846 this._gridLayoutMatchHeight(); 2847 wrap.masonry('appended', elements); 2848 wrap.masonry(); 2849 elements.css('visibility', 'visible'); 2850 }, this ) ); 2851 } 2852 else if(this.settings.layout == 'gallery') { 2853 this.gallery.resize(); 2854 elements.css('visibility', 'visible'); 2855 } 2856 2857 if( 'load_more' == this.settings.pagination ) { 2858 $( this.wrapperClass + ' .fl-post-grid-sizer.masonry-brick' ).appendTo( this.wrapperClass ); 2859 $( '#infscr-loading' ).appendTo( this.wrapperClass ); 2860 } 2861 2862 this.currPage++; 2863 2864 this._removeLoadMoreButton(); 2865 2866 // trigger an event 2867 node = $(wrap).closest('.fl-module-post-grid').data('node') 2868 $('.fl-node-' + node).trigger( 'gridScrollComplete', this ); 2869 }, 2870 2871 _infiniteScrollLoadMore: function() 2872 { 2873 var wrap = $( this.postClass ).first().parent(); 2874 2875 $( window ).unbind( '.infscr' ); 2876
2877 $(this.nodeClass + ' .fl-builder-pagination-load-more .fl-button').on( 'click', function(){ 2878 if( $( '#infscr-loading' ).length ) { 2879 $( '#infscr-loading' ).remove(); 2880 } 2881 2882 wrap.infinitescroll( 'retrieve' ); 2883 return false; 2884 }); 2885 }, 2886 2887 _removeLoadMoreButton: function() 2888 { 2889 if ( 'load_more' == this.settings.pagination && this.totalPages == this.currPage ) { 2890 $( this.nodeClass + ' .fl-builder-pagination-load-more' ).remove(); 2891 } 2892 } 2893 }; 2894 2895})(jQuery); 2896(function($) { 2897 2898 $(function() { 2899 2900 new FLBuilderPostGrid({ 2901 id: '5baa61fac7a35', 2902 layout: 'feed', 2903 pagination: 'numbers', 2904 postSpacing: '60', 2905 postWidth: '300', 2906 matchHeight: { 2907 default : '1', 2908 large : '', 2909 medium : '', 2910 responsive : '' 2911 }, 2912 isRTL: false, 2913 loadingText: 'Loading...' 2914 }); 2915 2916 }); 2917 2918})(jQuery); 2919 2920/* Start Layout Custom JS */ 2921 2922/* End Layout Custom JS */ 2923 2924 2925jQuery(function($) { 2926 2927 $(function() { 2928 $( '.fl-node-5da892bbc0b4e .fl-photo-img' ) 2929 .on( 'mouseenter', function( e ) { 2930 $( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' ); 2931 } ) 2932 .on( 'mouseleave', function( e ){ 2933 $( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null ); 2934 } ); 2935 }); 2936 window._fl_string_to_slug_regex = 'a-zA-Z0-9'; 2937}); 2938 2939(function($) { 2940 2941 $(function() { 2942 2943 new FLBuilderMenu({ 2944 id: '5da892bbc0b4a', 2945 type: 'horizontal', 2946 mobile: 'hamburger', 2947 mobileBelowRow: true, 2948 mobileFlyout: false, 2949 breakPoints: { 2950 large: 1200, 2951 medium: 992, 2952 small: 768 }, 2953 mobileBreakpoint: 'medium-mobile', 2954 postId : '15756', 2955 mobileStacked: true, 2956 submenuIcon: 'none', 2957 flyoutWidth: '250', 2958 }); 2959 2960 }); 2961 2962})(jQuery); 2963 2964/* Start Layout Custom JS */ 2965 2966/* End Layout Custom JS */ 2967 2968(function($){ 2969 2970 /** 2971 * Helper class for header layout logic. 2972 * 2973 * @since 1.0 2974 * @class FLThemeBuilderHeaderLayout 2975 */ 2976 FLThemeBuilderHeaderLayout = { 2977 2978 /** 2979 * A reference to the window object for this page. 2980 * 2981 * @since 1.0 2982 * @property {Object} win 2983 */ 2984 win : null, 2985 2986 /** 2987 * A reference to the body object for this page. 2988 * 2989 * @since 1.0 2990 * @property {Object} body 2991 */ 2992 body : null, 2993 2994 /** 2995 * A reference to the header object for this page. 2996 * 2997 * @since 1.0 2998 * @property {Object} header 2999 */ 3000 header : null, 3001 3002 /** 3003 * Whether this header overlays the content or not. 3004 * 3005 * @since 1.0 3006 * @property {Boolean} overlay 3007 */ 3008 overlay : false, 3009 3010 /** 3011 * Whether the page has the WP admin bar or not. 3012 * 3013 * @since 1.0 3014 * @property {Boolean} hasAdminBar 3015 */ 3016 hasAdminBar : false, 3017 3018 /** 3019 * Breakpoint for when the sticky header should apply. 3020 * 3021 * @since 1.4 3022 * @property {String} stickyOn 3023 */ 3024 stickyOn: '', 3025 3026 /** 3027 * A reference of the sticky and shrink header breakpoint. 3028 * 3029 * @since 1.2.5 3030 * @property {Number} breakpointWidth 3031 */ 3032 breakpointWidth: 0, 3033 3034 /** 3035 * Whether this header can be pinned from page load. 3036 * 3037 * @since 1.6.1 3038 * @property {Boolean} pinnable 3039 */ 3040 pinnable : false, 3041 3042 /** 3043 * Initializes header layout logic. 3044 * 3045 * @since 1.0 3046 * @method init 3047 */ 3048 init: function() 3049 { 3050 var editing = $( 'html.fl-builder-edit' ).length, 3051 header = $( '.fl-builder-content[data-type=header]' ), 3052 menuModule = header.find( '.fl-module-menu' ), 3053 breakpoint = null, 3054 adminBarHeight = 0; 3055 3056 if ( ! editing && header.length ) { 3057 3058 header.imagesLoaded( $.proxy( function() { 3059 3060 this.win = $( window ); 3061 this.body = $( 'body' ); 3062 this.header = header.eq( 0 ); 3063 this.overlay = !! Number( header.attr( 'data-overlay' ) ); 3064 this.hasAdminBar = !! $( 'body.admin-bar' ).length; 3065 this.stickyOn = this.header.data( 'sticky-on' ); 3066 breakpoint = this.header.data( 'sticky-breakpoint' ); 3067 3068 if ( '' == this.stickyOn ) { 3069 if ( typeof FLBuilderLayoutConfig.breakpoints[ breakpoint ] !== undefined ) { 3070 this.breakpointWidth = FLBuilderLayoutConfig.breakpoints[ breakpoint ]; 3071 } 3072 else { 3073 this.breakpointWidth = FLBuilderLayoutConfig.breakpoints.medium; 3074 } 3075 } 3076 3077 if ( Number( header.attr( 'data-sticky' ) ) ) { 3078 3079 this.header.data( 'original-top', this.header.offset().top ); 3080 3081 // The pin decision is made once here, while the header offset and the admin bar height are read from the same layout. 3082 adminBarHeight = $( '#wpadminbar' ).length ? $( '#wpadminbar' ).outerHeight() : 0; 3083 this.pinnable = this.overlay && this.header.data( 'original-top' ) <= adminBarHeight; 3084 3085 this.win.on( 'resize', $.throttle( 500, $.proxy( this._initSticky, this ) ) ); 3086 this._initSticky(); 3087 3088 } 3089 3090 }, this ) ); 3091 } 3092 }, 3093 3094 /** 3095 * Initializes sticky logic for a header. 3096 * 3097 * @since 1.0 3098 * @access private 3099 * @method _initSticky 3100 */ 3101 _initSticky: function( e ) 3102 {
3103 var header = $('.fl-builder-content[data-type=header]'), 3104 windowSize = this.win.width(), 3105 makeSticky = false; 3106 3107 makeSticky = this._makeWindowSticky( windowSize ); 3108 if ( makeSticky || ( this.breakpointWidth > 0 && windowSize >= this.breakpointWidth ) ) { 3109 this.win.on( 'scroll.fl-theme-builder-header-sticky', $.proxy( this._doSticky, this ) ); 3110 this._initPin(); 3111 3112 // Apply the sticky state on initial load when the page is 3113 // already scrolled past the header — e.g. when the browser 3114 // restores the scroll position on refresh. Overlay headers are 3115 // out of scope here; this mirrors the shrink-on-load behavior 3116 // in _initShrink(). 3117 if ( ! e && ! this.overlay && ! this.header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 3118 var loadWinTop = this.win.scrollTop(), 3119 loadHeaderTop = Math.floor( this.header.data( 'original-top' ) ); 3120 3121 if ( this.hasAdminBar && windowSize > 600 ) { 3122 loadWinTop += Math.floor( $( '#wpadminbar' ).outerHeight() ); 3123 } 3124 3125 if ( loadWinTop > loadHeaderTop ) { 3126 this.header.addClass( 'fl-theme-builder-header-sticky' ); 3127 this.header.addClass( 'fl-theme-builder-header-scrolled' ); 3128 this._adjustHeaderHeight(); 3129 this._adjustStickyHeaderWidth(); 3130 } 3131 } 3132 3133 // 3134 // Check if Event Type is 'resize' then invoke this._doSticky() 3135 // only if the 'fl-theme-builder-header-sticky' class is already present. 3136 // 3137 if ( e && 'resize' === e.type ) { 3138 if ( this.header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 3139 this._doSticky( e ); 3140 } 3141 this._adjustStickyHeaderWidth(); 3142 } 3143 3144 if ( Number( header.attr( 'data-shrink' ) ) ) { 3145 this.header.data( 'original-height', this.header.outerHeight() ); 3146 this.win.on( 'resize', $.throttle( 500, $.proxy( this._initShrink, this ) ) ); 3147 this._initShrink(); 3148 } 3149 3150 this._initFlyoutMenuFix( e ); 3151 } else { 3152 this.win.off( 'scroll.fl-theme-builder-header-sticky' ); 3153 this.win.off( 'resize.fl-theme-builder-header-sticky' ); 3154 3155 this.header.removeClass( 'fl-theme-builder-header-sticky' ); 3156 this.header.removeClass( 'fl-theme-builder-header-pinned' ); 3157 this.header.removeAttr( 'style' ); 3158 this.header.parent().css( 'padding-top', '0' ); 3159 } 3160 }, 3161 3162 /** 3163 * Pins an overlay header with fixed positioning from page load, avoiding a position change that a scroll handler cannot apply before the compositor paints. 3164 * 3165 * @since 1.6.1 3166 * @access private 3167 * @method _initPin 3168 */ 3169 _initPin: function() 3170 { 3171 this.header.toggleClass( 'fl-theme-builder-header-pinned', this.pinnable ); 3172 3173 if ( ! this.pinnable ) { 3174 return; 3175 } 3176 3177 // Fixed positioning resolves the width against the viewport, so boxed layouts require their maximum width immediately. 3178 this._adjustStickyHeaderWidth(); 3179 3180 // The header is visible from the first paint, so the scrolled state must reflect any restored scroll position. 3181 if ( this.win.scrollTop() > Math.floor( this.header.data( 'original-top' ) ) ) { 3182 this.header.addClass( 'fl-theme-builder-header-scrolled' ); 3183 } 3184 }, 3185 3186 /** 3187 * Check if Header should be sticky at a particular Window size. 3188 * 3189 * @since 1.4 3190 * @access private 3191 * @param widowSize 3192 * @method _makeWindowSticky 3193 */ 3194 _makeWindowSticky: function ( windowSize ) 3195 { 3196 var makeSticky = false; 3197 3198 switch (this.stickyOn) { 3199 case 'xl': 3200 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['large']; 3201 break; 3202 case '': // Default 3203 case 'desktop': 3204 makeSticky = windowSize >= FLBuilderLayoutConfig.breakpoints['medium']; 3205 break; 3206 case 'desktop-medium': 3207 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['small']; 3208 break; 3209 case 'large': 3210 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['medium'] && windowSize <= FLBuilderLayoutConfig.breakpoints['large']; 3211 break; 3212 case 'large-medium': 3213 makeSticky = windowSize > FLBuilderLayoutConfig.breakpoints['small'] && windowSize <= FLBuilderLayoutConfig.breakpoints['large']; 3214 break; 3215 case 'medium': 3216 makeSticky = ( windowSize <= FLBuilderLayoutConfig.breakpoints['medium'] && windowSize > FLBuilderLayoutConfig.breakpoints['small'] ); 3217 break; 3218 case 'medium-mobile':
3219 makeSticky = (windowSize <= FLBuilderLayoutConfig.breakpoints['medium']); 3220 break; 3221 case 'mobile': 3222 makeSticky = (windowSize <= FLBuilderLayoutConfig.breakpoints['small']); 3223 break; 3224 case 'all': 3225 makeSticky = true; 3226 break; 3227 } 3228 3229 return makeSticky; 3230 }, 3231 3232 /** 3233 * Sticks the header when the page is scrolled. 3234 * 3235 * @since 1.0 3236 * @access private 3237 * @method _doSticky 3238 */ 3239 _doSticky: function( e ) 3240 { 3241 var winTop = Math.floor( this.win.scrollTop() ), 3242 headerTop = Math.floor( this.header.data( 'original-top' ) ), 3243 hasStickyClass = this.header.hasClass( 'fl-theme-builder-header-sticky' ), 3244 hasScrolledClass = this.header.hasClass( 'fl-theme-builder-header-scrolled' ), 3245 beforeHeader = this.header.prevAll( '.fl-builder-content' ), 3246 bodyTopPadding = parseInt( jQuery('body').css('padding-top') ), 3247 winBarHeight = $('#wpadminbar').length ? $('#wpadminbar').outerHeight() : 0, 3248 headerHeight = 0; 3249 3250 if ( isNaN( bodyTopPadding ) ) { 3251 bodyTopPadding = 0; 3252 } 3253 3254 if ( this.hasAdminBar && this.win.width() > 600 ) { 3255 winTop += Math.floor( winBarHeight ); 3256 } 3257 3258 if ( winTop > headerTop ) { 3259 if ( ! hasStickyClass ) { 3260 if ( e && ( 'scroll' === e.type || 'smartscroll' === e.type ) ) { 3261 this.header.addClass( 'fl-theme-builder-header-sticky' ); 3262 if ( this.overlay && beforeHeader.length ) { 3263 this.header.css( 'top', winBarHeight); 3264 } 3265 } 3266 3267 if ( ! this.overlay ) { 3268 this._adjustHeaderHeight(); 3269 } 3270 } 3271 } 3272 else if ( hasStickyClass ) { 3273 this.header.removeClass( 'fl-theme-builder-header-sticky' ); 3274 this.header.removeAttr( 'style' ); 3275 this.header.parent().css( 'padding-top', '0' ); 3276 } 3277 3278 this._adjustStickyHeaderWidth(); 3279 3280 if ( winTop > headerTop ) { 3281 if ( ! hasScrolledClass ) { 3282 this.header.addClass( 'fl-theme-builder-header-scrolled' ); 3283 } 3284 } else if ( hasScrolledClass ) { 3285 this.header.removeClass( 'fl-theme-builder-header-scrolled' ); 3286 } 3287 3288 this._flyoutMenuFix( e ); 3289 }, 3290 3291 /** 3292 * Initializes flyout menu fixes on sticky header. 3293 * 3294 * @since 1.4.1 3295 * @method _initFlyoutMenuFix 3296 */ 3297 _initFlyoutMenuFix: function( e ) { 3298 var header = this.header, 3299 menuModule = header.closest( '.fl-menu' ), 3300 flyoutMenu = menuModule.find( '.fl-menu-mobile-flyout' ), 3301 isPushMenu = menuModule.hasClass( 'fl-menu-responsive-flyout-push' ) || menuModule.hasClass( 'fl-menu-responsive-flyout-push-opacity' ), 3302 isOverlay = menuModule.hasClass( 'fl-menu-responsive-flyout-overlay' ), 3303 flyoutPos = menuModule.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 3304 flyoutParent = header.parent().is( 'header' ) ? header.parent().parent() : header.parent(); 3305 isFullWidth = this.win.width() === header.width(), 3306 flyoutLayout = '', 3307 activePos = 250, 3308 headerPos = 0; 3309 3310 if ( ! flyoutMenu.length ) { 3311 return; 3312 } 3313 3314 if ( this.win.width() > header.parent().width() ) { 3315 headerPos = ( this.win.width() - header.width() ) / 2; 3316 } 3317 3318 if ( isOverlay ) { 3319 activePos = headerPos; 3320 } 3321 else if ( isPushMenu ) { 3322 activePos = activePos + headerPos; 3323 } 3324 flyoutMenu.data( 'activePos', activePos ); 3325 3326 if ( isPushMenu ) { 3327 flyoutLayout = 'push-' + flyoutPos; 3328 } 3329 else if ( isOverlay ) { 3330 flyoutLayout = 'overlay-' + flyoutPos; 3331 } 3332 3333 if ( isPushMenu && ! $( 'html' ).hasClass( 'fl-theme-builder-has-flyout-menu' ) ) { 3334 $( 'html' ).addClass( 'fl-theme-builder-has-flyout-menu' ); 3335 } 3336 3337 if ( ! flyoutParent.hasClass( 'fl-theme-builder-flyout-menu-' + flyoutLayout ) ) { 3338 flyoutParent.addClass( 'fl-theme-builder-flyout-menu-' + flyoutLayout ); 3339 } 3340 3341 if ( ! header.hasClass( 'fl-theme-builder-flyout-menu-overlay' ) && isOverlay ) { 3342 header.addClass( 'fl-theme-builder-flyout-menu-overlay' ); 3343 } 3344 3345 if ( ! header.hasClass( 'fl-theme-builder-header-full-width' ) && isFullWidth ) { 3346 header.addClass( 'fl-theme-builder-header-full-width' ); 3347 } 3348 else if ( ! isFullWidth ) { 3349 header.removeClass( 'fl-theme-builder-header-full-width' ); 3350 } 3351 3352 menuModule.on( 'click', '.fl-menu-mobile-toggle', $.proxy( function( event ){ 3353 if ( menuModule.find( '.fl-menu-mobile-toggle.fl-active' ).length ) {
3354 $( 'html' ).addClass( 'fl-theme-builder-flyout-menu-active' ); 3355 event.stopImmediatePropagation(); 3356 } 3357 else { 3358 $( 'html' ).removeClass( 'fl-theme-builder-flyout-menu-active' ); 3359 } 3360 3361 this._flyoutMenuFix( event ); 3362 }, this ) ); 3363 }, 3364 3365 /** 3366 * Fix flyout menu inside the sticky header. 3367 * 3368 * @since 1.4.1 3369 * @method _flyoutMenuFix 3370 */ 3371 _flyoutMenuFix: function( e ){ 3372 var header = this.header, 3373 menuModule = $( e.target ).closest( '.fl-menu' ), 3374 flyoutMenu = menuModule.find( '.fl-menu-mobile-flyout' ), 3375 flyoutPos = menuModule.hasClass( 'fl-flyout-right' ) ? 'right' : 'left', 3376 menuOpacity = menuModule.find( '.fl-menu-mobile-opacity' ), 3377 isScroll = 'undefined' !== typeof e && 'scroll' === e.handleObj.type, 3378 activePos = 'undefined' !== typeof flyoutMenu.data( 'activePos' ) ? flyoutMenu.data( 'activePos' ) : 0, 3379 headerPos = ( this.win.width() - header.width() ) / 2, 3380 inactivePos = headerPos > 0 ? activePos + 4 : 254; 3381 3382 if ( ! flyoutMenu.length ) { 3383 return; 3384 } 3385 3386 if ( this.overlay ) { 3387 return; 3388 } 3389 3390 if( $( '.fl-theme-builder-flyout-menu-active' ).length ) { 3391 3392 if ( isScroll && ! flyoutMenu.hasClass( 'fl-menu-disable-transition' ) ) { 3393 flyoutMenu.addClass( 'fl-menu-disable-transition' ); 3394 } 3395 3396 if ( header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 3397 if ( ! isScroll ) { 3398 setTimeout( $.proxy( function(){ 3399 flyoutMenu.css( flyoutPos, '-' + activePos + 'px' ); 3400 }, this ), 1 ); 3401 } 3402 else { 3403 flyoutMenu.css( flyoutPos, '-' + activePos + 'px' ); 3404 } 3405 } 3406 else { 3407 flyoutMenu.css( flyoutPos, '0px' ); 3408 } 3409 } 3410 else { 3411 if ( flyoutMenu.hasClass( 'fl-menu-disable-transition' ) ) { 3412 flyoutMenu.removeClass( 'fl-menu-disable-transition' ); 3413 } 3414 3415 if ( header.hasClass( 'fl-theme-builder-flyout-menu-overlay' ) && headerPos > 0 && headerPos < 250 ) { 3416 if ( header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 3417 inactivePos = headerPos + 254; 3418 } 3419 else { 3420 inactivePos = 254; 3421 } 3422 } 3423 3424 if ( e && e.type === 'resize' ) { 3425 inactivePos = headerPos + 254; 3426 } 3427 3428 flyoutMenu.css( flyoutPos, '-' + inactivePos + 'px' ); 3429 } 3430 3431 if ( e && menuModule.is('.fl-menu-responsive-flyout-overlay') && $.infinitescroll ) { 3432 e.stopImmediatePropagation(); 3433 } 3434 3435 if( menuOpacity.length ) { 3436 if ( header.hasClass( 'fl-theme-builder-header-sticky' ) ) { 3437 if ( '0px' === menuOpacity.css( 'left' ) ) { 3438 menuOpacity.css( 'left', '-' + headerPos + 'px' ); 3439 } 3440 } 3441 else { 3442 menuOpacity.css( 'left', '' ); 3443 } 3444 } 3445 }, 3446 3447 /** 3448 * Adjust sticky header width if BB Theme Boxed Layout is used. 3449 * 3450 * @since 1.4 3451 * @access private 3452 * @method _adjustStickyHeaderWidth 3453 */ 3454 _adjustStickyHeaderWidth: function () { 3455 if ( $('body').hasClass( 'fl-fixed-width' ) ) { 3456 var parentWidth = this.header.parent().width(); 3457 3458 // Better if this is set in the stylesheet file. 3459 if ( this.win.width() >= 992 ) { 3460 this.header.css({ 3461 'margin': '0 auto', 3462 'max-width': parentWidth, 3463 }); 3464 } 3465 else { 3466 this.header.css({ 3467 'margin': '', 3468 'max-width': '', 3469 }); 3470 } 3471 } 3472 }, 3473 3474 /** 3475 * Adjust Sticky Header Height 3476 * 3477 * @since 1.4 3478 * @access private 3479 * @method _adjustHeaderHeight 3480 */ 3481 _adjustHeaderHeight: function () { 3482 var beforeHeader = this.header.prevAll('.fl-builder-content'), 3483 beforeHeaderHeight = 0, 3484 beforeHeaderFix = 0, 3485 headerHeight = Math.floor( this.header.outerHeight() ), 3486 bodyTopPadding = parseInt( $( 'body' ).css( 'padding-top' ) ), 3487 wpAdminBarHeight = 0, 3488 totalHeaderHeight = 0; 3489 3490 if ( isNaN( bodyTopPadding ) ) { 3491 bodyTopPadding = 0; 3492 } 3493 3494 if ( beforeHeader.length ) { 3495 $.each( beforeHeader, function() { 3496 beforeHeaderHeight += Math.floor( $(this).outerHeight() ); 3497 }); 3498 // Subtract this value from the header parent's top padding. 3499 beforeHeaderFix = 2; 3500 } 3501 3502 if ( this.hasAdminBar && this.win.width() <= 600 ) { 3503 wpAdminBarHeight = Math.floor( $('#wpadminbar').outerHeight() ); 3504 } 3505 3506 totalHeaderHeight = Math.floor( beforeHeaderHeight + headerHeight); 3507 3508 if ( headerHeight > 0 ) { 3509 var headerParent = this.header.parent(), 3510 headerParentTopPadding = 0; 3511 3512 // If the header's parent container is the BODY tag ignore its top padding. 3513 if ( $( headerParent ).is('body') ) { 3514 headerParentTopPadding = Math.floor( headerHeight - wpAdminBarHeight ); 3515 } else { 3516 headerParentTopPadding = Math.floor( headerHeight - bodyTopPadding - wpAdminBarHeight ); 3517 } 3518 3519 $( headerParent ).css( 'padding-top', ( headerParentTopPadding - beforeHeaderFix ) + 'px' ); 3520 3521 this.header.css({ 3522 '-webkit-transform': 'translate(0px, -' + totalHeaderHeight + 'px)', 3523 '-ms-transform': 'translate(0px, -' + totalHeaderHeight + 'px)', 3524 'transform': 'translate(0px, -' + totalHeaderHeight + 'px)' 3525 }); 3526 3527 } 3528 3529 }, 3530 3531 /** 3532 * Initializes shrink logic for a header. 3533 * 3534 * @since 1.0 3535 * @access private 3536 * @method _initShrink 3537 */ 3538 _initShrink: function( e ) 3539 { 3540 if ( this.win.width() >= this.breakpointWidth ) {
3541 this.win.on( 'scroll.fl-theme-builder-header-shrink', $.proxy( this._doShrink, this ) ); 3542 this._setImageMaxHeight(); 3543 3544 if ( this.win.scrollTop() > 0 ){ 3545 this._doShrink(); 3546 } 3547 3548 } else { 3549 this.header.parent().css( 'padding-top', '0' ); 3550 this.win.off( 'scroll.fl-theme-builder-header-shrink' ); 3551 this._removeShrink(); 3552 this._removeImageMaxHeight(); 3553 } 3554 }, 3555 3556 /** 3557 * Shrinks the header when the page is scrolled. 3558 * 3559 * @since 1.0 3560 * @access private 3561 * @method _doShrink 3562 */ 3563 _doShrink: function( e ) 3564 { 3565 var winTop = this.win.scrollTop(), 3566 headerTop = this.header.data('original-top'), 3567 headerHeight = this.header.data('original-height'), 3568 shrinkImageHeight = this.header.data('shrink-image-height'), 3569 windowSize = this.win.width(), 3570 makeSticky = this._makeWindowSticky( windowSize ), 3571 hasClass = this.header.hasClass( 'fl-theme-builder-header-shrink' ); 3572 3573 3574 if ( this.hasAdminBar ) { 3575 winTop += 32; 3576 } 3577 3578 if ( makeSticky && ( winTop > headerTop + headerHeight ) ) { 3579 if ( ! hasClass ) { 3580 3581 this.header.addClass( 'fl-theme-builder-header-shrink' ); 3582 3583 // Shrink images but don't include lightbox and menu images. 3584 this.header.find('img').each( function( i ) { 3585 var image = $( this ), 3586 maxMegaMenu = image.closest( '.max-mega-menu' ).length, 3587 imageInLightbox = image.closest( '.fl-button-lightbox-content' ).length, 3588 imageInNavMenu = image.closest( 'li.menu-item' ).length; 3589 3590 if ( ! ( imageInLightbox || imageInNavMenu || maxMegaMenu ) ) { 3591 image.css( 'max-height', shrinkImageHeight ); 3592 } 3593 3594 }); 3595 3596 this.header.find( '.fl-row-content-wrap' ).each( function() { 3597 3598 var row = $( this ); 3599 3600 if ( parseInt( row.css( 'padding-bottom' ) ) > 5 ) { 3601 row.addClass( 'fl-theme-builder-header-shrink-row-bottom' ); 3602 } 3603 3604 if ( parseInt( row.css( 'padding-top' ) ) > 5 ) { 3605 row.addClass( 'fl-theme-builder-header-shrink-row-top' ); 3606 } 3607 } ); 3608 3609 this.header.find( '.fl-module' ).each( function() { 3610 3611 var module = $( this ).find( '.fl-module-content' ).length ? $( this ).find( '.fl-module-content' ) : $( this ); 3612 3613 if ( parseInt( module.css( 'margin-bottom' ) ) > 5 ) { 3614 module.addClass( 'fl-theme-builder-header-shrink-module-bottom' ); 3615 } 3616 3617 if ( parseInt( module.css( 'margin-top' ) ) > 5 ) { 3618 module.addClass( 'fl-theme-builder-header-shrink-module-top' ); 3619 } 3620 } ); 3621 } 3622 } else if (hasClass) { 3623 this.header.find( 'img' ).css( 'max-height', '' ); 3624 this._removeShrink(); 3625 } 3626 3627 // Fixes Shrink header issue with BB Theme when window is scrolled then resized and back. 3628 if ( 'undefined' === typeof( e ) && $('body').hasClass( 'fl-fixed-width' ) ) { 3629 if ( ! this.overlay ) { 3630 this._adjustHeaderHeight(); 3631 } 3632 } 3633 3634 }, 3635 3636 /** 3637 * Removes the header shrink effect. 3638 * 3639 * @since 1.0 3640 * @access private 3641 * @method _removeShrink 3642 */ 3643 _removeShrink: function() 3644 { 3645 var rows = this.header.find( '.fl-row-content-wrap' ), 3646 modules = this.header.find('.fl-module, .fl-module-content'); 3647 3648 rows.removeClass( 'fl-theme-builder-header-shrink-row-bottom' ); 3649 rows.removeClass( 'fl-theme-builder-header-shrink-row-top' ); 3650 modules.removeClass( 'fl-theme-builder-header-shrink-module-bottom' ); 3651 modules.removeClass( 'fl-theme-builder-header-shrink-module-top' ); 3652 this.header.removeClass( 'fl-theme-builder-header-shrink' ); 3653 }, 3654 3655 /** 3656 * Adds max height to images in modules for smooth scrolling. 3657 * 3658 * @since 1.1.1 3659 * @access private 3660 * @method _setImageMaxHeight 3661 */ 3662 _setImageMaxHeight: function() 3663 { 3664 var head = $( 'head' ), 3665 stylesId = 'fl-header-styles-' + this.header.data( 'post-id' ), 3666 styles = '', 3667 images = this.header.find( '.fl-module img' ); 3668 3669 if ( $( '#' + stylesId ).length ) { 3670 return; 3671 } 3672 3673 images.each( function( i ) { 3674 var image = $( this ), 3675 height = image.height(), 3676 node = image.closest( '.fl-module' ).data( 'node' ), 3677 className = 'fl-node-' + node + '-img-' + i, 3678 maxMegaMenu = image.closest( '.max-mega-menu' ).length, 3679 imageInLightbox = image.closest( '.fl-button-lightbox-content' ).length, 3680 imageInNavMenu = image.closest( 'li.menu-item' ).length; 3681 3682 if ( ! ( imageInLightbox || imageInNavMenu || maxMegaMenu ) ) { 3683 image.addClass( className ); 3684 styles += '.' + className + ' { max-height: ' + ( height ? height : image[0].height ) + 'px }'; 3685 } 3686 3687 } ); 3688
3689 if ( '' !== styles ) { 3690 head.append( '<style id="' + stylesId + '">' + styles + '</style>' ); 3691 } 3692 }, 3693 3694 /** 3695 * Removes max height on images in modules for smooth scrolling. 3696 * 3697 * @since 1.1.1 3698 * @access private 3699 * @method _removeImageMaxHeight 3700 */ 3701 _removeImageMaxHeight: function() 3702 { 3703 $( '#fl-header-styles-' + this.header.data( 'post-id' ) ).remove(); 3704 }, 3705 }; 3706 3707 $( function() { FLThemeBuilderHeaderLayout.init(); } ); 3708 3709})(jQuery); 3710
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.