PageSourceSearch

https://www.obdlink.com/wp-content/uploads/bb-plugin/cache/14813-l…?ver=fbca3f07a8bb52a794cad87616d918f4

js obdlink.com collected 2026-09-24 18:12:47 UTC 49,345 bytes, 1,801 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};
20(function($){
21
22	if(typeof FLBuilderLayout != 'undefined') {
23		return;
24	}
25
26	/**
27	 * Helper class with generic logic for a builder layout.
28	 *
29	 * @class FLBuilderLayout
30	 * @since 1.0
31	 */
32	FLBuilderLayout = {
33
34		/**
35		 * Initializes a builder layout.
36		 *
37		 * @since 1.0
38		 * @method init
39		 */
40		init: function()
41		{
42			// Destroy existing layout events.
43			FLBuilderLayout._destroy();
44
45			// Init CSS classes.
46			FLBuilderLayout._initClasses();
47
48			// Init backgrounds.
49			FLBuilderLayout._initBackgroun
49ds();
50
51			// Init buttons.
52			FLBuilderLayout._initButtons();
53
54			// Init row shape layer height.
55			FLBuilderLayout._initRowShapeLayerHeight();
56
57			// Only init if the builder isn't active.
58			if ( 0 === $('.fl-builder-edit').length ) {
59
60				// Init anchor links.
61				FLBuilderLayout._initAnchorLinks();
62
63				// Init the browser hash.
64				FLBuilderLayout._initHash();
65
66				// Init forms.
67				FLBuilderLayout._initForms();
68
69				FLBuilderLayout._reorderMenu();
70			}
71			else {
72				FLBuilderLayout._initNestedColsWidth();
73			}
74			$('body').removeClass( 'fl-no-js' );
75		},
76
77		/**
78		 * Public method for refreshing Wookmark or MosaicFlow galleries
79		 * within an element.
80		 *
81		 * @since 1.7.4
82		 * @method refreshGalleries
83		 */
84		refreshGalleries: function( element )
85		{
86			var $element  = 'undefined' == typeof element ? $( 'body' ) : $( element ),
87				mfContent = $element.find( '.fl-mosaicflow-content' ),
88				wmContent = $element.find( '.fl-gallery' ),
89				mfObject  = null;
90
91			if ( mfContent ) {
92
93				mfObject = mfContent.data( 'mosaicflow' );
94
95				if ( mfObject ) {
96					mfObject.columns = $( [] );
97					mfObject.columnsHeights = [];
98					mfContent.data( 'mosaicflow', mfObject );
99					mfContent.mosaicflow( 'refill' );
100				}
101			}
102			if ( wmContent ) {
103				wmContent.trigger( 'refreshWookmark' );
104			}
105		},
106
107		/**
108		 * Public method for refreshing Masonry within an element
109		 *
110		 * @since 1.8.1
111		 * @method refreshGridLayout
112		 */
113		refreshGridLayout: function( element )
114		{
115			var $element 		= 'undefined' == typeof element ? $( 'body' ) : $( element ),
116				msnryContent	= $element.find('.masonry');
117
118			if ( msnryContent.length )	{
119				msnryContent.masonry('layout');
120			}
121		},
122
123		/**
124		 * Public method for reloading BxSlider within an element
125		 *
126		 * @since 1.8.1
127		 * @method reloadSlider
128		 */
129		reloadSlider: function( content )
130		{
131			var $content = 'undefined' == typeof content ? $('body') : $(content);
132
133			// reload sliders.
134			if ($content.find('.bx-viewport > div').length > 0) {
135				$.each($content.find('.bx-viewport > div'), function (key, slider) {
136					setTimeout(function () {
137						$(slider).data('bxSlider').reloadSlider();
138					}, 100);
139				});
140			}
141		},
142
143		/**
144		 * Public method for resizing WP audio player
145		 *
146		 * @since 1.8.2
147		 * @method resizeAudio
148		 */
149		resizeAudio: function( element )
150		{
151			var $element 	 	= 'undefined' == typeof element ? $( 'body' ) : $( element ),
152				audioPlayers 	= $element.find('.wp-audio-shortcode.mejs-audio'),
153				player 		 	= null,
154				mejsPlayer 	 	= null,
155				rail 			= null,
156				railWidth 		= 400;
157
158			if ( audioPlayers.length && typeof mejs !== 'undefined' ) {
159            	audioPlayers.each(function(){
160	            	player 		= $(this);
161	            	mejsPlayer 	= mejs.players[player.attr('id')];
162	            	rail 		= player.find('.mejs-controls .mejs-time-rail');
163	            	var innerMejs = player.find('.mejs-inner'),
164	            		total 	  = player.find('.mejs-controls .mejs-time-total');
165
166	            	if ( typeof mejsPlayer !== 'undefined' ) {
167	            		railWidth = Math.ceil(player.width() * 0.8);
168
169	            		if ( innerMejs.length ) {
170
171		            		rail.css('width', railWidth +'px!important');
172		            		//total.width(rail.width() - 10);
173
174		            		mejsPlayer.options.autosizeProgress = true;
175
176		            		// webkit has trouble doing this without a delay
177							setTimeout(function () {
178								mejsPlayer.setControlsSize();
179							}, 50);
180
181			            	player.find('.mejs-inner').css({
182			            		visibility: 'visible',
183			            		height: 'inherit'
184			            	});
185		            	}
186		           	}
187	            });
188	        }
189		},
190
191		/**
192		 * Public method for preloading WP audio player when it's inside the hidden element
193		 *
194		 * @since 1.8.2
195		 * @method preloadAudio
196		 */
197		preloadAudio: function(element)
198		{
199			var $element 	 = 'undefined' == typeof element ? $( 'body' ) : $( element ),
200				contentWrap  = $element.closest('.fl-accordion-item'),
201				audioPlayers = $element.find('.wp-audio-shortcode.mejs-audio');
202
203			if ( ! contentWrap.hasClass('fl-accordion-item-active') && audioPlayers.find('.mejs-inner').length ) {
204				audioPlayers.find('.mejs-inner').css({
205					visibility : 'hidden',
206					height: 0
207				});
208			}
209		},
210
211		/**
212		 * Public method for resizing slideshow momdule within the tab
213		 *
214		 * @since 1.10.5
215		 * @method resizeSlideshow
216		 */
217		resizeSlideshow: function(){
218			if(typeof YUI !== 'undefined') {
219				YUI().use('node-event-simulate', function(Y) {
220					Y.one(window).simulate("resize");
221				});
222			}
223		},
224
225		/**
226		 * Public method for reloading an embedded Google Map within the tabs or hidden element.
227		 *
228		 * @since 2.2
229		 * @method reloadGoogleMap
230		 */
231		reloadGoogleMap: function(element){
232			var $element  = 'undefined' == typeof element ? $( 'body' ) : $( element ),
233			    googleMap = $element.find( 'iframe[src*="google.com/maps"]' );
234
235			if ( googleMap.length ) {
236			    googleMap.attr( 'src', function(i, val) {
237			        return val;
238			    });
239			}
240		},
241
242		/**
243		 * Unbinds builder layout events.
244		 *
245		 * @since 1.0
246		 * @access private
247		 * @method _destroy
248		 */
249		_destroy: function()
250		{
251			var win = $(window);
252
253			win.off('scroll.fl-bg-parallax');
254			win.off('resize.fl-bg-video');
255		},
256
257		/**
258		 * Checks to see if the current device has touch enabled.
259		 *
260		 * @since 1.0
261		 * @access private
262		 * @method _isTouch
263		 * @return {Boolean}
264		 */
265		_isTouch: function()
266		{
267			if(('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch)) {
268				return true;
269			}
270
271			return false;
272		},
273
274		/**
275		 * Checks to see if the current device is mobile.
276		 *
277		 * @since 1.7
278		 * @access private
279		 * @method _isMobile
280		 * @return {Boolean}
281		 */
282		_isMobile: function()
283		{
284			return /Mobile|Android|Silk\/|Kindle|BlackBerry|Opera Mini|Opera Mobi|webOS/i.test( navigator.userAgent );
285		},
286
287		/**
288		 * Initializes builder body classes.
289		 *
290		 * @since 1.0
291		 * @access private
292		 * @method _initClasses
293		 */
294		_initClasses: function()
295		{
296			var body = $( 'body' ),
297				ua   = navigator.userAgent;
298
299			// Add the builder body class (skip on block-only frontend to avoid layout break).
300			if ( ! body.hasClass( 'fl-builder-blocks-only' ) && ! body.hasClass( 'archive' ) && $( '.fl-builder-content-primary' ).length > 0 ) {
301				body.addClass('fl-builder');
302			}
303
304			// Add the builder touch body class.
305			if(FLBuilderLayout._isTouch()) {
306				body.addClass('fl-builder-touch');
307			}
308
309			// Add the builder mobile body class.
310			if(FLBuilderLayout._isMobile()) {
311				body.addClass('fl-builder-mobile');
312			}
313
314			if ( $(window).width() < FLBuilderLayoutConfig.breakpoints.small ) {
315				body.addClass( 'fl-builder-breakpoint-small' );
316			}
317
318			if ( $(window).width() > FLBuilderLayoutConfig.breakpoints.small && $(window).width() < FLBuilderLayoutConfig.breakpoints.medium ) {
319				body.addClass( 'fl-builder-breakpoint-medium' );
320			}
321
322			if ( $(window).width() > FLBuilderLayoutConfig.breakpoints.medium && $(window).width() < FLBuilderLayoutConfig.breakpoints.large ) {
323				body.addClass( 'fl-builder-breakpoint-large' );
324			}
325
326			if ( $(window).width() > FLBuilderLayoutConfig.breakpoints.large ) {
327				body.addClass( 'fl-builder-breakpoint-default' );
328			}
329
330			// IE11 body class.
331			if ( ua.indexOf( 'Trident/7.0' ) > -1 && ua.indexOf( 'rv:11.0' ) > -1 ) {
332				body.addClass( 'fl-builder-ie-11' );
333			}
334		},
335
336		/**
337		 * Initializes builder node backgrounds that require
338		 * additional JavaScript logic such as parallax.
339		 *
340		 * @since 1.1.4
341		 * @access private
342		 * @method _initBackgrounds
343		 */
344		_initBackgrounds: function()
345		{
346			var win = $(window);
347
348			// Init parallax backgrounds.
349			if($('.fl-row-bg-parallax').length > 0 && !FLBuilderLayout._isMobile()) {
350				FLBuilderLayout._scrollParallaxBackgrounds();
351				FLBuilderLayout._initParallaxBackgrounds();
352				win.on('resize.fl-bg-parallax', FLBuilderLayout._initParallaxBackgrounds);
353				win.on('scroll.fl-bg-parallax', FLBuilderLayout._scrollParallaxBackgrounds);
354			}
355
356			// Init video backgrounds.
357			if($('.fl-bg-video').length > 0) {
358				FLBuilderLayout._initBgVideos();
359				FLBuilderLayout._resizeBgVideos();
360
361				// Ensure FLBuilderLayout._resizeBgVideos() is only called once on window resize.
362				var resizeBGTimer = null;
363				win.on('resize.fl-bg-video', function(e){
364					clearTimeout( resizeBGTimer );
365					resizeBGTimer = setTimeout(function() {
366						FLBuilderLayout._resizeBgVideos(e);
367					}, 100 );
368				});
369			}
370		},
371
372		/**
373		 * Initializes all buttons in a layout.
374		 *
375		 * @since 2.10
376		 * @access private
377		 * @method _initButtons
378		 */
379		_initButtons: function()
380		{
381			// Trigger click event on Enter or Space key press for deprecated button markup.
382			$('a.fl-button[role="button"]').on('keydown', function(event) {
383				if (event.key === 'Enter' || event.key === ' ') {
384					// Necessary to prevent the default behavior of the space key from scrolling the page
385					event.preventDefault();
386					$(this).trigger('click');
387				}
388			});
389		},
390
391		/**
392		 * Initializes all parallax backgrounds in a layout.
393		 *
394		 * @since 1.1.4
395		 * @access private
396		 * @method _initParallaxBackgrounds
397		 */
398		_initParallaxBackgrounds: function()
399		{
400			$('.fl-row-bg-parallax').each(FLBuilderLayout._initParallaxBackground);
401		},
402
403		/**
404		 * Initializes a single parallax background.
405		 *
406		 * @since 1.1.4
407		 * @access private
408		 * @method _initParallaxBackgrounds
409		 */
410		_initParallaxBackground: function()
411		{
412			var row     = $(this),
413				content = row.find('> .fl-row-content-wrap'),
414				winWidth = $(window).width(),
415				screenSize = '',
416				imageSrc = {
417					default: '',
418					medium: '',
419					responsive: '',
420				};
421
422			imageSrc.default = row.data('parallax-image') || '';
423			imageSrc.medium = row.data('parallax-image-medium') || imageSrc.default;
424			imageSrc.responsive = row.data('parallax-image-responsive') || imageSrc.medium;
425
426			if (winWidth > FLBuilderLayoutConfig.breakpoints.medium) {
427				screenSize = 'default';
428			} else if (winWidth > FLBuilderLayoutConfig.breakpoints.small && winWidth <= FLBuilderLayoutConfig.breakpoints.medium ) {
429				screenSize = 'medium';
430			} else if (winWidth <= FLBuilderLayoutConfig.breakpoints.small) {
431				screenSize = 'responsive';
432			}
433
434			content.css('background-image', 'url(' + imageSrc[screenSize] + ')');
435			row.data('current-image-loaded', screenSize );
436
437		},
438
439		/**
440		 * Fires when the window is scrolled to adjust
441		 * parallax backgrounds.
442		 *
443		 * @since 1.1.4
444		 * @access private
445		 * @method _scrollParallaxBackgrounds
446		 */
447		_scrollParallaxBackgrounds: function()
448		{
449			$('.fl-row-bg-parallax').each(FLBuilderLayout._scrollParallaxBackground);
450		},
451
452		/**
453		 * Fires when the window is scrolled to adjust
454		 * a single parallax background.
455		 *
456		 * @since 1.1.4
457		 * @access private
458		 * @method _scrollParallaxBackground
459		 */
460		_scrollParallaxBackground: function()
461		{
462			var win     	  = $(window),
463				row     	  = $(this),
464				content 	  = row.find('> .fl-row-content-wrap'),
465				speed   	  = row.data('parallax-speed'),
466				offset  	  = content.offset(),
467				yPos		  = -((win.scrollTop() - offset.top) / speed),
468				initialOffset = ( row.data('parallax-offset') != null ) ? row.data('parallax-offset') : 0,
469				totalOffset   = yPos - initialOffset;
470
471			content.css('background-position', 'center ' + totalOffset + 'px');
472		},
473
474		/**
475		 * Initializes all video backgrounds.
476		 *
477		 * @since 1.6.3.3
478		 * @access private
479		 * @method _initBgVideos
480		 */
481		_initBgVideos: function()
482		{
483			$('.fl-bg-video').each(FLBuilderLayout._initBgVideo);
484		},
485
486		/**
487		 * Initializes a video background.
488		 *
489		 * @since 1.6.3.3
490		 * @access private
491		 * @method _initBgVideo
492		 */
493		_initBgVideo: function()
494		{
495			var wrap   = $( this ),
496				width       = wrap.data( 'width' ),
497				height      = wrap.data( 'height' ),
498				mp4         = wrap.data( 'mp4' ),
499				youtube     = wrap.data( 'youtube'),
500				vimeo       = wrap.data( 'vimeo'),
501				mp4Type     = wrap.data( 'mp4-type' ),
502				webm        = wrap.data( 'webm' ),
503				webmType    = wrap.data( 'webm-type' ),
504				fallback    = wrap.data( 'fallback' ),
505				loaded      = wrap.data( 'loaded' ),
506				videoMobile = wrap.data( 'video-mobile' ),
507				playPauseButton = wrap.find('.fl-bg-video-play-pause-control'),
508				fallbackTag = '',
509				videoTag    = null,
510				mp4Tag      = null,
511				webmTag     = null;
512
513			// Return if the video has been loaded for this row.
514			if ( loaded ) {
515				return;
516			}
517
518			videoTag  = $( '<video autoplay loop muted playsinline></video>' );
519
520			/**
521			 * Add poster image (fallback image)
522			 */
523			if( 'undefined' != typeof fallback && '' != fallback ) {
524				videoTag.attr( 'poster', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' )
525				videoTag.css({
526					backgroundImage: 'url("' + fallback + '")',
527					backgroundColor: 'transparent',
528					backgroundRepeat: 'no-repeat',
529					backgroundSize: 'cover',
530					backgroundPosition: 'center center',
531				})
532			}
533
534			// MP4 Source Tag
535			if ( 'undefined' != typeof mp4 && '' != mp4 ) {
536
537				mp4Tag = $( '<source />' );
538				mp4Tag.attr( 'src', mp4 );
539				mp4Tag.attr( 'type', mp4Type );
540
541				videoTag.append( mp4Tag );
542			}
543			// WebM Source Tag
544			if ( 'undefined' != typeof webm && '' != webm ) {
545
546				webmTag = $( '<source />' );
547				webmTag.attr( 'src', webm );
548				webmTag.attr( 'type', webmType );
549
550				videoTag.append( webmTag );
551			}
552
553			// This is either desktop, or mobile is enabled.
554			if ( ! FLBuilderLayout._isMobile() || ( FLBuilderLayout._isMobile() && "yes" == videoMobile ) ) {
555				if ( 'undefined' != typeof youtube ) {
556					FLBuilderLayout._initYoutubeBgVideo.apply( this );
557				}
558				else if ( 'undefined' != typeof vimeo ) {
559					FLBuilderLayout._initVimeoBgVideo.apply( this );
560				}
561				else {
562					wrap.append( videoTag );
563
564					if ( playPauseButton.length > 0 ) {
565						var video = videoTag[0];
566
567						playPauseButton.on( 'click', { video: video }, function(e) {
568							var video = e.data.video;
569
570							if ( video.paused ) {
571								video.play();
572							} else {
573								video.pause();
574							}
575						} );
576
577						$( video ).on( 'play playing', function () {
578							playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' );
579						} );
580						
581						$( video ).on( 'pause ended waiting', function () {
582							playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' );
583						} );
584					}
585				}
586			}
587			else {
588				// if we are here, it means we are on mobile and NO is set so remove video src and use fallback
589				videoTag.attr('src', '')
590				wrap.append( videoTag );
591			}
592
593			// Mark this video as loaded.
594			wrap.data('loaded', true);
595		},
596
597		/**
598		 * Initializes Youtube video background
599		 *
600		 * @since 1.9
601		 * @access private
602		 * @method _initYoutubeBgVideo
603		 */
604		_initYoutubeBgVideo: function()
605		{
606			var playerWrap  = $(this),
607				videoId     = playerWrap.data('video-id'),
608				videoPlayer = playerWrap.find('.fl-bg-video-player'),
609				enableAudio = playerWrap.data('enable-audio'),
610				audioButton = playerWrap.find('.fl-bg-video-audio'),
611				playPauseButton = playerWrap.find('.fl-bg-video-play-pause-control'),
612				startTime   = 'undefined' !== typeof playerWrap.data('start') ? playerWrap.data('start') : 0,
613				startTime   = 'undefined' !== typeof playerWrap.data('t') && startTime === 0 ? playerWrap.data('t') : startTime,
614				endTime     = 'undefined' !== typeof playerWrap.data('end') ? playerWrap.data('end') : 0,
615				loop        = 'undefined' !== typeof playerWrap.data('loop') ? playerWrap.data('loop') : 1,
616				stateCount  = 0,
617				player,fallback_showing;
618
619			if ( videoId ) {
620				fallback = playerWrap.data('fallback') || false
621				if( fallback ) {
622					playerWrap.find('iframe').remove()
623					fallbackTag = $( '<div></div>' );
624					fallbackTag.addClass( 'fl-bg-video-fallback' );
625					fallbackTag.css( 'background-image', 'url(' + playerWrap.data('fallback') + ')' );
626					fallbackTag.css( 'background-size', 'cover' );
627					fallbackTag.css( 'transition', 'background-image 1s')
628					playerWrap.append( fallbackTag );
629					fallback_showing = true;
630				}
631				FLBuilderLayout._onYoutubeApiReady( function( YT ) {
632					setTimeout( function() {
633
634						player = new YT.Player( videoPlayer[0], {
635							videoId: videoId,
636							events: {
637								onReady: function(event) {
638									if ( "no" === enableAudio || FLBuilderLayout._isMobile() ) {
639										event.target.mute();
640									}
641									else if ( "yes" === enableAudio && event.target.isMuted ) {
642										event.target.unMute();
643									}
644
645									// Store an instance to a parent
646									playerWrap.data('YTPlayer', player);
647									FLBuilderLayout._resizeYoutubeBgVideo.apply(playerWrap);
648
649									// Queue the video.
650									event.target.playVideo();
651
652									if ( audioButton.length > 0 && ! FLBuilderLayout._isMobile() ) {
653										audioButton.on( 'click', {button: audioButton, player: player}, FLBuilderLayout._toggleBgVideoAudio );
654									}
655
656									if ( playPauseButton.length > 0 ) {
657										playPauseButton.on( 'click', {player: player}, function(e) {
658											var player = e.data.player;
659			
660											if ( 1 === player.getPlayerState() ) {
661												player.pauseVideo();
662											} else {
663												player.playVideo();
664											}
665										});
666									}
667								},
668								onStateChange: function( event ) {
669
670									if ( event.data === 1 ) {
671										if ( fallback_showing ) {
672											$( '.fl-bg-video-fallback' ).css( 'background-image', 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)' )
673										}
674									}
675									// Manual check if video is not playable in some browsers.
676									// StateChange order: [-1, 3, -1]
677									if ( stateCount < 4 ) {
678										stateCount++;
679									}
680
681									// Comply with the audio policy in some browsers like Chrome and Safari.
682									if ( stateCount > 1 && -1 === event.data && "yes" === enableAudio ) {
683										player.mute();
684										player.playVideo();
685										audioButton.show();
686									}
687
688									if ( event.data === YT.PlayerState.ENDED && 1 === loop ) {
689										if ( startTime > 0 ) {
690											player.seekTo( startTime );
691										}
692										else {
693											player.playVideo();
694										}
695									}
696
697									if ( event.data === YT.PlayerState.PLAYING ) {
698										playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' );
699									} else if ( event.data === YT.PlayerState.PAUSED ) {
700										playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' );
701									} else if ( event.data === YT.PlayerState.BUFFERING ) {
702										playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' );
703									} else if ( event.data === YT.PlayerState.CUED ) {
704										playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' );
705									} else if ( event.data === YT.PlayerState.ENDED ) {
706										playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' );
707									}
708								},
709								onError: function(event) {
710									console.info('YT Error: ' + event.data)
711									FLBuilderLayout._onErrorYoutubeVimeo(playerWrap)
712								}
713							},
714							playerVars: {
715								playsinline: FLBuilderLayout._isMobile() ? 1 : 0,
716								controls: 0,
717								showinfo: 0,
718								rel : 0,
719								start: startTime,
720								end: endTime,
721							}
722						} );
723					}, 1 );
724				} );
725			}
726		},
727
728		/**
729		 * On youtube or vimeo error show the fallback image if available.
730		 * @since 2.0.7
731		 */
732		_onErrorYoutubeVimeo: function(playerWrap) {
733
734			fallback = playerWrap.data('fallback') || false
735			if( ! fallback ) {
736				return false;
737			}
738			playerWrap.find('iframe').remove()
739			fallbackTag = $( '<div></div>' );
740			fallbackTag.addClass( 'fl-bg-video-fallback' );
741			fallbackTag.css( 'background-image', 'url(' + playerWrap.data('fallback') + ')' );
742			playerWrap.append( fallbackTag );
743		},
744
745		/**
746		 * Check if Youtube API has been downloaded
747		 *
748		 * @since 1.9
749		 * @access private
750		 * @method _onYoutubeApiReady
751		 * @param  {Function} callback Method to call when YT API has been loaded
752		 */
753		_onYoutubeApiReady: function( callback ) {
754			if ( window.YT && YT.loaded ) {
755				callback( YT );
756			} else {
757				// If not ready check again by timeout..
758				setTimeout( function() {
759					FLBuilderLayout._onYoutubeApiReady( callback );
760				}, 350 );
761			}
762		},
763
764		/**
765		 * Initializes Vimeo video background
766		 *
767		 * @since 1.9
768		 * @access private
769		 * @method _initVimeoBgVideo
770		 */
771		_initVimeoBgVideo: function()
772		{
773			var playerWrap	= $(this),
774				videoId 	= playerWrap.data('video-id'),
775				videoHash 	= playerWrap.data('video-hash'),
776				videoPlayer = playerWrap.find('.fl-bg-video-player'),
777				enableAudio = playerWrap.data('enable-audio'),
778				audioButton = playerWrap.find('.fl-bg-video-audio'),
779				playPauseButton = playerWrap.find('.fl-bg-video-play-pause-control'),
780				playerState = '',
781				player,
782				width = playerWrap.outerWidth(),
783				ua    = navigator.userAgent;
784
785			if ( typeof Vimeo !== 'undefined' && videoId )	{
786				
787				const vimOptions = {
788					loop       : true,
789					title      : false,
790					portrait   : false,
791					background : true,
792					autopause  : false,
793					muted      : true,
794				};
795
796				if ( videoHash.length ) {
797					vimOptions.url = `https://player.vimeo.com/video/${ videoId }?h=${ videoHash }`;
798				} else {
799					vimOptions.id = videoId;
800				}
801
802				player = new Vimeo.Player(videoPlayer[0], vimOptions );
803				playerWrap.data('VMPlayer', player);
804				if ( "no" === enableAudio ) {
805					player.setVolume(0);
806				}
807				else if ("yes" === enableAudio ) {
808					// Chrome, Safari, Firefox have audio policy restrictions for autoplay videos.
809					if ( ua.indexOf("Safari") > -1 || ua.indexOf("Chrome") > -1 || ua.indexOf("Firefox") > -1 ) {
810						player.setVolume(0);
811						audioButton.show();
812					}
813					else {
814						player.setVolume(1);
815					}
816				}
817
818				player.play().catch(function(error) {
819					FLBuilderLayout._onErrorYoutubeVimeo(playerWrap)
820				});
821
822				if ( audioButton.length > 0 ) {
823					audioButton.on( 'click', {button: audioButton, player: player}, FLBuilderLayout._toggleBgVideoAudio );
824				}
825
826				player.on( 'play', function() {
827					playerState = 'play';
828					playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' );
829				} );
830				player.on( 'pause', function() {
831					playerState = 'pause';
832					playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' );
833				} );
834				player.on( 'ended', function() {
835					playerState = 'ended';
836					playPauseButton.removeClass( 'fa-pause' ).addClass( 'fa-play' );
837				} );
838				player.on( 'bufferstart', function() {
839					playerState = 'bufferstart';
840					playPauseButton.removeClass( 'fa-play' ).addClass( 'fa-pause' );
841				} );
842
843				if ( playPauseButton.length > 0 ) {
844					playPauseButton.on( 'click', { player: player }, function( e ) {
845						var player = e.data.player;
846
847						if ( playerState === 'play' ) {
848							player.pause();
849						} else {
850							player.play();
851						}
852					} );
853				}
854			}
855		},
856
857		/**
858		 * Mute / unmute audio on row's video background.
859		 * It works for both Youtube and Vimeo.
860		 *
861		 * @since 2.1.3
862		 * @access private
863		 * @method _toggleBgVideoAudio
864		 * @param {Object} e Method arguments
865		 */
866		_toggleBgVideoAudio: function( e ) {
867			var player  = e.data.player,
868			    control = e.data.button.find('.fl-audio-control');
869
870			if ( control.hasClass( 'fa-volume-off' ) ) {
871				// Unmute
872				control
873					.removeClass( 'fa-volume-off' )
874					.addClass( 'fa-volume-up' );
875				e.data.button.find( '.fa-times' ).hide();
876
877				if ( 'function' === typeof player.unMute ) {
878					player.unMute();
879				}
880				else {
881					player.setVolume( 1 );
882				}
883			}
884			else {
885				// Mute
886				control
887					.removeClass( 'fa-volume-up' )
888					.addClass( 'fa-volume-off' );
889				e.data.button.find( '.fa-times' ).show();
890
891				if ( 'function' === typeof player.unMute ) {
892					player.mute();
893				}
894				else {
895					player.setVolume( 0 );
896				}
897			}
898		},
899
900		/**
901		 * Fires when there is an error loading a video
902		 * background source and shows the fallback.
903		 *
904		 * @since 1.6.3.3
905		 * @access private
906		 * @method _videoBgSourceError
907		 * @param {Object} e An event object
908		 * @deprecated 2.0.3
909		 */
910		_videoBgSourceError: function( e )
911		{
912			var source 		= $( e.target ),
913				wrap   		= source.closest( '.fl-bg-video' ),
914				vid		    = wrap.find( 'video' ),
915				fallback  	= wrap.data( 'fallback' ),
916				fallbackTag = '';
917			source.remove();
918
919			if ( vid.find( 'source' ).length ) {
920				// Don't show the fallback if we still have other sources to check.
921				return;
922			} else if ( '' !== fallback ) {
923				fallbackTag = $( '<div></div>' );
924				fallbackTag.addClass( 'fl-bg-video-fallback' );
925				fallbackTag.css( 'background-image', 'url(' + fallback + ')' );
926				wrap.append( fallbackTag );
927				vid.remove();
928			}
929		},
930
931		/**
932		 * Fires when the window is resized to resize
933		 * all video backgrounds.
934		 *
935		 * @since 1.1.4
936		 * @access private
937		 * @method _resizeBgVideos
938		 */
939		_resizeBgVideos: function()
940		{
941			$('.fl-bg-video').each( function() {
942
943				FLBuilderLayout._resizeBgVideo.apply( this );
944
945				if ( $( this ).parent().find( 'img' ).length > 0 ) {
946					$( this ).parent().imagesLoaded( $.proxy( FLBuilderLayout._resizeBgVideo, this ) );
947				}
948			} );
949		},
950
951		/**
952		 * Fires when the window is resized to resize
953		 * a single video background.
954		 *
955		 * @since 1.1.4
956		 * @access private
957		 * @method _resizeBgVideo
958		 */
959		_resizeBgVideo: function()
960		{
961			if ( 0 === $( this ).find( 'video' ).length && 0 === $( this ).find( 'iframe' ).length ) {
962				return;
963			}
964
965			var wrap        = $(this),
966				wrapHeight  = wrap.outerHeight(),
967				wrapWidth   = wrap.outerWidth(),
968				vid         = wrap.find('video'),
969				vidHeight   = wrap.data('height'),
970				vidWidth    = wrap.data('width'),
971				newWidth    = wrapWidth,
972				newHeight   = Math.round(vidHeight * wrapWidth/vidWidth),
973				newLeft     = 0,
974				newTop      = 0,
975				iframe 		= wrap.find('iframe'),
976				isRowFullHeight = $(this).closest('.fl-row-bg-video').hasClass('fl-row-full-height'),
977				vidCSS          = {
978					top:       '50%',
979					left:      '50%',
980					transform: 'translate(-50%,-50%)',
981				};
982
983			if ( vid.length ) {
984				if(vidHeight === '' || typeof vidHeight === 'undefined' || vidWidth === '' || typeof vidWidth === 'undefined') {
985					vid.css({
986						'left'      : '0px',
987						'top'       : '0px',
988						'width'     : newWidth + 'px'
989					});
990
991					// Try to set the actual video dimension on 'loadedmetadata' when using URL as video source
992					vid.on('loadedmetadata', FLBuilderLayout._resizeOnLoadedMeta);
993
994					return;
995				}
996
997				if ( ! isRowFullHeight ) {
998					if ( newHeight < wrapHeight ) {
999						newHeight = wrapHeight;
1000						newLeft   = -((newWidth - wrapWidth) / 2);
1001						newWidth  = vidHeight ? Math.round(vidWidth * wrapHeight/vidHeight) : newWidth;
1002					}
1003					else {
1004						newTop = -((newHeight - wrapHeight)/2);
1005					}
1006					vidCSS = {
1007						left   : newLeft + 'px',
1008						top    : newTop + 'px',
1009						height : newHeight + 'px',
1010						width  : newWidth + 'px',
1011					}
1012				}
1013
1014				vid.css( vidCSS );
1015
1016			}
1017			else if ( iframe.length ) {
1018
1019				// Resize Youtube video player within iframe tag
1020				if ( typeof wrap.data('youtube') !== 'undefined' ) {
1021					FLBuilderLayout._resizeYoutubeBgVideo.apply(this);
1022				}
1023			}
1024		},
1025
1026		/**
1027		 * Fires when video meta has been loaded.
1028		 * This will be Triggered when width/height attributes were not specified during video background resizing.
1029		 *
1030		 * @since 1.8.5
1031		 * @access private
1032		 * @method _resizeOnLoadedMeta
1033		 */
1034		_resizeOnLoadedMeta: function(){
1035			var video 		= $(this),
1036				wrapHeight 	= video.parent().outerHeight(),
1037				wrapWidth 	= video.parent().outerWidth(),
1038				vidWidth 	= video[0].videoWidth,
1039				vidHeight 	= video[0].videoHeight,
1040				newHeight   = Math.round(vidHeight * wrapWidth/vidWidth),
1041				newWidth    = wrapWidth,
1042				newLeft     = 0,
1043				newTop 		= 0;
1044
1045			if(newHeight < wrapHeight) {
1046				newHeight   = wrapHeight;
1047				newWidth    = Math.round(vidWidth * wrapHeight/vidHeight);
1048				newLeft     = -((newWidth - wrapWidth)/2);
1049			}
1050			else {
1051				newTop      = -((newHeight - wrapHeight)/2);
1052			}
1053
1054			video.parent().data('width', vidWidth);
1055			video.parent().data('height', vidHeight);
1056
1057			video.css({
1058				'left'      : newLeft + 'px',
1059				'top'       : newTop + 'px',
1060				'width'     : newWidth + 'px',
1061				'height' 	: newHeight + 'px'
1062			});
1063		},
1064
1065		/**
1066		 * Fires when the window is resized to resize
1067		 * a single Youtube video background.
1068		 *
1069		 * @since 1.9
1070		 * @access private
1071		 * @method _resizeYoutubeBgVideo
1072		 */
1073		_resizeYoutubeBgVideo: function()
1074		{
1075			var wrap				= $(this),
1076				wrapWidth 			= wrap.outerWidth(),
1077				wrapHeight 			= wrap.outerHeight(),
1078				player 				= wrap.data('YTPlayer'),
1079				video 				= player ? player.getIframe() : null,
1080				aspectRatioSetting 	= '16:9', // Medium
1081				aspectRatioArray 	= aspectRatioSetting.split( ':' ),
1082				aspectRatio 		= aspectRatioArray[0] / aspectRatioArray[1],
1083				ratioWidth 			= wrapWidth / aspectRatio,
1084				ratioHeight 		= wrapHeight * aspectRatio,
1085				isWidthFixed 		= wrapWidth / wrapHeight > aspectRatio,
1086				width 				= isWidthFixed ? wrapWidth : ratioHeight,
1087				height 				= isWidthFixed ? ratioWidth : wrapHeight;
1088
1089			if ( video ) {
1090				$(video).width( width ).height( height );
1091			}
1092		},
1093
1094		/**
1095		 * Opens a tab or accordion item if the browser hash is set
1096		 * to the ID of one on the page.
1097		 *
1098		 * @since 1.6.0
1099		 * @access private
1100		 * @method _initHash
1101		 */
1102		_initHash: function()
1103		{
1104			var hash 			= window.location.hash.replace( '#', '' ).split( '/' ).shift(),
1105				element 		= null,
1106				tabs			= null,
1107				responsiveLabel	= null,
1108				tabIndex		= null,
1109				label			= null;
1110
1111			if ( '' !== hash ) {
1112
1113				try {
1114
1115					element = $( '#' + hash );
1116
1117					if ( element.length > 0 ) {
1118
1119						if ( element.hasClass( 'fl-accordion-item' ) ) {
1120							setTimeout( function() {
1121								element.find( '.fl-accordion-button' ).trigger( 'click' );
1122							}, 100 );
1123						}
1124						if ( element.hasClass( 'fl-tabs-panel' ) ) {
1125							setTimeout( function() {
1126								tabs 			= element.closest( '.fl-tabs' );
1127								responsiveLabel = element.find( '.fl-tabs-panel-label' );
1128								tabIndex 		= responsiveLabel.data( 'index' );
1129								label 			= tabs.find( '.fl-tabs-labels .fl-tabs-label[data-index=' + tabIndex + ']' );
1130
1131								label[0].click();
1132								FLBuilderLayout._scrollToElement(element);
1133							}, 100 );
1134						}
1135					}
1136				}
1137				catch( e ) {}
1138			}
1139		},
1140
1141		/**
1142		 * Initializes all anchor links on the page for smooth scrolling.
1143		 *
1144		 * @since 1.4.9
1145		 * @access private
1146		 * @method _initAnchorLinks
1147		 */
1148		_initAnchorLinks: function()
1149		{
1150			$( 'a, [role="link"]' ).each( FLBuilderLayout._initAnchorLink );
1151		},
1152
1153		/**
1154		 * Initializes a single anchor link for smooth scrolling.
1155		 *
1156		 * @since 1.4.9
1157		 * @access private
1158		 * @method _initAnchorLink
1159		 */
1160		_initAnchorLink: function()
1161		{
1162			var link    = $( this ),
1163				href    = link.data( 'url' ) ? link.data( 'url' ) : link.attr( 'href' ),
1164				target  = link.data( 'url' ) ? new URL( href, window.location.href ) : this,
1165				loc     = window.location,
1166				id      = null,
1167				element = null,
1168				flNode  = false;
1169
1170			if ( 'undefined' != typeof href && href.indexOf( '#' ) > -1 && link.closest('svg').length < 1 ) {
1171
1172				if ( loc.pathname.replace( /^\//, '' ) == target.pathname.replace( /^\//, '' ) && loc.hostname == target.hostname ) {
1173
1174					try {
1175
1176						id      = href.split( '#' ).pop();
1177						// If there is no ID then we have nowhere to look
1178						// Fixes a quirk in jQuery and FireFox
1179						if( ! id ) {
1180							return;
1181						}
1182						element = $( '#' + id );
1183
1184						if ( element.length > 0 ) {
1185							flNode = element.hasClass( 'fl-row' ) || element.hasClass( 'fl-col' ) || element.hasClass( 'fl-module' );
1186							if ( !element.hasClass( 'fl-no-scroll' ) && ( link.hasClass( 'fl-scroll-link' ) || flNode ) ) {
1187								$( link ).on( 'click', FLBuilderLayout._scrollToElementOnLinkClick );
1188							}
1189							if ( element.hasClass( 'fl-accordion-item' ) ) {
1190								$( link ).on( 'click', FLBuilderLayout._scrollToAccordionOnLinkClick );
1191							}
1192							if ( element.hasClass( 'fl-tabs-panel' ) ) {
1193								$( link ).on( 'click', FLBuilderLayout._scrollToTabOnLinkClick );
1194							}
1195						}
1196					}
1197					catch( e ) {}
1198				}
1199			}
1200		},
1201
1202		/**
1203		 * Scrolls to an element when an anchor link is clicked.
1204		 *
1205		 * @since 1.4.9
1206		 * @access private
1207		 * @method _scrollToElementOnLinkClick
1208		 * @param {Object} e An event object.
1209		 * @param {Function} callback A function to call when the scroll is complete.
1210		 */
1211		_scrollToElementOnLinkClick: function( e, callback )
1212		{
1213			var attribute = $( this ).data( 'url' ) ? $( this ).data( 'url' ) : $( this ).attr( 'href' );
1214			var element = $( '#' + attribute.split( '#' ).pop() );
1215
1216			FLBuilderLayout._scrollToElement( element, callback );
1217
1218			e.preventDefault();
1219		},
1220
1221		/**
1222		 * Scrolls to an element.
1223		 *
1224		 * @since 1.6.4.5
1225		 * @access private
1226		 * @method _scrollToElement
1227		 * @param {Object} element The element to scroll to.
1228		 * @param {Function} callback A function to call when the scroll is complete.
1229		 */
1230		_scrollToElement: function( element, callback )
1231		{
1232			var config  = FLBuilderLayoutConfig.anchorLinkAnimations,
1233				dest    = 0,
1234				win     = $( window ),
1235				doc     = $( document );
1236
1237			if ( element.length > 0 ) {
1238
1239				if ( 'fixed' === element.css('position') || 'fixed' === element.parent().css('position') ) {
1240					dest = element.position().top;
1241				}
1242				else if ( element.offset().top > doc.height() - win.height() ) {
1243					dest = doc.height() - win.height();
1244				}
1245				else {
1246					dest = element.offset().top - config.offset;
1247				}
1248
1249				$( 'html, body' ).stop( true ).animate( { scrollTop: dest }, config.duration, config.easing, function() {
1250
1251					if ( 'undefined' != typeof callback ) {
1252						callback();
1253					}
1254
1255					if ( undefined != element.attr( 'id' ) && window.location.hash !== '#' + element.attr( 'id' ) ) {
1256
1257						var firefox_version = window.navigator.userAgent.match( /Firefox\/(\d+)\./ ),
1258							firefox_version = firefox_version ? parseInt( firefox_version[1], 10 ) : null;
1259
1260						if ( firefox_version !== null && firefox_version < 135 ) {
1261							window.location.hash = element.attr( 'id' );
1262						} else {
1263							if ( history.pushState ) {
1264								history.pushState( null, null, '#' + element.attr( 'id' ) );
1265							} else {
1266								window.location.hash = element.attr( 'id' );
1267							}
1268						}
1269					}
1270				} );
1271			}
1272		},
1273
1274		/**
1275		 * Scrolls to an accordion item when a link is clicked.
1276		 *
1277		 * @since 1.5.9
1278		 * @access private
1279		 * @method _scrollToAccordionOnLinkClick
1280		 * @param {Object} e An event object.
1281		 */
1282		_scrollToAccordionOnLinkClick: function( e )
1283		{
1284			var element = $( '#' + $( this ).attr( 'href' ).split( '#' ).pop() );
1285
1286			if ( element.length > 0 ) {
1287
1288				var callback = function() {
1289					if ( element ) {
1290						element.find( '.fl-accordion-button' ).trigger( 'click' );
1291						element = false;
1292					}
1293				};
1294
1295				FLBuilderLayout._scrollToElementOnLinkClick.call( this, e, callback );
1296			}
1297		},
1298
1299		/**
1300		 * Scrolls to a tab panel when a link is clicked.
1301		 *
1302		 * @since 1.5.9
1303		 * @access private
1304		 * @method _scrollToTabOnLinkClick
1305		 * @param {Object} e An event object.
1306		 */
1307		_scrollToTabOnLinkClick: function( e )
1308		{
1309			var element 		= $( '#' + $( this ).attr( 'href' ).split( '#' ).pop() ),
1310				tabs			= null,
1311				label   		= null,
1312				responsiveLabel = null;
1313
1314			if ( element.length > 0 ) {
1315
1316				tabs 			= element.closest( '.fl-tabs' );
1317				responsiveLabel = element.find( '.fl-tabs-panel-label' );
1318				tabIndex 		= responsiveLabel.data( 'index' );
1319				label 			= tabs.find( '.fl-tabs-labels .fl-tabs-label[data-index=' + tabIndex + ']' );
1320
1321				if ( responsiveLabel.is( ':visible' ) ) {
1322
1323					var callback = function() {
1324						if ( element ) {
1325							responsiveLabel.trigger( $.Event( 'click', { which: 1 } ) );
1326						}
1327					};
1328
1329					FLBuilderLayout._scrollToElementOnLinkClick.call( this, e, callback );
1330				}
1331				else {
1332					label[0].click();
1333					FLBuilderLayout._scrollToElement( element );
1334				}
1335
1336				e.preventDefault();
1337			}
1338		},
1339
1340		/**
1341		 * Initializes all builder forms on a page.
1342		 *
1343		 * @since 1.5.4
1344		 * @access private
1345		 * @method _initForms
1346		 */
1347		_initForms: function()
1348		{
1349			if ( ! FLBuilderLayout._hasPlaceholderSupport ) {
1350				$( '.fl-form-field input' ).each( FLBuilderLayout._initFormFieldPlaceholderFallback );
1351			}
1352
1353			$( '.fl-form-field input' ).on( 'focus', FLBuilderLayout._clearFormFieldError );
1354		},
1355
1356		/**
1357		 * Checks to see if the current device has HTML5
1358		 * placeholder support.
1359		 *
1360		 * @since 1.5.4
1361		 * @access private
1362		 * @method _hasPlaceholderSupport
1363		 * @return {Boolean}
1364		 */
1365		_hasPlaceholderSupport: function()
1366		{
1367			var input = document.createElement( 'input' );
1368
1369			return 'undefined' != input.placeholder;
1370		},
1371
1372		/**
1373		 * Initializes the fallback for when placeholders aren't supported.
1374		 *
1375		 * @since 1.5.4
1376		 * @access private
1377		 * @method _initFormFieldPlaceholderFallback
1378		 */
1379		_initFormFieldPlaceholderFallback: function()
1380		{
1381			var field       = $( this ),
1382				val         = field.val(),
1383				placeholder = field.attr( 'placeholder' );
1384
1385			if ( 'undefined' != placeholder && '' === val ) {
1386				field.val( placeholder );
1387				field.on( 'focus', FLBuilderLayout._hideFormFieldPlaceholderFallback );
1388				field.on( 'blur', FLBuilderLayout._showFormFieldPlaceholderFallback );
1389			}
1390		},
1391
1392		/**
1393		 * Hides a fallback placeholder on focus.
1394		 *
1395		 * @since 1.5.4
1396		 * @access private
1397		 * @method _hideFormFieldPlaceholderFallback
1398		 */
1399		_hideFormFieldPlaceholderFallback: function()
1400		{
1401			var field       = $( this ),
1402				val         = field.val(),
1403				placeholder = field.attr( 'placeholder' );
1404
1405			if ( val == placeholder ) {
1406				field.val( '' );
1407			}
1408		},
1409
1410		/**
1411		 * Shows a fallback placeholder on blur.
1412		 *
1413		 * @since 1.5.4
1414		 * @access private
1415		 * @method _showFormFieldPlaceholderFallback
1416		 */
1417		_showFormFieldPlaceholderFallback: function()
1418		{
1419			var field       = $( this ),
1420				val         = field.val(),
1421				placeholder = field.attr( 'placeholder' );
1422
1423			if ( '' === val ) {
1424				field.val( placeholder );
1425			}
1426		},
1427
1428		/**
1429		 * Clears a form field error message.
1430		 *
1431		 * @since 1.5.4
1432		 * @access private
1433		 * @method _clearFormFieldError
1434		 */
1435		_clearFormFieldError: function()
1436		{
1437			var field = $( this );
1438
1439			field.removeAttr( 'aria-invalid' );
1440			field.removeClass( 'fl-form-error' );
1441			const message = field.attr('aria-describedby');
1442			message ? $( '#' + message ).hide() : field.siblings( '.fl-form-error-message' ).hide();
1443		},
1444
1445		/**
1446		 * Init Row Shape Layer's height.
1447		 *
1448		 * @since 2.5.3
1449		 * @access private
1450		 * @method _initRowShapeLayerHeight
1451		 */
1452		_initRowShapeLayerHeight: function () {
1453			FLBuilderLayout._adjustRowShapeLayerHeight();
1454			$( window ).on( 'resize', FLBuilderLayout._adjustRowShapeLayerHeight );
1455		},
1456
1457		/**
1458		 * Set parent column width of nested columns when it's zero or blank.
1459		 *
1460		 * @since 2.7.4
1461		 * @access private
1462		 * @method _initNestedColsWidth
1463		 */
1464		_initNestedColsWidth: function()
1465		{
1466			var nestedCols = $( '.fl-col-has-cols' );
1467
1468			if ( nestedCols.length <= 0 ) {
1469				return;
1470			}
1471
1472			$( nestedCols ).each( function(index, col ){
1473				if ( $( col ).width() <= 0 ) {
1474					$( col ).css( 'width', FLBuilderLayoutConfig.emptyColWidth );
1475				}
1476			});
1477		},
1478
1479		/**
1480		 * Adjust Row Shape Layer's height to fix to remove the fine line that appears on certain screen sizes.
1481		 *
1482		 * @since 2.5.3
1483		 * @access private
1484		 * @method _adjustRowShapeLayerHeight
1485		 */
1486		_adjustRowShapeLayerHeight: function() {
1487			var rowShapeLayers = $('.fl-builder-shape-layer');
1488
1489			$( rowShapeLayers ).each(function (index) {
1490				var rowShapeLayer = $(this),
1491					shape = $(rowShapeLayer).find('svg'),
1492					height = shape.height(),
1493					excludeShapes = '.fl-builder-shape-circle, .fl-builder-shape-dot-cluster, .fl-builder-shape-topography, .fl-builder-shape-rect';
1494
1495				if ( ! rowShapeLayer.is( excludeShapes ) ) {
1496					$(shape).css('height', Math.ceil( height ) );
1497				}
1498			});
1499		},
1500		_string_to_slug: function( str ) {
1501			str = str.replace(/^\s+|\s+$/g, ''); // trim
1502			if ( 'undefined' == typeof window._fl_string_to_slug_regex ) {
1503				regex = new RegExp('[^a-zA-Z0-9\'":() !.,-_|]', 'g');
1504			} else {
1505				regex = new RegExp('[^' + window._fl_string_to_slug_regex + '\'":\(\) !.,-_|\\\p{Letter}]', 'ug');
1506			}
1507			str = str.replace(regex, '') // remove invalid chars
1508				.replace(/\s+/g, ' '); // collapse whitespace and replace by a space
1509			return str;
1510		},
1511		_reorderMenu: function() {
1512			if ( $('#wp-admin-bar-fl-builder-frontend-edit-link-default li').length > 1 ) {
1513					$( '#wp-admin-bar-fl-builder-frontend-duplicate-link' )
1514					.appendTo('#wp-admin-bar-fl-builder-frontend-edit-link-default')
1515					.css( 'padding-top', '5px' )
1516					.css( 'border-top', '2px solid #1D2125' )
1517					.css( 'margin-top', '5px' )
1518				}
1519		}
1520	};
1521
1522	/* Initializes the builder layout. */
1523	$(function(){
1524		FLBuilderLayout.init();
1525	});
1526
1527})(jQuery);
1528(function($){
1529
1530	if(typeof FLBuilderLayoutModules !== 'undefined') {
1531		return;
1532	}
1533
1534	/**
1535	 * Helper class with generic logic for builder modules.
1536	 * If generic module code needs to work in both the block
1537	 * editor and builder, it should go here. Otherwise, it
1538	 * should go on FLBuilderLayout.
1539	 *
1540	 * @since 2.9
1541	 */
1542	FLBuilderLayoutModules = {
1543
1544		/**
1545		 * Initializes builder module logic.
1546		 *
1547		 * @since 2.9
1548		 * @method init
1549		 */
1550		init: function()
1551		{
1552			// Only init if the builder isn't active.
1553			if ( 0 === $('.fl-builder-edit').length ) {
1554
1555				// Init module animations.
1556				FLBuilderLayoutModules._initModuleAnimations();
1557			}
1558		},
1559
1560		/**
1561		 * Initializes module animations.
1562		 *
1563		 * @since 1.1.9
1564		 * @access private
1565		 * @method _initModuleAnimations
1566		 */
1567		_initModuleAnimations: function()
1568		{
1569			if(typeof jQuery.fn.waypoint !== 'undefined') {
1570				$('.fl-animation').each( function() {
1571					var node = $( this ),
1572						nodeTop = node.offset().top,
1573						winHeight = $( window ).height(),
1574						bodyHeight = $( 'body' ).height(),
1575						waypoint = FLBuilderLayoutConfig.waypoint,
1576						offset = '80%';
1577
1578					if ( typeof waypoint.offset !== undefined ) {
1579						offset = FLBuilderLayoutConfig.waypoint.offset + '%';
1580					}
1581
1582					if ( bodyHeight - nodeTop < winHeight * 0.2 ) {
1583						offset = '100%';
1584					}
1585
1586					node.waypoint({
1587						offset: offset,
1588						handler: FLBuilderLayoutModules._doModuleAnimation
1589					});
1590				} );
1591			}
1592		},
1593
1594		/**
1595		 * Runs a module animation.
1596		 *
1597		 * @since 1.1.9
1598		 * @access private
1599		 * @method _doModuleAnimation
1600		 */
1601		_doModuleAnimation: function()
1602		{
1603			var module = 'undefined' == typeof this.element ? $(this) : $(this.element),
1604				delay = parseFloat(module.data('animation-delay')),
1605				duration = parseFloat(module.data('animation-duration'));
1606
1607			if ( ! isNaN( duration ) ) {
1608				module.css( 'animation-duration', duration + 's' );
1609			}
1610
1611			if(!isNaN(delay) && delay > 0) {
1612				setTimeout(function(){
1613					module.addClass('fl-animated');
1614				}, delay * 1000);
1615			} else {
1616				setTimeout(function(){
1617					module.addClass('fl-animated');
1618				}, 1);
1619			}
1620		}
1621	};
1622
1623	/* Initializes builder module logic. */
1624	$(function(){
1625		FLBuilderLayoutModules.init();
1626	});
1627
1628})(jQuery);
1629
1630/* Start Global JS */
1631const addToCart = document.querySelector('#singleCartBTN .add_to_cart_button');
1632
1633if (addToCart) {
1634    addToCart.innerHTML = "buy now";
1635    addToCart.style.color = 'rgba(255, 255, 255, 1)';
1636    addToCart.addEventListener('mouseover', function(e) {
1637      e.target.style.color = '#ffca08';
1638    });
1639    addToCart.addEventListener('mouseout', function(e) {
1640      e.target.style.color = '#ffffff';
1641    });
1642}
1643/* End Global JS */
1644
1645jQuery(function($) {
1646	
1647		$(function() {
1648		$( '.fl-node-5f5a7d5cf0258 .fl-photo-img' )
1649			.on( 'mouseenter', function( e ) {
1650				$( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' );
1651			} )
1652			.on( 'mouseleave', function( e ){
1653				$( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null );
1654			} );
1655	});
1656		window._fl_string_to_slug_regex = 'a-zA-Z0-9';
1657});
1658jQuery(function($) {
1659	
1660		$(function() {
1661		$( '.fl-node-5f5a818e158d1 .fl-photo-img' )
1662			.on( 'mouseenter', function( e ) {
1663				$( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' );
1664			} )
1665			.on( 'mouseleave', function( e ){
1666				$( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null );
1667			} );
1668	});
1669		window._fl_string_to_slug_regex = 'a-zA-Z0-9';
1670});
1671jQuery(function($) {
1672	
1673		$(function() {
1674		$( '.fl-node-5f5a88807b44f .fl-photo-img' )
1675			.on( 'mouseenter', function( e ) {
1676				$( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' );
1677			} )
1678			.on( 'mouseleave', function( e ){
1679				$( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null );
1680			} );
1681	});
1682		window._fl_string_to_slug_regex = 'a-zA-Z0-9';
1683});
1684jQuery(function($) {
1685	
1686		$(function() {
1687		$( '.fl-node-5f652b1b4fe3d .fl-photo-img' )
1688			.on( 'mouseenter', function( e ) {
1689				$( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' );
1690			} )
1691			.on( 'mouseleave', function( e ){
1692				$( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null );
1693			} );
1694	});
1695		window._fl_string_to_slug_regex = 'a-zA-Z0-9';
1696});
1697jQuery(function($) {
1698	
1699		$(function() {
1700		$( '.fl-node-5f652b3662e3b .fl-photo-img' )
1701			.on( 'mouseenter', function( e ) {
1702				$( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' );
1703			} )
1704			.on( 'mouseleave', function( e ){
1705				$( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null );
1706			} );
1707	});
1708		window._fl_string_to_slug_regex = 'a-zA-Z0-9';
1709});
1710jQuery(function($) {
1711	
1712		$(function() {
1713		$( '.fl-node-5f652cb4b9a98 .fl-photo-img' )
1714			.on( 'mouseenter', function( e ) {
1715				$( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' );
1716			} )
1717			.on( 'mouseleave', function( e ){
1718				$( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null );
1719			} );
1720	});
1721		window._fl_string_to_slug_regex = 'a-zA-Z0-9';
1722});
1723jQuery(function($) {
1724	
1725		$(function() {
1726		$( '.fl-node-5f5a7b2b8d39f .fl-photo-img' )
1727			.on( 'mouseenter', function( e ) {
1728				$( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' );
1729			} )
1730			.on( 'mouseleave', function( e ){
1731				$( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null );
1732			} );
1733	});
1734		window._fl_string_to_slug_regex = 'a-zA-Z0-9';
1735});
1736jQuery(function($) {
1737	
1738		$(function() {
1739		$( '.fl-node-5f5a7ad315be0 .fl-photo-img' )
1740			.on( 'mouseenter', function( e ) {
1741				$( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' );
1742			} )
1743			.on( 'mouseleave', function( e ){
1744				$( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null );
1745			} );
1746	});
1747		window._fl_string_to_slug_regex = 'a-zA-Z0-9';
1748});
1749jQuery(function($) {
1750	
1751		$(function() {
1752		$( '.fl-node-5f5a7b09af746 .fl-photo-img' )
1753			.on( 'mouseenter', function( e ) {
1754				$( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' );
1755			} )
1756			.on( 'mouseleave', function( e ){
1757				$( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null );
1758			} );
1759	});
1760		window._fl_string_to_slug_regex = 'a-zA-Z0-9';
1761});
1762(function($){
1763	})(jQuery);
1764jQuery(function($) {
1765	
1766		$(function() {
1767		$( '.fl-node-0uysiczf5pmo .fl-photo-img' )
1768			.on( 'mouseenter', function( e ) {
1769				$( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' );
1770			} )
1771			.on( 'mouseleave', function( e ){
1772				$( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null );
1773			} );
1774	});
1775		window._fl_string_to_slug_regex = 'a-zA-Z0-9';
1776});
1777jQuery(function($) {
1778	
1779		$(function() {
1780		$( '.fl-node-vas6gpbhu0dm .fl-photo-img' )
1781			.on( 'mouseenter', function( e ) {
1782				$( this ).data( 'title', $( this ).attr( 'title' ) ).removeAttr( 'title' );
1783			} )
1784			.on( 'mouseleave', function( e ){
1785				$( this ).attr( 'title', $( this ).data( 'title' ) ).data( 'title', null );
1786			} );
1787	});
1788		window._fl_string_to_slug_regex = 'a-zA-Z0-9';
1789});
1790(function($){
1791	})(jQuery);
1792
1793/* Start Global Node Custom JS */
1794
1795/* End Global Node Custom JS */
1796
1797
1798/* Start Layout Custom JS */
1799
1800/* End Layout Custom JS */
1801

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.