PageSourceSearch

https://www.obdlink.com/wp-content/uploads/bb-plugin/cache/24e5649…4808fbd-layout-bundle.js?ver=2.11.1.1-1.6.1

js obdlink.com collected 2026-10-02 03:52:21 UTC 105,365 bytes, 3,583 lines download raw bytes

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

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.