fl-builder-layout.js 37 KB

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