PageSourceSearch

https://mihow.org/wp-content/uploads/sites/414/bb-plugin/cache/324…?ver=d1c0c6e827a3132f2ddbec5a9ab0faa0

js mihow.org collected 2026-10-04 13:34:10 UTC 36,436 bytes, 1,390 lines download raw bytes

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.