PageSourceSearch

https://rizepropertymanagement.com/wp-content/uploads/bb-plugin/ca…?ver=a3625031e8d5be48d79e1f0ef2844656

js rizepropertymanagement.com collected 2026-10-08 00:26:29 UTC 39,566 bytes, 1,487 lines download raw bytes

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

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.