1var wpAjaxUrl = 'https://www.vanderbilt.edu/mihow/wp-admin/admin-ajax.php';var flBuilderUrl = 'https://www.vanderbilt.edu/mihow/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.vanderbilt.edu/mihow/wp-content/plugins/bb-plugin/', 9 wpAjaxUrl : 'https://www.vanderbilt.edu/mihow/wp-admin/admin-ajax.php' 10 }, 11 breakpoints : { 12 small : 768, 13 medium : 992 }, 14 waypoint: { 15 offset: 80 16 } 17}; 18(function($){ 19 20 if(typeof FLBuilderLayout != 'undefined') { 21 return; 22 } 23 24 /** 25 * Helper class with generic logic for a builder layout. 26 * 27 * @class FLBuilderLayout 28 * @since 1.0 29 */ 30 FLBuilderLayout = { 31 32 /** 33 * Initializes a builder layout. 34 * 35 * @since 1.0 36 * @method init 37 */ 38 init: function() 39 { 40 // Destroy existing layout events. 41 FLBuilderLayout._destroy(); 42 43 // Init CSS classes. 44 FLBuilderLayout._initClasses(); 45 46 // Init backgrounds. 47 FLBuilderLayout._initBackgrounds(); 48 49 // Only init if the builder isn't active. 50 if ( 0 === $('.fl-builder-edit').length ) { 51 52 // Init module animations. 53 FLBuilderLayout._initModuleAnimations(); 54 55 // Init anchor links. 56 FLBuilderLayout._initAnchorLinks(); 57 58 // Init the browser hash. 59 FLBuilderLayout._initHash(); 60 61 // Init forms. 62 FLBuilderLayout._initForms(); 63 } 64 }, 65 66 /** 67 * Public method for refreshing Wookmark or MosaicFlow galleries 68 * within an element. 69 * 70 * @since 1.7.4 71 * @method refreshGalleries 72 */ 73 refreshGalleries: function( element ) 74 { 75 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 76 mfContent = $element.find( '.fl-mosaicflow-content' ), 77 wmContent = $element.find( '.fl-gallery' ), 78 mfObject = null; 79 80 if ( mfContent ) { 81 82 mfObject = mfContent.data( 'mosaicflow' ); 83 84 if ( mfObject ) { 85 mfObject.columns = $( [] ); 86 mfObject.columnsHeights = []; 87 mfContent.data( 'mosaicflow', mfObject ); 88 mfContent.mosaicflow( 'refill' ); 89 } 90 } 91 if ( wmContent ) { 92 wmContent.trigger( 'refreshWookmark' ); 93 } 94 }, 95 96 /** 97 * Public method for refreshing Masonry within an element 98 * 99 * @since 1.8.1 100 * @method refreshGridLayout 101 */ 102 refreshGridLayout: function( element ) 103 { 104 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 105 msnryContent = $element.find('.masonry'); 106 107 if ( msnryContent.length ) { 108 msnryContent.masonry('layout'); 109 } 110 }, 111 112 /** 113 * Public method for reloading BxSlider within an element 114 * 115 * @since 1.8.1 116 * @method reloadSlider 117 */ 118 reloadSlider: function( element ) 119 { 120 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 121 bxContent = $element.find('.bx-viewport > div').eq(0), 122 bxObject = null; 123 124 if ( bxContent.length ) { 125 bxObject = bxContent.data( 'bxSlider'); 126 if ( bxObject ) { 127 bxObject.reloadSlider(); 128 } 129 } 130 }, 131 132 /** 133 * Public method for resizing WP audio player 134 * 135 * @since 1.8.2 136 * @method resizeAudio 137 */ 138 resizeAudio: function( element ) 139 { 140 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 141 audioPlayers = $element.find('.wp-audio-shortcode.mejs-audio'), 142 player = null, 143 mejsPlayer = null, 144 rail = null, 145 railWidth = 400; 146 147 if ( audioPlayers.length && typeof mejs !== 'undefined' ) { 148 audioPlayers.each(function(){ 149 player = $(this); 150 mejsPlayer = mejs.players[player.attr('id')]; 151 rail = player.find('.mejs-controls .mejs-time-rail'); 152 var innerMejs = player.find('.mejs-inner'), 153 total = player.find('.mejs-controls .mejs-time-total'); 154 155 if ( typeof mejsPlayer !== 'undefined' ) { 156 railWidth = Math.ceil(player.width() * 0.8); 157 158 if ( innerMejs.length ) { 159 160 rail.css('width', railWidth +'px!important'); 161 //total.width(rail.width() - 10); 162 163 mejsPlayer.options.autosizeProgress = true; 164 165 // webkit has trouble doing this without a delay 166 setTimeout(function () { 167 mejsPlayer.setControlsSize(); 168 }, 50); 169 170 player.find('.mejs-inner').css({ 171 visibility: 'visible', 172 height: 'inherit' 173 }); 174 } 175 } 176 }); 177 } 178 }, 179 180 /** 181 * Public method for preloading WP audio player when it's inside the hidden element 182 * 183 * @since 1.8.2 184 * @method preloadAudio 185 */ 186 preloadAudio: function(element) 187 { 188 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ), 189 contentWrap = $element.closest('.fl-accordion-item'), 190 audioPlayers = $element.find('.wp-audio-shortcode.mejs-audio'); 191 192 if ( ! contentWrap.hasClass('fl-accordion-item-active') && audioPlayers.find('.mejs-inner').length ) { 193 audioPlayers.find('.mejs-inner').css({ 194 visibility : 'hidden', 195 height: 0 196 }); 197 } 198 }, 199 200 /** 201 * Public method for resizing slideshow momdule within the tab 202 * 203 * @since 1.10.5 204 * @method resizeSlideshow 205 */ 206 resizeSlideshow: function(){ 207 if(typeof YUI !== 'undefined') { 208 YUI().use('node-event-simulate', function(Y) { 209 Y.one(window).simulate("resize"); 210 }); 211 } 212 }, 213 214 /** 215 * Public method for reloading an embedded Google Map within the tabs or hidden element. 216 * 217 * @since 2.2 218 * @method reloadGoogleMap 219 */ 220 reloadGoogleMap: function(element){ 221 var $element = 'undefined' == typeof element ? $( 'body' ) : $( element ),
222 googleMap = $element.find( 'iframe[src*="google.com/maps"]' ); 223 224 if ( googleMap.length ) { 225 googleMap.attr( 'src', function(i, val) { 226 return val; 227 }); 228 } 229 }, 230 231 /** 232 * Unbinds builder layout events. 233 * 234 * @since 1.0 235 * @access private 236 * @method _destroy 237 */ 238 _destroy: function() 239 { 240 var win = $(window); 241 242 win.off('scroll.fl-bg-parallax'); 243 win.off('resize.fl-bg-video'); 244 }, 245 246 /** 247 * Checks to see if the current device has touch enabled. 248 * 249 * @since 1.0 250 * @access private 251 * @method _isTouch 252 * @return {Boolean} 253 */ 254 _isTouch: function() 255 { 256 if(('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch)) { 257 return true; 258 } 259 260 return false; 261 }, 262 263 /** 264 * Checks to see if the current device is mobile. 265 * 266 * @since 1.7 267 * @access private 268 * @method _isMobile 269 * @return {Boolean} 270 */ 271 _isMobile: function() 272 { 273 return /Mobile|Android|Silk\/|Kindle|BlackBerry|Opera Mini|Opera Mobi|webOS/i.test( navigator.userAgent ); 274 }, 275 276 /** 277 * Initializes builder body classes. 278 * 279 * @since 1.0 280 * @access private 281 * @method _initClasses 282 */ 283 _initClasses: function() 284 { 285 var body = $( 'body' ), 286 ua = navigator.userAgent; 287 288 // Add the builder body class. 289 if ( ! body.hasClass( 'archive' ) && $( '.fl-builder-content-primary' ).length > 0 ) { 290 body.addClass('fl-builder'); 291 } 292 293 // Add the builder touch body class. 294 if(FLBuilderLayout._isTouch()) { 295 body.addClass('fl-builder-touch'); 296 } 297 298 // Add the builder mobile body class. 299 if(FLBuilderLayout._isMobile()) { 300 body.addClass('fl-builder-mobile'); 301 } 302 303 if ( $(window).width() < FLBuilderLayoutConfig.breakpoints.small ) { 304 body.addClass( 'fl-builder-breakpoint-small' ); 305 } 306 307 if ( $(window).width() > FLBuilderLayoutConfig.breakpoints.small && $(window).width() < FLBuilderLayoutConfig.breakpoints.medium ) { 308 body.addClass( 'fl-builder-breakpoint-medium' ); 309 } 310 311 if ( $(window).width() > FLBuilderLayoutConfig.breakpoints.medium ) { 312 body.addClass( 'fl-builder-breakpoint-large' ); 313 } 314 315 // IE11 body class. 316 if ( ua.indexOf( 'Trident/7.0' ) > -1 && ua.indexOf( 'rv:11.0' ) > -1 ) { 317 body.addClass( 'fl-builder-ie-11' ); 318 } 319 }, 320 321 /** 322 * Initializes builder node backgrounds that require 323 * additional JavaScript logic such as parallax. 324 * 325 * @since 1.1.4 326 * @access private
327 * @method _initBackgrounds 328 */ 329 _initBackgrounds: function() 330 { 331 var win = $(window); 332 333 // Init parallax backgrounds. 334 if($('.fl-row-bg-parallax').length > 0 && !FLBuilderLayout._isMobile()) { 335 FLBuilderLayout._scrollParallaxBackgrounds(); 336 FLBuilderLayout._initParallaxBackgrounds(); 337 win.on('resize.fl-bg-parallax', FLBuilderLayout._initParallaxBackgrounds); 338 win.on('scroll.fl-bg-parallax', FLBuilderLayout._scrollParallaxBackgrounds); 339 } 340 341 // Init video backgrounds. 342 if($('.fl-bg-video').length > 0) { 343 FLBuilderLayout._initBgVideos(); 344 FLBuilderLayout._resizeBgVideos(); 345 win.on('resize.fl-bg-video', FLBuilderLayout._resizeBgVideos); 346 } 347 }, 348 349 /** 350 * Initializes all parallax backgrounds in a layout. 351 * 352 * @since 1.1.4 353 * @access private 354 * @method _initParallaxBackgrounds 355 */ 356 _initParallaxBackgrounds: function() 357 { 358 $('.fl-row-bg-parallax').each(FLBuilderLayout._initParallaxBackground); 359 }, 360 361 /** 362 * Initializes a single parallax background. 363 * 364 * @since 1.1.4 365 * @access private 366 * @method _initParallaxBackgrounds 367 */ 368 _initParallaxBackground: function() 369 { 370 var row = $(this), 371 content = row.find('> .fl-row-content-wrap'), 372 winWidth = $(window).width(), 373 screenSize = '', 374 imageSrc = { 375 default: '', 376 medium: '', 377 responsive: '', 378 }; 379 380 imageSrc.default = row.data('parallax-image') || ''; 381 imageSrc.medium = row.data('parallax-image-medium') || imageSrc.default; 382 imageSrc.responsive = row.data('parallax-image-responsive') || imageSrc.medium; 383 384 if (winWidth > FLBuilderLayoutConfig.breakpoints.medium) { 385 screenSize = 'default'; 386 } else if (winWidth > FLBuilderLayoutConfig.breakpoints.small && winWidth <= FLBuilderLayoutConfig.breakpoints.medium ) { 387 screenSize = 'medium'; 388 } else if (winWidth <= FLBuilderLayoutConfig.breakpoints.small) { 389 screenSize = 'responsive'; 390 } 391 392 content.css('background-image', 'url(' + imageSrc[screenSize] + ')'); 393 row.data('current-image-loaded', screenSize ); 394 395 }, 396 397 /** 398 * Fires when the window is scrolled to adjust 399 * parallax backgrounds. 400 * 401 * @since 1.1.4 402 * @access private 403 * @method _scrollParallaxBackgrounds 404 */ 405 _scrollParallaxBackgrounds: function() 406 { 407 $('.fl-row-bg-parallax').each(FLBuilderLayout._scrollParallaxBackground); 408 }, 409 410 /** 411 * Fires when the window is scrolled to adjust 412 * a single parallax background. 413 * 414 * @since 1.1.4 415 * @access private 416 * @method _scrollParallaxBackground 417 */ 418 _scrollParallaxBackground: function() 419 { 420 var win = $(window), 421 row = $(this), 422 content = row.find('> .fl-row-content-wrap'), 423 speed = row.data('parallax-speed'), 424 offset = content.offset(), 425 yPos = -((win.scrollTop() - offset.top) / speed), 426 initialOffset = ( row.data('parallax-offset') != null ) ? row.data('parallax-offset') : 0, 427 totalOffset = yPos - initialOffset; 428 429 content.css('background-position', 'center ' + totalOffset + 'px'); 430 }, 431 432 /** 433 * Initializes all video backgrounds. 434 * 435 * @since 1.6.3.3 436 * @access private 437 * @method _initBgVideos 438 */ 439 _initBgVideos: function() 440 { 441 $('.fl-bg-video').each(FLBuilderLayout._initBgVideo); 442 }, 443 444 /** 445 * Initializes a video background. 446 * 447 * @since 1.6.3.3 448 * @access private 449 * @method _initBgVideo 450 */ 451 _initBgVideo: function() 452 { 453 var wrap = $( this ), 454 width = wrap.data( 'width' ), 455 height = wrap.data( 'height' ), 456 mp4 = wrap.data( 'mp4' ), 457 youtube = wrap.data( 'youtube'), 458 vimeo = wrap.data( 'vimeo'), 459 mp4Type = wrap.data( 'mp4-type' ), 460 webm = wrap.data( 'webm' ), 461 webmType = wrap.data( 'webm-type' ), 462 fallback = wrap.data( 'fallback' ), 463 loaded = wrap.data( 'loaded' ), 464 videoMobile = wrap.data( 'video-mobile' ), 465 fallbackTag = '', 466 videoTag = null, 467 mp4Tag = null, 468 webmTag = null; 469 470 // Return if the video has been loaded for this row. 471 if ( loaded ) { 472 return; 473 } 474
475 videoTag = $( '<video autoplay loop muted playsinline></video>' ); 476 477 /** 478 * Add poster image (fallback image) 479 */ 480 if( 'undefined' != typeof fallback && '' != fallback ) { 481 videoTag.attr( 'poster', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' ) 482 videoTag.css( 'background', 'transparent url("' + fallback + '") no-repeat center center' ) 483 videoTag.css( 'background-size', 'cover' ) 484 videoTag.css( 'height', '100%' ) 485 } 486 487 // MP4 Source Tag 488 if ( 'undefined' != typeof mp4 && '' != mp4 ) { 489 490 mp4Tag = $( '<source />' ); 491 mp4Tag.attr( 'src', mp4 ); 492 mp4Tag.attr( 'type', mp4Type ); 493 494 videoTag.append( mp4Tag ); 495 } 496 // WebM Source Tag 497 if ( 'undefined' != typeof webm && '' != webm ) { 498 499 webmTag = $( '<source />' ); 500 webmTag.attr( 'src', webm ); 501 webmTag.attr( 'type', webmType ); 502 503 videoTag.append( webmTag ); 504 } 505 506 // This is either desktop, or mobile is enabled. 507 if ( ! FLBuilderLayout._isMobile() || ( FLBuilderLayout._isMobile() && "yes" == videoMobile ) ) { 508 if ( 'undefined' != typeof youtube ) { 509 FLBuilderLayout._initYoutubeBgVideo.apply( this ); 510 } 511 else if ( 'undefined' != typeof vimeo ) { 512 FLBuilderLayout._initVimeoBgVideo.apply( this ); 513 } 514 else { 515 wrap.append( videoTag ); 516 } 517 } 518 else { 519 // if we are here, it means we are on mobile and NO is set so remove video src and use fallback 520 videoTag.attr('src', '') 521 wrap.append( videoTag ); 522 } 523 524 // Mark this video as loaded. 525 wrap.data('loaded', true); 526 }, 527 528 /** 529 * Initializes Youtube video background 530 * 531 * @since 1.9 532 * @access private 533 * @method _initYoutubeBgVideo 534 */ 535 _initYoutubeBgVideo: function() 536 { 537 var playerWrap = $(this), 538 videoId = playerWrap.data('video-id'), 539 videoPlayer = playerWrap.find('.fl-bg-video-player'), 540 enableAudio = playerWrap.data('enable-audio'), 541 audioButton = playerWrap.find('.fl-bg-video-audio'), 542 startTime = 'undefined' !== typeof playerWrap.data('start') ? playerWrap.data('start') : 0, 543 startTime = 'undefined' !== typeof playerWrap.data('t') && startTime === 0 ? playerWrap.data('t') : startTime, 544 endTime = 'undefined' !== typeof playerWrap.data('end') ? playerWrap.data('end') : 0, 545 loop = 'undefined' !== typeof playerWrap.data('loop') ? playerWrap.data('loop') : 1, 546 stateCount = 0, 547 player,fallback_showing; 548 549 if ( videoId ) { 550 fallback = playerWrap.data('fallback') || false 551 if( fallback ) { 552 playerWrap.find('iframe').remove() 553 fallbackTag = $( '<div></div>' ); 554 fallbackTag.addClass( 'fl-bg-video-fallback' ); 555 fallbackTag.css( 'background-image', 'url(' + playerWrap.data('fallback') + ')' ); 556 fallbackTag.css( 'background-size', 'cover' ); 557 fallbackTag.css( 'transition', 'background-image 1s') 558 playerWrap.append( fallbackTag ); 559 fallback_showing = true; 560 } 561 FLBuilderLayout._onYoutubeApiReady( function( YT ) { 562 setTimeout( function() { 563 564 player = new YT.Player( videoPlayer[0], { 565 videoId: videoId, 566 events: { 567 onReady: function(event) { 568 if ( "no" === enableAudio || FLBuilderLayout._isMobile() ) { 569 event.target.mute(); 570 } 571 else if ( "yes" === enableAudio && event.target.isMuted ) { 572 event.target.unMute(); 573 } 574 575 // Store an instance to a parent 576 playerWrap.data('YTPlayer', player); 577 FLBuilderLayout._resizeYoutubeBgVideo.apply(playerWrap); 578 579 // Queue the video. 580 event.target.playVideo(); 581 582 if ( audioButton.length > 0 && ! FLBuilderLayout._isMobile() ) { 583 audioButton.on( 'click', {button: audioButton, player: player}, FLBuilderLayout._toggleBgVideoAudio ); 584 } 585 }, 586 onStateChange: function( event ) { 587 588 if ( event.data === 1 ) { 589 if ( fallback_showing ) { 590 $( '.fl-bg-video-fallback' ).css( 'background-image', 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)' ) 591 } 592 } 593 // Manual check if video is not playable in some browsers. 594 // StateChange order: [-1, 3, -1] 595 if ( stateCount < 4 ) { 596 stateCount++; 597 } 598 599 // Comply with the audio policy in some browsers like Chrome and Safari. 600 if ( stateCount > 1 && (-1 === event.data || 2 === event.data) && "yes" === enableAudio ) { 601 player.mute(); 602 player.playVideo(); 603 audioButton.show(); 604 } 605 606 if ( event.data === YT.PlayerState.ENDED && 1 === loop ) { 607 if ( startTime > 0 ) { 608 player.seekTo( startTime ); 609 } 610 else { 611 player.playVideo(); 612 } 613 } 614 }, 615 onError: function(event) { 616 console.info('YT Error: ' + event.data) 617 FLBuilderLayout._onErrorYoutubeVimeo(playerWrap) 618 } 619 }, 620 playerVars: { 621 playsinline: FLBuilderLayout._isMobile() ? 1 : 0, 622 controls: 0, 623 showinfo: 0, 624 rel : 0, 625 start: startTime, 626 end: endTime, 627 } 628 } ); 629 }, 1 ); 630 } ); 631 } 632 }, 633 634 /** 635 * On youtube or vimeo error show the fallback image if available. 636 * @since 2.0.7 637 */ 638 _onErrorYoutubeVimeo: function(playerWrap) { 639 640 fallback = playerWrap.data('fallback') || false 641 if( ! fallback ) { 642 return false;
643 } 644 playerWrap.find('iframe').remove() 645 fallbackTag = $( '<div></div>' ); 646 fallbackTag.addClass( 'fl-bg-video-fallback' ); 647 fallbackTag.css( 'background-image', 'url(' + playerWrap.data('fallback') + ')' ); 648 playerWrap.append( fallbackTag ); 649 }, 650 651 /** 652 * Check if Youtube API has been downloaded 653 * 654 * @since 1.9 655 * @access private 656 * @method _onYoutubeApiReady 657 * @param {Function} callback Method to call when YT API has been loaded 658 */ 659 _onYoutubeApiReady: function( callback ) { 660 if ( window.YT && YT.loaded ) { 661 callback( YT ); 662 } else { 663 // If not ready check again by timeout.. 664 setTimeout( function() { 665 FLBuilderLayout._onYoutubeApiReady( callback ); 666 }, 350 ); 667 } 668 }, 669 670 /** 671 * Initializes Vimeo video background 672 * 673 * @since 1.9 674 * @access private 675 * @method _initVimeoBgVideo 676 */ 677 _initVimeoBgVideo: function() 678 { 679 var playerWrap = $(this), 680 videoId = playerWrap.data('video-id'), 681 videoPlayer = playerWrap.find('.fl-bg-video-player'), 682 enableAudio = playerWrap.data('enable-audio'), 683 audioButton = playerWrap.find('.fl-bg-video-audio'), 684 player, 685 width = playerWrap.outerWidth(), 686 ua = navigator.userAgent; 687 688 if ( typeof Vimeo !== 'undefined' && videoId ) { 689 player = new Vimeo.Player(videoPlayer[0], { 690 id : videoId, 691 loop : true, 692 title : false, 693 portrait : false, 694 background : true, 695 autopause : false, 696 muted : true 697 }); 698 699 playerWrap.data('VMPlayer', player); 700 if ( "no" === enableAudio ) { 701 player.setVolume(0); 702 } 703 else if ("yes" === enableAudio ) { 704 // Chrome and Safari have audio policy restrictions for autoplay videos. 705 if ( ua.indexOf("Safari") > -1 || ua.indexOf("Chrome") > -1 ) { 706 player.setVolume(0); 707 audioButton.show(); 708 } 709 else { 710 player.setVolume(1); 711 } 712 } 713 714 player.play().catch(function(error) { 715 FLBuilderLayout._onErrorYoutubeVimeo(playerWrap) 716 }); 717 718 if ( audioButton.length > 0 ) { 719 audioButton.on( 'click', {button: audioButton, player: player}, FLBuilderLayout._toggleBgVideoAudio ); 720 } 721 } 722 }, 723 724 /** 725 * Mute / unmute audio on row's video background. 726 * It works for both Youtube and Vimeo. 727 * 728 * @since 2.1.3 729 * @access private 730 * @method _toggleBgVideoAudio 731 * @param {Object} e Method arguments 732 */ 733 _toggleBgVideoAudio: function( e ) { 734 var player = e.data.player, 735 control = e.data.button.find('.fl-audio-control'); 736 737 if ( control.hasClass( 'fa-volume-off' ) ) { 738 // Unmute 739 control 740 .removeClass( 'fa-volume-off' ) 741 .addClass( 'fa-volume-up' ); 742 e.data.button.find( '.fa-times' ).hide(); 743 744 if ( 'function' === typeof player.unMute ) { 745 player.unMute(); 746 } 747 else { 748 player.setVolume( 1 ); 749 } 750 } 751 else { 752 // Mute 753 control 754 .removeClass( 'fa-volume-up' ) 755 .addClass( 'fa-volume-off' ); 756 e.data.button.find( '.fa-times' ).show(); 757 758 if ( 'function' === typeof player.unMute ) { 759 player.mute(); 760 } 761 else { 762 player.setVolume( 0 ); 763 } 764 } 765 }, 766 767 /** 768 * Fires when there is an error loading a video 769 * background source and shows the fallback. 770 * 771 * @since 1.6.3.3 772 * @access private 773 * @method _videoBgSourceError 774 * @param {Object} e An event object 775 * @deprecated 2.0.3 776 */ 777 _videoBgSourceError: function( e ) 778 { 779 var source = $( e.target ), 780 wrap = source.closest( '.fl-bg-video' ), 781 vid = wrap.find( 'video' ), 782 fallback = wrap.data( 'fallback' ), 783 fallbackTag = ''; 784 source.remove(); 785 786 if ( vid.find( 'source' ).length ) { 787 // Don't show the fallback if we still have other sources to check. 788 return; 789 } else if ( '' !== fallback ) { 790 fallbackTag = $( '<div></div>' ); 791 fallbackTag.addClass( 'fl-bg-video-fallback' ); 792 fallbackTag.css( 'background-image', 'url(' + fallback + ')' ); 793 wrap.append( fallbackTag ); 794 vid.remove(); 795 } 796 }, 797 798 /** 799 * Fires when the window is resized to resize 800 * all video backgrounds. 801 * 802 * @since 1.1.4 803 * @access private 804 * @method _resizeBgVideos 805 */ 806 _resizeBgVideos: function() 807 { 808 $('.fl-bg-video').each( function() { 809 810 FLBuilderLayout._resizeBgVideo.apply( this ); 811 812 if ( $( this ).parent().find( 'img' ).length > 0 ) { 813 $( this ).parent().imagesLoaded( $.proxy( FLBuilderLayout._resizeBgVideo, this ) ); 814 } 815 } ); 816 }, 817 818 /** 819 * Fires when the window is resized to resize 820 * a single video background. 821 * 822 * @since 1.1.4 823 * @access private 824 * @method _resizeBgVideo 825 */ 826 _resizeBgVideo: function() 827 { 828 if ( 0 === $( this ).find( 'video' ).length && 0 === $( this ).find( 'iframe' ).length ) { 829 return; 830 } 831 832 var wrap = $(this), 833 wrapHeight = wrap.outerHeight(), 834 wrapWidth = wrap.outerWidth(), 835 vid = wrap.find('video'), 836 vidHeight = wrap.data('height'), 837 vidWidth = wrap.data('width'), 838 newWidth = wrapWidth, 839 newHeight = Math.round(vidHeight * wrapWidth/vidWidth), 840 newLeft = 0, 841 newTop = 0, 842 iframe = wrap.find('iframe'); 843 844 if ( vid.length ) { 845 if(vidHeight === '' || typeof vidHeight === 'undefined' || vidWidth === '' || typeof vidWidth === 'undefined') { 846 vid.css({ 847 'left' : '0px', 848 'top' : '0px', 849 'width' : newWidth + 'px' 850 }); 851 852 // Try to set the actual video dimension on 'loadedmetadata' when using URL as video source 853 vid.on('loadedmetadata', FLBuilderLayout._resizeOnLoadedMeta); 854 855 } 856 else { 857 if(newHeight < wrapHeight) { 858 newHeight = wrapHeight; 859 newLeft = -((newWidth - wrapWidth) / 2); 860 861 if ( 0 != vidHeight ) { 862 newWidth = Math.round(vidWidth * wrapHeight/vidHeight); 863 } 864 } 865 else { 866 newTop = -((newHeight - wrapHeight)/2); 867 } 868 869 vid.css({ 870 'left' : newLeft + 'px', 871 'top' : newTop + 'px', 872 'height' : newHeight + 'px', 873 'width' : newWidth + 'px' 874 }); 875 876 vid.on('loadedmetadata', FLBuilderLayout._resizeOnLoadedMeta); 877 878 } 879 } 880 else if ( iframe.length ) { 881 882 // Resize Youtube video player within iframe tag 883 if ( typeof wrap.data('youtube') !== 'undefined' ) { 884 FLBuilderLayout._resizeYoutubeBgVideo.apply(this); 885 } 886 } 887 }, 888 889 /** 890 * Fires when video meta has been loaded. 891 * This will be Triggered when width/height attributes were not specified during video background resizing. 892 * 893 * @since 1.8.5 894 * @access private 895 * @method _resizeOnLoadedMeta 896 */ 897 _resizeOnLoadedMeta: function(){ 898 var video = $(this), 899 wrapHeight = video.parent().outerHeight(), 900 wrapWidth = video.parent().outerWidth(), 901 vidWidth = video[0].videoWidth, 902 vidHeight = video[0].videoHeight, 903 newHeight = Math.round(vidHeight * wrapWidth/vidWidth), 904 newWidth = wrapWidth, 905 newLeft = 0, 906 newTop = 0; 907 908 if(newHeight < wrapHeight) { 909 newHeight = wrapHeight; 910 newWidth = Math.round(vidWidth * wrapHeight/vidHeight); 911 newLeft = -((newWidth - wrapWidth)/2); 912 } 913 else { 914 newTop = -((newHeight - wrapHeight)/2); 915 } 916 917 video.parent().data('width', vidWidth); 918 video.parent().data('height', vidHeight); 919 920 video.css({ 921 'left' : newLeft + 'px', 922 'top' : newTop + 'px', 923 'width' : newWidth + 'px', 924 'height' : newHeight + 'px' 925 }); 926 }, 927 928 /** 929 * Fires when the window is resized to resize 930 * a single Youtube video background. 931 * 932 * @since 1.9 933 * @access private 934 * @method _resizeYoutubeBgVideo 935 */ 936 _resizeYoutubeBgVideo: function() 937 { 938 var wrap = $(this), 939 wrapWidth = wrap.outerWidth(), 940 wrapHeight = wrap.outerHeight(), 941 player = wrap.data('YTPlayer'), 942 video = player ? player.getIframe() : null, 943 aspectRatioSetting = '16:9', // Medium 944 aspectRatioArray = aspectRatioSetting.split( ':' ),
945 aspectRatio = aspectRatioArray[0] / aspectRatioArray[1], 946 ratioWidth = wrapWidth / aspectRatio, 947 ratioHeight = wrapHeight * aspectRatio, 948 isWidthFixed = wrapWidth / wrapHeight > aspectRatio, 949 width = isWidthFixed ? wrapWidth : ratioHeight, 950 height = isWidthFixed ? ratioWidth : wrapHeight; 951 952 if ( video ) { 953 $(video).width( width ).height( height ); 954 } 955 }, 956 957 /** 958 * Initializes module animations. 959 * 960 * @since 1.1.9 961 * @access private 962 * @method _initModuleAnimations 963 */ 964 _initModuleAnimations: function() 965 { 966 if(typeof jQuery.fn.waypoint !== 'undefined') { 967 $('.fl-animation').each( function() { 968 var node = $( this ), 969 nodeTop = node.offset().top, 970 winHeight = $( window ).height(), 971 bodyHeight = $( 'body' ).height(), 972 waypoint = FLBuilderLayoutConfig.waypoint, 973 offset = '80%'; 974 975 if ( typeof waypoint.offset !== undefined ) { 976 offset = FLBuilderLayoutConfig.waypoint.offset + '%'; 977 } 978 979 if ( bodyHeight - nodeTop < winHeight * 0.2 ) { 980 offset = '100%'; 981 } 982 983 node.waypoint({ 984 offset: offset, 985 handler: FLBuilderLayout._doModuleAnimation 986 }); 987 } ); 988 } 989 }, 990 991 /** 992 * Runs a module animation. 993 * 994 * @since 1.1.9 995 * @access private 996 * @method _doModuleAnimation 997 */ 998 _doModuleAnimation: function() 999 { 1000 var module = 'undefined' == typeof this.element ? $(this) : $(this.element), 1001 delay = parseFloat(module.data('animation-delay')), 1002 duration = parseFloat(module.data('animation-duration')); 1003 1004 if ( ! isNaN( duration ) ) { 1005 module.css( 'animation-duration', duration + 's' ); 1006 } 1007 1008 if(!isNaN(delay) && delay > 0) { 1009 setTimeout(function(){ 1010 module.addClass('fl-animated'); 1011 }, delay * 1000); 1012 } else { 1013 setTimeout(function(){ 1014 module.addClass('fl-animated'); 1015 }, 1); 1016 } 1017 }, 1018 1019 /** 1020 * Opens a tab or accordion item if the browser hash is set 1021 * to the ID of one on the page. 1022 * 1023 * @since 1.6.0 1024 * @access private 1025 * @method _initHash 1026 */ 1027 _initHash: function() 1028 { 1029 var hash = window.location.hash.replace( '#', '' ).split( '/' ).shift(), 1030 element = null, 1031 tabs = null, 1032 responsiveLabel = null, 1033 tabIndex = null, 1034 label = null; 1035 1036 if ( '' !== hash ) { 1037 1038 try { 1039 1040 element = $( '#' + hash ); 1041 1042 if ( element.length > 0 ) { 1043 1044 if ( element.hasClass( 'fl-accordion-item' ) ) { 1045 setTimeout( function() { 1046 element.find( '.fl-accordion-button' ).trigger( 'click' ); 1047 }, 100 ); 1048 } 1049 if ( element.hasClass( 'fl-tabs-panel' ) ) { 1050 1051 setTimeout( function() { 1052 1053 tabs = element.closest( '.fl-tabs' ); 1054 responsiveLabel = element.find( '.fl-tabs-panel-label' ); 1055 tabIndex = responsiveLabel.data( 'index' ); 1056 label = tabs.find( '.fl-tabs-labels .fl-tabs-label[data-index=' + tabIndex + ']' ); 1057 1058 if ( responsiveLabel.is( ':visible' ) ) { 1059 responsiveLabel.trigger( 'click' ); 1060 } 1061 else { 1062 label[0].click(); 1063 FLBuilderLayout._scrollToElement( element ); 1064 } 1065 1066 }, 100 ); 1067 } 1068 } 1069 } 1070 catch( e ) {} 1071 } 1072 }, 1073 1074 /** 1075 * Initializes all anchor links on the page for smooth scrolling. 1076 * 1077 * @since 1.4.9 1078 * @access private 1079 * @method _initAnchorLinks 1080 */ 1081 _initAnchorLinks: function() 1082 { 1083 $( 'a' ).each( FLBuilderLayout._initAnchorLink ); 1084 }, 1085 1086 /** 1087 * Initializes a single anchor link for smooth scrolling. 1088 * 1089 * @since 1.4.9 1090 * @access private 1091 * @method _initAnchorLink 1092 */ 1093 _initAnchorLink: function() 1094 { 1095 var link = $( this ), 1096 href = link.attr( 'href' ), 1097 loc = window.location, 1098 id = null, 1099 element = null, 1100 flNode = false; 1101 1102 if ( 'undefined' != typeof href && href.indexOf( '#' ) > -1 && link.closest('svg').length < 1 ) { 1103 1104 if ( loc.pathname.replace( /^\//, '' ) == this.pathname.replace( /^\//, '' ) && loc.hostname == this.hostname ) { 1105 1106 try { 1107 1108 id = href.split( '#' ).pop(); 1109 // If there is no ID then we have nowhere to look 1110 // Fixes a quirk in jQuery and FireFox 1111 if( ! id ) { 1112 return; 1113 } 1114 element = $( '#' + id ); 1115 1116 if ( element.length > 0 ) { 1117 flNode = element.hasClass( 'fl-row' ) || element.hasClass( 'fl-col' ) || element.hasClass( 'fl-module' ); 1118 if ( !element.hasClass( 'fl-no-scroll' ) && ( link.hasClass( 'fl-scroll-link' ) || flNode ) ) { 1119 $( link ).on( 'click', FLBuilderLayout._scrollToElementOnLinkClick ); 1120 } 1121 if ( element.hasClass( 'fl-accordion-item' ) ) { 1122 $( link ).on( 'click', FLBuilderLayout._scrollToAccordionOnLinkClick ); 1123 } 1124 if ( element.hasClass( 'fl-tabs-panel' ) ) { 1125 $( link ).on( 'click', FLBuilderLayout._scrollToTabOnLinkClick ); 1126 } 1127 } 1128 } 1129 catch( e ) {} 1130 } 1131 } 1132 }, 1133 1134 /** 1135 * Scrolls to an element when an anchor link is clicked. 1136 * 1137 * @since 1.4.9 1138 * @access private 1139 * @method _scrollToElementOnLinkClick 1140 * @param {Object} e An event object. 1141 * @param {Function} callback A function to call when the scroll is complete. 1142 */ 1143 _scrollToElementOnLinkClick: function( e, callback ) 1144 { 1145 var element = $( '#' + $( this ).attr( 'href' ).split( '#' ).pop() ); 1146 1147 FLBuilderLayout._scrollToElement( element, callback ); 1148 1149 e.preventDefault(); 1150 }, 1151 1152 /** 1153 * Scrolls to an element. 1154 * 1155 * @since 1.6.4.5 1156 * @access private 1157 * @method _scrollToElement 1158 * @param {Object} element The element to scroll to. 1159 * @param {Function} callback A function to call when the scroll is complete. 1160 */ 1161 _scrollToElement: function( element, callback ) 1162 { 1163 var config = FLBuilderLayoutConfig.anchorLinkAnimations, 1164 dest = 0, 1165 win = $( window ), 1166 doc = $( document ); 1167 1168 if ( element.length > 0 ) { 1169 1170 if ( 'fixed' === element.css('position') || 'fixed' === element.parent().css('position') ) { 1171 dest = element.position().top; 1172 } 1173 else if ( element.offset().top > doc.height() - win.height() ) { 1174 dest = doc.height() - win.height(); 1175 } 1176 else { 1177 dest = element.offset().top - config.offset; 1178 } 1179 1180 $( 'html, body' ).animate( { scrollTop: dest }, config.duration, config.easing, function() { 1181 1182 if ( 'undefined' != typeof callback ) { 1183 callback(); 1184 } 1185 1186 if ( undefined != element.attr( 'id' ) ) { 1187 1188 if ( history.pushState ) { 1189 history.pushState( null, null, '#' + element.attr( 'id' ) ); 1190 } 1191 else { 1192 window.location.hash = element.attr( 'id' ); 1193 } 1194 } 1195 } ); 1196 } 1197 }, 1198 1199 /** 1200 * Scrolls to an accordion item when a link is clicked. 1201 * 1202 * @since 1.5.9 1203 * @access private 1204 * @method _scrollToAccordionOnLinkClick 1205 * @param {Object} e An event object. 1206 */ 1207 _scrollToAccordionOnLinkClick: function( e ) 1208 { 1209 var element = $( '#' + $( this ).attr( 'href' ).split( '#' ).pop() ); 1210 1211 if ( element.length > 0 ) { 1212 1213 var callback = function() { 1214 if ( element ) { 1215 element.find( '.fl-accordion-button' ).trigger( 'click' ); 1216 element = false;
1217 } 1218 }; 1219 1220 FLBuilderLayout._scrollToElementOnLinkClick.call( this, e, callback ); 1221 } 1222 }, 1223 1224 /** 1225 * Scrolls to a tab panel when a link is clicked. 1226 * 1227 * @since 1.5.9 1228 * @access private 1229 * @method _scrollToTabOnLinkClick 1230 * @param {Object} e An event object. 1231 */ 1232 _scrollToTabOnLinkClick: function( e ) 1233 { 1234 var element = $( '#' + $( this ).attr( 'href' ).split( '#' ).pop() ), 1235 tabs = null, 1236 label = null, 1237 responsiveLabel = null; 1238 1239 if ( element.length > 0 ) { 1240 1241 tabs = element.closest( '.fl-tabs' ); 1242 responsiveLabel = element.find( '.fl-tabs-panel-label' ); 1243 tabIndex = responsiveLabel.data( 'index' ); 1244 label = tabs.find( '.fl-tabs-labels .fl-tabs-label[data-index=' + tabIndex + ']' ); 1245 1246 if ( responsiveLabel.is( ':visible' ) ) { 1247 1248 var callback = function() { 1249 if ( element ) { 1250 responsiveLabel.trigger( 'click' ); 1251 element = false; 1252 } 1253 }; 1254 1255 FLBuilderLayout._scrollToElementOnLinkClick.call( this, e, callback ); 1256 } 1257 else { 1258 label[0].click(); 1259 FLBuilderLayout._scrollToElement( element ); 1260 } 1261 1262 e.preventDefault(); 1263 } 1264 }, 1265 1266 /** 1267 * Initializes all builder forms on a page. 1268 * 1269 * @since 1.5.4 1270 * @access private 1271 * @method _initForms 1272 */ 1273 _initForms: function() 1274 { 1275 if ( ! FLBuilderLayout._hasPlaceholderSupport ) { 1276 $( '.fl-form-field input' ).each( FLBuilderLayout._initFormFieldPlaceholderFallback ); 1277 } 1278 1279 $( '.fl-form-field input' ).on( 'focus', FLBuilderLayout._clearFormFieldError ); 1280 }, 1281 1282 /** 1283 * Checks to see if the current device has HTML5 1284 * placeholder support. 1285 * 1286 * @since 1.5.4 1287 * @access private 1288 * @method _hasPlaceholderSupport 1289 * @return {Boolean} 1290 */ 1291 _hasPlaceholderSupport: function() 1292 { 1293 var input = document.createElement( 'input' ); 1294 1295 return 'undefined' != input.placeholder; 1296 }, 1297 1298 /** 1299 * Initializes the fallback for when placeholders aren't supported. 1300 * 1301 * @since 1.5.4 1302 * @access private 1303 * @method _initFormFieldPlaceholderFallback 1304 */ 1305 _initFormFieldPlaceholderFallback: function() 1306 { 1307 var field = $( this ), 1308 val = field.val(), 1309 placeholder = field.attr( 'placeholder' ); 1310 1311 if ( 'undefined' != placeholder && '' === val ) { 1312 field.val( placeholder ); 1313 field.on( 'focus', FLBuilderLayout._hideFormFieldPlaceholderFallback ); 1314 field.on( 'blur', FLBuilderLayout._showFormFieldPlaceholderFallback ); 1315 } 1316 }, 1317 1318 /** 1319 * Hides a fallback placeholder on focus. 1320 * 1321 * @since 1.5.4 1322 * @access private 1323 * @method _hideFormFieldPlaceholderFallback 1324 */ 1325 _hideFormFieldPlaceholderFallback: function() 1326 { 1327 var field = $( this ), 1328 val = field.val(), 1329 placeholder = field.attr( 'placeholder' ); 1330 1331 if ( val == placeholder ) { 1332 field.val( '' ); 1333 } 1334 }, 1335 1336 /** 1337 * Shows a fallback placeholder on blur. 1338 * 1339 * @since 1.5.4 1340 * @access private 1341 * @method _showFormFieldPlaceholderFallback 1342 */ 1343 _showFormFieldPlaceholderFallback: function() 1344 { 1345 var field = $( this ), 1346 val = field.val(), 1347 placeholder = field.attr( 'placeholder' ); 1348 1349 if ( '' === val ) { 1350 field.val( placeholder ); 1351 } 1352 }, 1353 1354 /** 1355 * Clears a form field error message. 1356 * 1357 * @since 1.5.4 1358 * @access private 1359 * @method _clearFormFieldError 1360 */ 1361 _clearFormFieldError: function() 1362 { 1363 var field = $( this ); 1364 1365 field.removeClass( 'fl-form-error' ); 1366 field.siblings( '.fl-form-error-message' ).hide(); 1367 } 1368 }; 1369 1370 /* Initializes the builder layout. */ 1371 $(function(){ 1372 FLBuilderLayout.init(); 1373 }); 1374 1375})(jQuery); 1376 1377/* Start Global JS */ 1378 1379/* End Global JS */ 1380 1381 1382/* Start Global Node Custom JS */ 1383 1384/* End Global Node Custom JS */ 1385 1386 1387/* Start Layout Custom JS */ 1388 1389/* End Layout Custom JS */ 1390
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.