var cssua=function(e,o,i){"use strict";var s=/\s*([\-\w ]+)[\s\/\:]([\d_]+\b(?:[\-\._\/]\w+)*)/,r=/([\w\-\.]+[\s\/][v]?[\d_]+\b(?:[\-\._\/]\w+)*)/g,n=/\b(?:(blackberry\w*|bb10)|(rim tablet os))(?:\/(\d+\.\d+(?:\.\w+)*))?/,a=/\bsilk-accelerated=true\b/,b=/\bfluidapp\b/,t=/(\bwindows\b|\bmacintosh\b|\blinux\b|\bunix\b)/,l=/(\bandroid\b|\bipad\b|\bipod\b|\bwindows phone\b|\bwpdesktop\b|\bxblwp7\b|\bzunewp7\b|\bwindows ce\b|\bblackberry\w*|\bbb10\b|\brim tablet os\b|\bmeego|\bwebos\b|\bpalm|\bsymbian|\bj2me\b|\bdocomo\b|\bpda\b|\bchtml\b|\bmidp\b|\bcldc\b|\w*?mobile\w*?|\w*?phone\w*?)/,p=/(\bxbox\b|\bplaystation\b|\bnintendo\s+\w+)/,c={parse:function(e,o){var i={};if(o&&(i.standalone=o),!(e=(""+e).toLowerCase()))return i;for(var c,d,m=e.split(/[()]/),w=0,_=m.length;w<_;w++)if(w%2){var u=m[w].split(";");for(c=0,d=u.length;c0;)i+=" ua-"+e+"-"+o.substring(0,s),s=o.indexOf("-",s+1);i+=" ua-"+e+"-"+o}return i}var i="";for(var s in e)s&&e.hasOwnProperty(s)&&(i+=o(s,e[s]));return i},encode:function(e){var o="";for(var i in e)i&&e.hasOwnProperty(i)&&(o&&(o+="&"),o+=encodeURIComponent(i)+"="+encodeURIComponent(e[i]));return o}};c.userAgent=c.ua=c.parse(o,i);var d=c.format(c.ua)+" js";return e.className?e.className=e.className.replace(/\bno-js\b/g,"")+d:e.className=d.substr(1),c}(document.documentElement,navigator.userAgent,navigator.standalone);;/* global FusionApp, FusionEvents, fusionBuilderText */ var FusionPageBuilder = FusionPageBuilder || {}; ( function() { jQuery( document ).ready( function() { // Builder Toolbar FusionPageBuilder.Toolbar = window.wp.Backbone.View.extend( { /** * Boolean if the sidebar panel is open. */ isSidebarOpen: false, template: FusionPageBuilder.template( jQuery( '#fusion-app-front-end-toolbar' ).html() ), events: { 'click .trigger-submenu-toggling': 'toggleSubMenu', 'click .fusion-builder-preview-viewport .toggle-viewport': 'previewViewport', 'click .fusion-builder-preview-viewport .toggle-viewport-breakpoints': 'toggleViewportBreakpoints', 'click #fusion-frontend-builder-toggle-global-panel': 'togglePanel', 'click .fusion-exit-builder-list a': 'exitBuilder', 'click [data-link]': 'languageSwitch', 'click .preview a': 'previewToggle', 'click .toolbar-toggle a': 'toolbarToggle', 'click .fusion-builder-save-page': 'savePage', 'click .fusion-builder-keyboard-shortcuts': 'openKeyBoardShortCuts', 'change .save-wrapper .post-status input': 'updatePostStatus' }, /** * Initialize empty language data. * * @since 2.0.0 * @param {Object} attributes - The attributes object. * @return {Object} this */ initialize: function( attributes ) { // Default empty language data. this.languageData = { switcher: false, active: false }; this.viewport = 'desktop'; // Whether to use flags for labels. this.languageFlags = true; this.previewMode = false; // We need to check clicks everywhere, not just in the toolbar // so this can't be a standard listener in the events object. this.toggleSubMenusCloseHandler(); this.listenTo( attributes.fusionApp, 'change:hasChange', this.render ); this.listenTo( FusionEvents, 'fusion-disconnected', this.setWarningColor ); this.listenTo( FusionEvents, 'fusion-reconnected', this.removeWarningColor ); this.listenTo( FusionEvents, 'fusion-sidebar-toggled', this.setActiveStyling ); this.listenTo( FusionEvents, 'fusion-app-setup', this.reEnablePreviewMode ); // Debounced event trigger. this._triggerEvent = _.debounce( _.bind( this.triggerEvent, this ), 300 ); }, /** * Renders the view. * * @since 2.0.0 * @return {Object} this */ render: function() { this.$el.html( this.template( { switcher: this.languageData.switcher, postChanged: FusionApp.get( 'hasChange' ), postStatus: FusionApp.getPost( 'post_status' ), sidebarOpen: jQuery( 'body' ).hasClass( 'expanded' ) } ) ); if ( 'undefined' !== typeof FusionApp && FusionApp.builderToolbarView ) { jQuery( '.fusion-builder-live-toolbar' ).append( FusionApp.builderToolbarView.render().el ); } this.previewViewport(); return this; }, /** * Changes the viewport. * * @since 2.0.0 * @param {Object} event - The event. * @return {void} */ previewViewport: function( event ) { var self = this, indicator = jQuery( 'li.fusion-builder-preview-viewport .viewport-indicator' ), indicatorIcons = jQuery( indicator.find( 'a[data-indicate-viewport]' ) ); if ( event ) { event.preventDefault(); this.viewport = jQuery( event.currentTarget ).attr( 'data-viewport' ); FusionApp.setPreviewWindowSize( this.viewport ); } // Change the indicator icon depending on the active viewport. _.each( indicatorIcons, function( indicatorIcon ) { var indicatorViewport = jQuery( indicatorIcon ).data( 'indicate-viewport' ); jQuery( indicatorIcon ).removeClass( 'active' ); if ( self.viewport === indicatorViewport ) { jQuery( indicatorIcon ).addClass( 'active' ); } } ); // Mark the selected viewport as active. jQuery( 'a.viewport-indicator > span' ).removeClass( 'active' ); jQuery( 'a.viewport-indicator > span[data-indicate-viewport="' + self.viewport + '"]' ).addClass( 'active' ); jQuery( window ).trigger( 'resize' ); // Timing related check. Sometimes the jQuery isn't loaded yet in iframe. if ( 'function' === typeof jQuery( '#fb-preview' )[ 0 ].contentWindow.jQuery ) { jQuery( '#fb-preview' )[ 0 ].contentWindow.jQuery( 'body' ).trigger( 'resize' ); } jQuery( '#fb-preview' ).attr( 'data-viewport', self.viewport ); // For responsive element options. this.responsiveOptions( self.viewport ); if ( event ) { this._triggerEvent(); } }, /** * Trigger viewport update event. * * @since 3.0 * @return {void} */ triggerEvent: function() { FusionEvents.trigger( 'fusion-preview-viewport-update' ); }, /** * Toggles predefined / custom viewport breakpoints. * * @since 2.0.0 * @param {Object} event - The event. * @return {void} */ toggleViewportBreakpoints: function( event ) { var $target, $parent; if ( ! event ) { return; } event.preventDefault(); $target = jQuery( event.target ); $parent = $target.closest( 'li' ); $target.siblings().removeClass( 'active-breakpoints' ); $target.addClass( 'active-breakpoints' ); $parent.find( '> ul' ).attr( 'aria-expanded', 'false' ); $parent.find( '> #' + $target.data( 'viewport-breakpoints' ) ).attr( 'aria-expanded', 'true' ); }, /** * Toggle preview mode. * * @since 2.0.0 * @param {Object} event - The JS event. * @return {void} */ previewToggle: function( event ) { var self = this; if ( event ) { event.preventDefault(); } if ( this.previewMode ) { // Disable preview mode. jQuery( 'body' ).removeClass( 'fusion-builder-preview-mode' ); jQuery( '#fb-preview' )[ 0 ].contentWindow.jQuery( 'body' ).removeClass( 'fusion-builder-preview-mode' ); jQuery( '.fusion-builder-live .fusion-builder-live-toolbar .fusion-toolbar-nav li.preview a' ).removeClass( 'active' ); // If we're on preview mode, make sure the global-options sidebar is hidden. if ( this.isSidebarOpen ) { FusionApp.sidebarView.openSidebar(); this.isSidebarOpen = false; } // Remove the stylesheet for preview CSS. jQuery( '#fb-preview' )[ 0 ].contentWindow.jQuery( '#fusion-preview-frame-builder-no-controls-css-css' ).attr( 'media', 'none' ); this.previewMode = false; FusionEvents.trigger( 'fusion-preview-toggle' ); } else { self.enablePreviewMode( true ); } }, reEnablePreviewMode: function() { if ( this.previewMode ) { this.enablePreviewMode( false ); } }, /** * Toggle the toolbar. * * @since 2.0.0 * @param {Object} event - The JS event. * @return {void} */ toolbarToggle: function( event ) { if ( 'undefined' !== typeof event ) { event.preventDefault(); } jQuery( 'body' ).toggleClass( 'collapsed-toolbar' ); }, /** * Enables preview mode. * * @since 2.0.0 * @return {void} */ enablePreviewMode: function( toggled ) { toggled = 'undefined' === typeof toggled ? false : toggled; jQuery( '#fb-preview' )[ 0 ].contentWindow.jQuery( 'body' ).addClass( 'fusion-builder-preview-mode' ); jQuery( 'body' ).addClass( 'fusion-builder-preview-mode' ); jQuery( '.fusion-builder-live .fusion-builder-live-toolbar .fusion-toolbar-nav li.preview a' ).addClass( 'active' ); if ( toggled ) { this.isSidebarOpen = FusionApp.sidebarView.panelIsOpen(); // If we're on preview mode, make sure the global-options sidebar is hidden. FusionApp.sidebarView.closeSidebar(); // Hide already open inline toolbars. this.clearSelection( jQuery( '#fb-preview' )[ 0 ].contentWindow ); jQuery( '#fb-preview' ).contents().find( '.medium-editor-toolbar-actions.visible' ).removeClass( 'visible' ); // If we're on preview mode, close open dialogs. jQuery( '.ui-dialog-content' ).dialog( 'close' ); } // Add the stylesheet for preview CSS. jQuery( '#fb-preview' )[ 0 ].contentWindow.jQuery( '#fusion-preview-frame-builder-no-controls-css-css' ).attr( 'media', 'all' ); this.previewMode = true; FusionEvents.trigger( 'fusion-preview-toggle' ); }, clearSelection: function( frameWindow ) { if ( frameWindow.getSelection ) { if ( frameWindow.getSelection().empty ) { // Chrome frameWindow.getSelection().empty(); } else if ( frameWindow.getSelection().removeAllRanges ) { // Firefox frameWindow.getSelection().removeAllRanges(); } } else if ( frameWindow.selection ) { // IE? frameWindow.selection.empty(); } }, /** * Exit the builder and return to the frontend. * * @since 2.0.0 * @param {Object} event - The JS event. * @return {void} */ exitBuilder: function( event ) { var linkTag = jQuery( event.currentTarget ), link = linkTag.attr( 'href' ), frameUrl = jQuery( '#fb-preview' ).attr( 'src' ); event.preventDefault(); if ( ! linkTag.parent().hasClass( 'exit-to-dashboard' ) ) { if ( linkTag.parent().hasClass( 'exit-to-back-end' ) ) { link = FusionApp.data.backendLink || linkTag.data( 'admin-url' ) + '?post=' + FusionApp.getPost( 'post_id' ) + '&action=edit'; } else { if ( -1 !== frameUrl.indexOf( 'builder=true' ) ) { frameUrl = frameUrl.split( 'builder=true' ); frameUrl = frameUrl[ 0 ]; if ( '?' === frameUrl[ frameUrl.length - 1 ] ) { frameUrl = frameUrl.slice( 0, -1 ); } } link = frameUrl; } } // cmd/ctrl and click, open in new tab. if ( FusionApp.modifierActive ) { window.open( link, '_blank' ); return; } // Make user confirm. if ( FusionApp.hasContentChanged( 'page' ) ) { FusionApp.confirmationPopup( { title: fusionBuilderText.unsaved_changes, content: fusionBuilderText.changes_will_be_lost, class: 'fusion-confirmation-unsaved-changes', actions: [ { label: fusionBuilderText.cancel, classes: 'cancel no', callback: function() { FusionApp.confirmationPopup( { action: 'hide' } ); } }, { label: fusionBuilderText.just_leave, classes: 'dont-save yes', callback: function() { FusionApp.manualSwitch = true; window.location.href = link; } }, { label: fusionBuilderText.leave, classes: 'save yes', callback: function() { var successAction = {}; successAction.action = 'exit_builder'; successAction.link = link; FusionApp.savePostContent( successAction ); } } ] } ); return; } FusionApp.manualSwitch = true; window.location.href = link; }, /** * Creates/updates the language switcher. * * @since 2.0.0 * @return {void} */ updateLanguageSwitcher: function() { this.languageData = { switcher: FusionApp.data.languageSwitcher, active: FusionApp.data.language }; this.render(); }, /** * Get language flag from data. * * @since 2.0.0 * @param {Object} data - The data formatted as an object containing "flag", "country_flag_url", "native_name", "name". * @param {string} id - The language ID. * @return {string} */ getLanguageFlag: function( data, id ) { var $languageFlag = ''; // No data, return id. if ( 'undefined' === typeof data ) { $languageFlag = id; } // Flag checks. if ( this.languageFlags ) { if ( 'undefined' !== typeof data.flag ) { $languageFlag = ' '; } if ( 'undefined' !== typeof data.country_flag_url ) { $languageFlag = ' '; } } return $languageFlag; }, /** * Get language label from data. * * @since 2.0.0 * @param {Object} data - The data formatted as an object containing "flag", "country_flag_url", "native_name", "name". * @param {string} id - The language ID. * @return {string} */ getLanguageLabel: function( data, id ) { var $languageLabel = ''; // No data, return id. if ( 'undefined' === typeof data ) { $languageLabel = id; } // WPML and PLL checks. if ( 'undefined' !== typeof data.native_name ) { $languageLabel += data.native_name; } if ( 'undefined' !== typeof data.name ) { $languageLabel += data.name; } return $languageLabel; }, /** * Get language link from data. * * @since 2.0.0 * @param {Object} data - The data formatted as an object containing "url". * @param {string} id - The language ID. * @return {void} */ getLanguageLink: function( data, id ) { if ( 'undefined' === typeof data ) { return id; } if ( 'undefined' !== typeof data.url ) { return data.url; } return id; }, /** * Switch the page language. * * @since 2.0.0 * @param {Object} event - The JS event. * @return {void} */ languageSwitch: function( event ) { var targetUrl = jQuery( event.currentTarget ).data( 'link' ); event.preventDefault(); if ( '' !== targetUrl ) { // If global has changed, we need to create transients before switching. if ( FusionApp.hasContentChanged( 'global' ) ) { FusionApp.fullRefresh( targetUrl, event ); } else { FusionApp.checkLink( event, targetUrl ); } } }, /** * Toggles sidebar open or closed. * * @since 2.0.0 * @param {Object} event - The JS event. * @return {void} */ togglePanel: function( event ) { if ( 'undefined' !== typeof event ) { event.preventDefault(); } if ( 'undefined' !== typeof FusionApp.sidebarView.togglePanel ) { FusionApp.sidebarView.togglePanel(); } }, setActiveStyling: function( open ) { var $anchor = this.$el.find( '#fusion-frontend-builder-toggle-global-panel' ); if ( open ) { $anchor.addClass( 'active' ); } else { $anchor.removeClass( 'active' ); } }, /** * Toggles the submenu. * * @param {Object} event - The click event. * @return {void} */ toggleSubMenu: function( event ) { var eventTarget = jQuery( event.target ), triggers = jQuery( '.fusion-builder-live-toolbar .trigger-submenu-toggling' ), subMenus = jQuery( '.fusion-builder-live-toolbar .submenu-trigger-target' ), subMenu; if ( 'undefined' !== typeof event ) { event.preventDefault(); } if ( jQuery( 'body' ).hasClass( 'fusion-hide-all-tooltips' ) ) { return; } if ( ! eventTarget.hasClass( 'trigger-submenu-toggling' ) ) { eventTarget = jQuery( eventTarget.closest( '.trigger-submenu-toggling' ) ); } subMenu = eventTarget.parent().find( '.submenu-trigger-target' ); if ( subMenu.length ) { if ( 'false' === subMenu.attr( 'aria-expanded' ) ) { subMenu.attr( 'aria-expanded', 'true' ); eventTarget.addClass( 'active' ); // Close any other open submenus that might exist. _.each( triggers, function( trigger ) { if ( jQuery( trigger )[ 0 ] !== jQuery( eventTarget )[ 0 ] ) { jQuery( trigger ).removeClass( 'active' ); } } ); _.each( subMenus, function( sub ) { if ( jQuery( sub )[ 0 ] !== jQuery( subMenu )[ 0 ] ) { jQuery( sub ).attr( 'aria-expanded', 'false' ); } } ); } else { subMenu.attr( 'aria-expanded', 'false' ); eventTarget.removeClass( 'active' ); } } }, /** * Closes submenus when we click outside the trigger. * * @return {void} */ toggleSubMenusCloseHandler: function() { // Passive is a significant performance improvement // so we should use it if supported by the browser. var self = this, passiveSupported = false, passive = false, options; try { options = { get passive() { // jshint ignore:line passiveSupported = true; return true; } }; window.addEventListener( 'test', options, options ); window.removeEventListener( 'test', options, options ); } catch ( err ) { passiveSupported = false; } passive = passiveSupported ? { passive: true } : false; window.addEventListener( 'click', self.toggleSubMenusClose, passive ); window.frames[ 0 ].window.addEventListener( 'click', self.toggleSubMenusClose, passive ); }, /** * Closes submenus when we click outside the trigger. * * @param {Object} event - The event. * @return {void} */ toggleSubMenusClose: function( event ) { var target = jQuery( event.target ), allSubMenus = jQuery( '.fusion-builder-live-toolbar .submenu-trigger-target' ), subMenu; if ( target.hasClass( 'trigger-submenu-toggling' ) || target.closest( '.trigger-submenu-toggling' ).length ) { // We clicked on a toggle, so we need to close all OTHER dropdowns. // First of all, make sure we've got the right target element. if ( ! target.hasClass( 'submenu-trigger-target' ) ) { target = target.parent().find( '.submenu-trigger-target' ); } // Find the submenu. subMenu = target.parent().find( '.submenu-trigger-target' ); // If we could not find the submenu, early exit. if ( ! subMenu.length ) { return; } // Go through all submenus _.each( allSubMenus, function( item ) { // Skip current item. if ( subMenu[ 0 ].offsetParent === item.offsetParent ) { return; } jQuery( item ).attr( 'aria-expanded', false ); } ); } else { // We did not click on a toggle, close ALL dropdowns. allSubMenus.attr( 'aria-expanded', false ); // Go through all buttons and remove .active class. _.each( jQuery( '.fusion-builder-live-toolbar .trigger-submenu-toggling.active' ), function( item ) { jQuery( item ).removeClass( 'active' ); } ); } }, /** * Renders the FusionPageBuilder.KeyBoardShortcuts View view. * * @since 2.0.0 * @param {Object} event - The event. * @return {void} */ openKeyBoardShortCuts: function( event ) { var view; if ( 'undefined' !== typeof event ) { event.preventDefault(); event.stopPropagation(); } if ( jQuery( '.fusion-builder-dialog' ).length && jQuery( '.fusion-builder-dialog' ).is( ':visible' ) ) { FusionApp.multipleDialogsNotice(); return; } view = new FusionPageBuilder.keyBoardShorCutsView(); view.render(); }, /** * Sets a warning to let you know connection has been lost. * * @since 2.0.0 * @return {void} */ setWarningColor: function() { this.$el.find( '.fusion-builder-save-page' ).addClass( 'failed' ); }, /** * Remove warning to let you know re-connection successful. * * @since 2.0.0 * @return {void} */ removeWarningColor: function() { this.$el.find( '.fusion-builder-save-page' ).removeClass( 'failed' ); }, /** * Saves the page. * * @since 2.0.0 * @param {Object} event - The event. * @return {void} */ savePage: function( event ) { if ( event ) { event.preventDefault(); } // Do not proceed if button is disabled. if ( 'true' === jQuery( event.target ).data( 'disabled' ) || true === jQuery( event.target ).data( 'disabled' ) ) { return; } FusionApp.savePostContent(); }, /** * Updates the post status. * * @since 2.0 * @return {void} */ updatePostStatus: function() { var postStatus = this.$el.find( '.save-wrapper .post-status input:checked' ).length ? this.$el.find( '.save-wrapper .post-status input:checked' ).val() : FusionApp.getPost( 'post_status' ); FusionApp.setPost( 'post_status', postStatus ); FusionApp.contentChange( 'page', 'page-setting' ); }, /** * Adds data for responsive options. * * @since 3.0 * @param {String} viewport - The selected viewport. * @return {void} */ responsiveOptions: function( viewport ) { var viewPorts = { 'desktop': 'large', 'tablet-portrait-custom': 'medium', 'mobile-portrait-custom': 'small' }; jQuery( 'body' ).removeClass( function ( index, className ) { return ( className.match( /(^|\s)fusion-builder-module-settings-\S+/g ) || [] ).join( ' ' ); } ); jQuery( 'body' ).addClass( 'fusion-builder-module-settings-' + viewPorts[ viewport ] ); jQuery( '#fb-preview' )[ 0 ].contentWindow.jQuery( 'body' ).removeClass( function ( index, className ) { return ( className.match( /(^|\s)awb-le-viewport-S+/g ) || [] ).join( ' ' ); } ); jQuery( '#fb-preview' )[ 0 ].contentWindow.jQuery( 'body' ).addClass( 'awb-le-viewport-' + viewPorts[ viewport ] ); } } ); } ); }( jQuery ) ); ;/* global FusionEvents */ var FusionPageBuilder = FusionPageBuilder || {}; ( function() { FusionPageBuilder.Dialog = Backbone.Model.extend( { initialize: function() { var self = this; // Dialog as percentage. this.dialogWidth = 0.85 * jQuery( window ).width(), this.dialogHeight = 0.9 * jQuery( window ).height(); // Initial dialog settings. this.setDialogData(); jQuery( window ).on( 'resize', function() { self.resizeDialog(); } ); this.extendDialog(); }, extendDialog: function() { jQuery.widget( 'ui.dialog', jQuery.extend( {}, jQuery.ui.dialog.prototype, { _title: function( title ) { var $dialogContent = this.element, $tabMenu = $dialogContent.find( '.fusion-builder-modal-top-container' ), $titleBar = title.closest( '.ui-dialog-titlebar' ); $dialogContent.before( $tabMenu ); if ( $dialogContent.parent( '.fusion-builder-child-element' ).length ) { $titleBar.find( '.ui-dialog-title' ).before( '' ); } else if ( 'undefined' !== typeof this.options.type ) { $titleBar.find( '.ui-dialog-titlebar-close' ).before( '
' ); } if ( ! this.options.title ) { title.html( ' ' ); } else { title.text( this.options.title ); } }, _hide: function( event ) { jQuery( '#fb-preview' )[ 0 ].contentWindow.jQuery( 'body' ).removeClass( 'fusion-dialog-ui-active' ); this._trigger( 'close', event ); } } ) ); }, /** * Resizes dialogs. * * @since 2.0.0 * @return {void} */ resizeDialog: function() { var titleBar = jQuery( '.fusion-builder-large-library-dialog .ui-dialog-titlebar' ), titleBarHeight = titleBar.length ? titleBar.height() : 0; this.dialogWidth = 0.85 * jQuery( window ).width(), this.dialogHeight = ( 0.9 * ( jQuery( window ).height() - 54 ) ) - titleBarHeight; jQuery( '.fusion_builder_modal_settings:ui-dialog, #fusion-builder-front-end-library:ui-dialog, .fusion-builder-keyboard-shortcuts-dialog .ui-dialog-content:ui-dialog, .fusion-builder-preferences-dialog .ui-dialog-content:ui-dialog' ).dialog( 'option', 'width', this.dialogWidth ); jQuery( '.fusion_builder_modal_settings:ui-dialog, #fusion-builder-front-end-library:ui-dialog, .fusion-builder-keyboard-shortcuts-dialog .ui-dialog-content:ui-dialog, .fusion-builder-preferences-dialog .ui-dialog-content:ui-dialog' ).dialog( 'option', 'height', this.dialogHeight ); }, /** * Sets the dialog data from browser if it exists. * * @since 2.0.0 * @return {void} */ setDialogData: function() { if ( 'undefined' !== typeof Storage && 'undefined' !== localStorage.getItem( 'dialogData' ) && localStorage.getItem( 'dialogData' ) ) { this.dialogData = JSON.parse( localStorage.getItem( 'dialogData' ) ); this.dialogData.of = window; this.dialogData.width = this.dialogData.width > jQuery( window ).width() ? jQuery( window ).width() : this.dialogData.width; this.dialogData.height = this.dialogData.height > jQuery( window ).height() ? jQuery( window ).height() : this.dialogData.height; } else { this.dialogData = { width: 450, height: 400, position: { my: 'right bottom', at: 'right-50 bottom-100', of: window } }; } }, /** * Saves the position of a dialog. * * @since 2.0.0 * @param {Object} [offset] Contains the position left & top args. * @return {void} */ saveDialogPosition: function( offset ) { this.dialogData.position = { my: 'left top', at: 'left+' + offset.left + ' top+' + offset.top + '' }; this.storeDialogData(); }, /** * Saves the dialog size. * * @since 2.0.0 * @param {Object} [size] Contains the width & height params. * @return {void} */ saveDialogSize: function( size ) { this.dialogData.width = size.width; this.dialogData.height = size.height; this.storeDialogData(); }, /** * Checks if dialog is positioned out of viewport. * * @since 2.0.0 * @param {Object} [offset] Contains the position left & top args. * @return {boolean} */ maybeRepositionDialog: function( $dialog ) { if ( jQuery( window ).width() < $dialog.offset().left + $dialog.width() ) { jQuery( $dialog ).position( { my: 'center', at: 'center', of: window } ); return true; } return false; }, /** * Stored dialog data in browser. * * @since 2.0.0 * @return {void} */ storeDialogData: function() { var saveData = jQuery.extend( true, {}, this.dialogData ); delete saveData.of; delete saveData.position.of; if ( 'undefined' !== typeof Storage ) { localStorage.setItem( 'dialogData', JSON.stringify( saveData ) ); } }, /** * Handle tabs in dialogs. * * @since 2.0.0 * @param {Object} [thisEl] The element. * @return {void} */ dialogTabs: function( thisEl ) { thisEl.find( '.fusion-tabs-menu a' ).on( 'click', function( event ) { var target = jQuery( this ).attr( 'href' ) + '.fusion-tab-content'; jQuery( this ).parent( 'li' ).siblings().removeClass( 'current' ); jQuery( this ).parent( 'li' ).addClass( 'current' ); event.preventDefault(); thisEl.find( '.fusion-tab-content' ).hide().removeClass( 'active' ); thisEl.find( target ).show().addClass( 'active' ); if ( jQuery( '.fusion-builder-modal-top-container' ).find( '.fusion-elements-filter' ).length ) { setTimeout( function() { jQuery( '.fusion-builder-modal-top-container' ).find( '.fusion-elements-filter' ).focus(); }, 50 ); } FusionEvents.trigger( 'fusion-tab-changed' ); if ( 0 < thisEl.closest( '.fusion-sidebar-section' ).length ) { jQuery( target ).closest( '.fusion-tabs' ).scrollTop( 0 ); } else { thisEl.closest( '.ui-dialog-content' ).scrollTop( 0 ); } } ); thisEl.find( '.fusion-tabs-menu > li:first-child a' ).trigger( 'click' ); }, /** * Adds classes necessary to prevent iframe from catching pointer events. * * @since 2.0.0 * @return {void} */ addResizingClasses: function() { jQuery( 'body' ).addClass( 'fusion-preview-block fusion-dialog-resizing' ); }, /** * Removes classes necessary to prevent iframe from catching pointer events. * * @since 2.0.0 * @return {void} */ removeResizingClasses: function() { jQuery( 'body' ).removeClass( 'fusion-preview-block fusion-dialog-resizing' ); }, /** * Adds modal hover event necessary to prevent iframe from catching pointer events. * * @since 2.0.0 * @return {void} */ addResizingHoverEvent: function() { jQuery( '.ui-dialog .ui-resizable-handle' ).hover( function() { jQuery( 'body' ).addClass( 'fusion-preview-block' ); }, function() { if ( ! jQuery( 'body' ).hasClass( 'fusion-dialog-resizing' ) ) { jQuery( 'body' ).removeClass( 'fusion-preview-block' ); } } ); } } ); }( jQuery ) ); ;var FusionPageBuilder = FusionPageBuilder || {}; ( function() { FusionPageBuilder.Validate = Backbone.Model.extend( { /** * Validates dimension css values. * * @param {string} value - The value we want to validate. * @return {boolean} */ cssValue: function( value, allowNumeric ) { var validUnits = [ 'rem', 'em', 'ex', '%', 'px', 'cm', 'mm', 'in', 'pt', 'pc', 'ch', 'vh', 'vw', 'vmin', 'vmax' ], partsValidity = true, self = this, numericValue, unit, parts; // 0 is always a valid value, and we can't check calc() values effectively. if ( '0' === value || '' === value || ( 0 <= value.indexOf( 'calc(' ) && 0 <= value.indexOf( ')' ) ) ) { return true; } if ( 0 <= value.indexOf( ' ' ) ) { parts = value.split( ' ' ); _.each( parts, function( part ) { if ( ! self.cssValue( part, false ) ) { partsValidity = false; } } ); return partsValidity; } // Get the numeric value. numericValue = parseFloat( value ); // Get the unit unit = value.replace( numericValue, '' ); if ( true === allowNumeric && ( '' === unit || ! unit ) ) { return true; } // Check the validity of the numeric value and units. if ( isNaN( numericValue ) || 0 > _.indexOf( validUnits, unit ) ) { return false; } return true; }, /** * Color validation. * * @since 2.0.0 * @param {string} value - The color-value we're validating. * @param {string} mode - The color-mode (rgba or hex). * @return {boolean} */ validateColor: function( value, mode ) { if ( '' === value ) { return true; } // Invalid value if not a string. if ( ! _.isString( value ) ) { return false; } if ( 0 === value.indexOf( '--' ) || ( /var\(\s*--/i ).test( value ) ) { return true; } if ( 'hex' === mode ) { return this.colorHEX( value ); } else if ( 'rgba' === mode ) { return this.colorRGBA( value ); } // Validate RGBA. if ( -1 !== value.indexOf( 'rgba' ) ) { return this.colorRGBA( value ); } // Validate HEX. return this.colorHEX( value ); }, /** * Validates a hex color. * * @since 2.0.0 * @param {string} value - The value we're validating. * @return {boolean} */ colorHEX: function( value ) { var hexValue; if ( '' === value ) { return true; } // If value does not include '#', then it's invalid hex. if ( -1 === value.indexOf( '#' ) ) { return false; } hexValue = value.replace( '#', '' ); // Check if hexadecimal. return ( ! isNaN( parseInt( hexValue, 16 ) ) ); }, /** * Validates an rgba color. * * @since 2.0.0 * @param {string} value - The value we're validating. * @return {boolean} */ colorRGBA: function( value ) { var valid = true, parts; if ( '' === value ) { return true; } if ( -1 === value.indexOf( 'rgba(' ) || -1 === value.indexOf( ')' ) ) { return false; } parts = value.replace( 'rgba(', '' ).replace( ')', '' ).split( ',' ); if ( 4 !== parts.length ) { return false; } _.each( parts, function( part ) { var num = parseFloat( part, 10 ); if ( isNaN( num ) ) { valid = false; return false; } if ( 0 > num || 255 < num ) { valid = false; return false; } } ); return valid; }, /** * Adds and removes messages in the control. * * @param {string} id - The setting ID. * @param {string} message - The message to add. * @return {void} */ message: function( action, id, input, message ) { var element = jQuery( '.fusion-builder-option[data-option-id="' + id + '"]' ), messageClass = 'fusion-builder-validation', messageWrapper = '
'; // No reason to proceed if we can't find the element. if ( ! element.length ) { return; } if ( 'add' === action ) { // If the message wrapper doesn't exist, add it. if ( ! element.find( '.' + messageClass ).length ) { element.find( '.option-details' ).append( messageWrapper ); jQuery( input ).addClass( 'error' ); } // Add the message to the validation error wrapper. element.find( '.' + messageClass ).html( message ); } else if ( 'remove' === action ) { element.find( '.' + messageClass ).remove(); jQuery( input ).removeClass( 'error' ); } } } ); }( jQuery ) ); ;/* global FusionApp, fusionSanitize */ /* eslint no-unused-vars: 0 */ var FusionPageBuilder = FusionPageBuilder || {}; ( function() { FusionPageBuilder.Callback = Backbone.Model.extend( { fusionOption: function( value, args ) { var poValue = false; if ( 'object' === typeof args && 'string' === typeof args.id && 'string' === typeof args.type ) { if ( 'PO' === args.type && '' !== value ) { return value; } else if ( 'PO' === args.type ) { return FusionApp.settings[ args.id ]; } poValue = 'undefined' !== typeof FusionApp.data.postMeta._fusion && 'undefined' !== typeof FusionApp.data.postMeta._fusion[ args.id ] ? FusionApp.data.postMeta._fusion[ args.id ] : false; if ( poValue && '' !== poValue ) { return poValue; } return value; } return value; }, awbHeaderBreakpoint: function( value, args ) { var $contents = jQuery( '#fb-preview' ).contents(), breakpointVal = 1; if ( 'medium' === value || 'small' === value ) { breakpointVal = fusionSanitize.getOption( 'visibility_' + value ); } else if ( 'custom' === value ) { breakpointVal = 'undefined' !== typeof FusionApp.data.postMeta._fusion.header_custom_breakpoint ? FusionApp.data.postMeta._fusion.header_custom_breakpoint : 800; } $contents.find( '#awb-side-header-css' ).attr( 'media', 'only screen and (min-width: ' + parseInt( breakpointVal, 10 ) + 'px)' ); }, awbCustomHeaderBreakpoint: function( value, args ) { if ( 'undefined' !== typeof FusionApp.data.postMeta._fusion.header_breakpoint && 'custom' === FusionApp.data.postMeta._fusion.header_breakpoint ) { jQuery( '#fb-preview' ).contents().find( '#awb-side-header-css' ).attr( 'media', 'only screen and (min-width: ' + parseInt( value, 10 ) + 'px)' ); } }, awbHeaderPosition: function( value, args ) { var $body = jQuery( '#fb-preview' ).contents().find( 'body' ); if ( 'left' === value || 'right' === value ) { $body.removeClass( 'awbh-left awbh-right' ).addClass( 'side-header awbh-' + value ); if ( 'undefined' === typeof FusionApp.data.postMeta._fusion.header_breakpoint ) { FusionApp.data.postMeta._fusion.header_breakpoint = 'small'; } this.awbHeaderBreakpoint( FusionApp.data.postMeta._fusion.header_breakpoint, args ); } else { $body.removeClass( 'side-header awbh-left awbh-right' ); } } } ); }( jQuery ) ); ;/* global FusionApp, fusionAllElements, FusionEvents, FusionPageBuilderViewManager, FusionPageBuilderApp */ /* jshint -W024, -W098*/ var FusionPageBuilder = FusionPageBuilder || {}; ( function() { FusionPageBuilder.Dependencies = Backbone.Model.extend( { /** * Init. * * @since 2.0.0 * @return {void} */ initialize: function( options, view, $targetEl, repeaterFields, $parentEl ) { var self = this, currentOptions; this.$targetEl = 'undefined' !== typeof $targetEl ? $targetEl : view.$el; this.repeaterFields = 'undefined' !== typeof repeaterFields ? repeaterFields : false; this.$parentEl = 'undefined' !== typeof $parentEl ? $parentEl : this.$targetEl; this.type = view.type; this.elementView = view; // Dependency object key names switch ( this.type ) { case 'TO': self.dependencyKey = 'required'; self.settingKey = 'setting'; self.operatorKey = 'operator'; currentOptions = view.options; break; case 'PO': self.dependencyKey = 'dependency'; self.settingKey = 'field'; self.operatorKey = 'comparison'; currentOptions = view.options; break; case 'EO': self.dependencyKey = 'dependency'; self.settingKey = 'element'; self.operatorKey = 'operator'; currentOptions = options; break; } // Special case, we override view options from repeater. if ( self.repeaterFields ) { self.currentOptions = repeaterFields; } else { self.currentOptions = currentOptions; } self.parentValues = 'undefined' !== typeof view.parentValues ? view.parentValues : false; self.collectDependencies(); self.collectDependencyIds(); if ( 'undefined' !== typeof self.dependencyIds && self.dependencyIds.length ) { this.$targetEl.find( self.dependencyIds.substring( 2 ) ).on( 'change paste keyup fusion-change', function() { self.processDependencies( jQuery( this ).attr( 'id' ), view ); } ); // Listen for TO changes, refresh dependencies for new default. if ( 'object' === typeof self.dependencies ) { _.each( _.keys( self.dependencies ), function( param ) { FusionEvents.on( 'fusion-param-default-update-' + param, function() { self.processDependencies( param, view ); } ); } ); } } // Repeater dependency from parent view. if ( 'undefined' !== typeof self.parentDependencyIds && self.parentDependencyIds.length ) { this.$parentEl.on( 'change paste keyup fusion-change', self.parentDependencyIds.substring( 2 ), function() { self.processDependencies( jQuery( this ).attr( 'id' ), view, true ); } ); } self.dependenciesInitialCheck( view ); // Process page option default values. if ( 'PO' === view.type ) { self.processPoDefaults( view ); } else if ( 'EO' === view.type && 'undefined' !== typeof avadaPanelIFrame ) { self.processEoDefaults( view ); } }, /** * Initial option dependencies check. * * @since 2.0.0 */ dependenciesInitialCheck: function( view ) { var self = this; // Check for any option dependencies that are not on this tab. jQuery.each( _.keys( self.dependencies ), function( index, value ) { // jshint ignore: line if ( 'undefined' === typeof self.currentOptions[ value ] ) { self.processDependencies( value, view ); } } ); // Check each option on this tab. jQuery.each( self.currentOptions, function( index ) { self.processDependencies( index, view ); } ); }, buildPassedArray: function( dependencies, gutterCheck ) { var self = this, $passedArray = [], toName; // Check each dependency for that id. jQuery.each( dependencies, function( index, dependency ) { var setting = dependency[ self.settingKey ], operator = dependency[ self.operatorKey ], value = dependency.value, hasParent = -1 !== setting.indexOf( 'parent_' ), element = self.repeaterFields && hasParent ? self.$parentEl.find( '.fusion-builder-module-settings' ).data( 'element' ) : self.$targetEl.find( '.fusion-builder-module-settings' ).data( 'element' ), result = false, poFields, parentValue, containerView, containerParams; if ( self.repeaterFields && hasParent ) { parentValue = self.$parentEl.find( '#' + setting.replace( 'parent_', '' ) ).val(); } else if ( 0 < self.$targetEl.find( '#' + setting ).closest( '.dynamic-param-fields' ).length ) { // Check and exclude for dynamic data fields. parentValue = self.$targetEl.find( '#' + setting ).closest( '[data-dynamic]' ).siblings().find( '#' + setting ).val(); } else { const input = self.$targetEl.find( '#' + setting ); // Multi Select option. if ( input.is( 'div' ) && input.is( '.fusion-form-multiple-select' ) ) { parentValue = []; input.find( '.fusion-select-options input.fusion-select-option:checked' ).each( function() { parentValue.push( jQuery( this ).val() ); } ); } else { parentValue = input.val(); } } if ( 'undefined' === typeof parentValue ) { if ( 'TO' === self.type ) { parentValue = FusionApp.settings[ setting ]; } else if ( 'PO' === self.type ) { if ( 'undefined' !== typeof FusionApp.data.postMeta[ setting ] ) { parentValue = FusionApp.data.postMeta[ setting ]; } if ( 'undefined' !== typeof FusionApp.data.postMeta._fusion && 'undefined' !== typeof FusionApp.data.postMeta._fusion[ setting ] ) { parentValue = FusionApp.data.postMeta._fusion[ setting ]; } // Get the default value. if ( ( 'undefined' === typeof parentValue || '' === parentValue ) ) { poFields = FusionApp.sidebarView.getFlatPoObject(); if ( poFields[ setting ] && poFields[ setting ][ 'default' ] ) { parentValue = poFields[ setting ][ 'default' ]; } } } } // Use fake value if dynamic data is set. if ( '' === parentValue && ! hasParent && 'true' === self.$targetEl.find( '#' + setting ).closest( '.fusion-builder-option' ).attr( 'data-dynamic' ) ) { parentValue = 'using-dynamic-value'; } // Get from element defaults. if ( ( 'undefined' === typeof parentValue || '' === parentValue ) && 'EO' === self.type && 'undefined' !== typeof fusionAllElements[ element ] && 'undefined' !== typeof fusionAllElements[ element ].defaults && 'undefined' !== typeof fusionAllElements[ element ].defaults[ setting ] ) { parentValue = fusionAllElements[ element ].defaults[ setting ]; } // Hide 'flex / legacy' choice for containers when Header layout section is edited. if ( 'EO' === self.type && 'fusion_builder_container' === element && 'template_type' === setting && 'undefined' !== typeof FusionApp.data.template_category && 'header' === FusionApp.data.template_category ) { $passedArray.push( false ); return; } // Hide or show 'flex / legacy' choice based on TO setting. if ( 'EO' === self.type && 'fusion_builder_container' === element && 'template_type' === setting ) { $passedArray.push( 'undefined' !== typeof FusionApp.settings.container_legacy_support && '1' === FusionApp.settings.container_legacy_support ); return; } // Special check for parent container type. if ( 'EO' === self.type && 'fusion_builder_container' === setting && 'object' === typeof self.elementView ) { containerView = FusionPageBuilderViewManager.getView( self.elementView.model.get( 'parent' ) ); if ( 'object' === typeof containerView ) { containerView = FusionPageBuilderApp.getParentContainer( containerView.model.get( 'parent' ) ); if ( 'object' === typeof containerView ) { containerParams = 'object' === typeof containerView.values ? containerView.values : containerView.model.get( 'params' ); parentValue = containerParams[ ( 'undefined' !== typeof dependency.param ? dependency.param : 'type' ) ]; $passedArray.push( self.doesTestPass( parentValue, value, operator ) ); return; } } } if ( 'undefined' !== typeof parentValue ) { if ( 'TO' === self.type || 'FBE' === self.type ) { result = self.doesTestPass( parentValue, value, operator ); if ( false === gutterCheck ) { if ( self.$targetEl.find( '[data-option-id=' + setting + ']' ).is( ':hidden' ) && ! self.$targetEl.find( '[data-option-id=' + setting + ']' ).closest( '.repeater-fields' ).length ) { result = false; } } $passedArray.push( Number( result ) ); } else { // Page Options if ( '' === parentValue || 'default' === parentValue ) { if ( 'undefined' !== typeof FusionApp.settingsPoTo[ setting ] ) { // Get TO name toName = FusionApp.settingsPoTo[ setting ]; // Get TO value parentValue = FusionApp.settings[ toName ]; // Fix value names ( TO to PO ) parentValue = self.fixPoToValue( parentValue ); } } if ( 'EO' === self.type && 'undefined' !== typeof self.attributes[ setting ] && 'range' !== self.attributes[ setting ].type ) { // Fix value names ( TO to EO ) parentValue = self.fixEoToValue( parentValue ); } $passedArray.push( self.doesTestPass( parentValue, value, operator ) ); } } else { // Check parent element values. For parent to child dependencies. if ( self.parentValues ) { if ( 'parent_' === setting.substring( 0, 7 ) ) { if ( 'object' === typeof self.parentValues && self.parentValues[ setting.replace( dependency.element.substring( 0, 7 ), '' ) ] ) { parentValue = self.parentValues[ setting.replace( dependency.element.substring( 0, 7 ), '' ) ]; } else { parentValue = ''; } } } // Check for current post type dependency. if ( 'EO' === self.type && '_post_type_edited' === setting && 'object' === typeof self.elementView ) { parentValue = FusionApp.data.postDetails.post_type; } $passedArray.push( self.doesTestPass( parentValue, value, operator ) ); } } ); return $passedArray; }, /** * Collect and return all dependencies. * * @since 2.0.0 * @return {void} */ collectDependencies: function() { var self = this, dependency, optionName, setting, dependencies = []; jQuery.each( self.currentOptions, function( index, value ) { dependency = value[ self.dependencyKey ]; // Dependency found if ( ! _.isUndefined( dependency ) ) { optionName = index; // Check each dependency for this option jQuery.each( dependency, function( i, opt ) { setting = opt[ self.settingKey ]; // If option has dependency add to check array. if ( _.isUndefined( dependencies[ setting ] ) ) { dependencies[ setting ] = [ { option: optionName, or: value.or } ]; } else { dependencies[ setting ].push( { option: optionName, or: value.or } ); } } ); } } ); self.dependencies = dependencies; }, /** * Collect IDs of options with dependencies. * * @since 2.0.0 * @return string */ collectDependencyIds: function() { var self = this, dependency, setting, dependencyIds = '', parentDependencyIds = ''; jQuery.each( self.currentOptions, function( index, value ) { dependency = value[ self.dependencyKey ]; // Dependency found if ( ! _.isUndefined( dependency ) ) { // Check each dependency for this option jQuery.each( dependency, function( i, opt ) { setting = opt[ self.settingKey ]; // Create IDs of fields to check for. ( Listeners ) if ( 'parent_' === setting.substring( 0, 7 ) && 0 > parentDependencyIds.indexOf( '#' + setting.replace( 'parent_', '' ) ) ) { parentDependencyIds += ', #' + setting.replace( 'parent_', '' ); } else if ( 0 > dependencyIds.indexOf( '#' + setting ) ) { dependencyIds += ', #' + setting; } } ); } } ); self.dependencyIds = dependencyIds; // Repeater, set parent dependency Ids. if ( '' !== parentDependencyIds && self.repeaterFields ) { self.parentDependencyIds = parentDependencyIds; } }, /** * Hide or show the control for an option. * * @since 2.0.0 * @param {boolean} [show] Whether we want to hide or show the option. * @param {string} [optionName] The option-name. * @return {void} */ hideShowOption: function( show, optionName ) { if ( show ) { this.$targetEl.find( '[data-option-id="' + optionName + '"]' ).fadeIn( 300 ); this.$targetEl.find( '[data-option-id="' + optionName + '"]' ).removeClass( 'dependency-hide' ); } else { this.$targetEl.find( '[data-option-id="' + optionName + '"]' ).hide(); this.$targetEl.find( '[data-option-id="' + optionName + '"]' ).addClass( 'dependency-hide' ); } }, /** * Check option for fusion-or-gutter. * * @since 2.0.0 * @param {Object} option * @return {Object} */ toGutterCheck: function( option ) { var singleOrGutter, gutterSequence, gutterCheck = false, gutter = {}; singleOrGutter = ( ! _.isUndefined( option[ 'class' ] ) && 'fusion-or-gutter' === option[ 'class' ] ) ? option[ 'class' ] : false; if ( ! singleOrGutter ) { gutterSequence = ( ! _.isUndefined( option[ 'class' ] ) && 'fusion-or-gutter' !== option[ 'class' ] ) ? option[ 'class' ].replace( 'fusion-gutter-', '' ).split( '-' ) : false; } if ( singleOrGutter || gutterSequence ) { gutterCheck = true; } gutter = { single: singleOrGutter, sequence: gutterSequence, check: gutterCheck }; return gutter; }, /** * Process dependencies for an option. * * @since 2.0.0 * @param {string} [currentId] The setting-ID. * @return {void} */ processDependencies: function( currentId, view, fromParent ) { var self = this, gutter = {}, childGutter = {}, show = false, optionName, passedArray, dependentOn, childOptionName, childDependencies, childPassedArray; if ( 'function' === typeof view.beforeProcessDependencies ) { view.beforeProcessDependencies(); } // If fromParent is set we need to check for ID with parent_ added. if ( 'undefined' !== typeof fromParent && fromParent ) { currentId = 'parent_' + currentId; } // Loop through each option id that is dependent on this option. jQuery.each( self.dependencies[ currentId ], function( index, value ) { show = false; optionName = value.option; dependentOn = self.currentOptions[ optionName ][ self.dependencyKey ]; passedArray = []; gutter = {}; if ( 'TO' === self.type || 'FBE' === self.type ) { // Check for fusion-or-gutter. gutter = self.toGutterCheck( self.currentOptions[ optionName ] ); // Check each dependent option for that id. passedArray = self.buildPassedArray( dependentOn, gutter.check ); // Show / Hide option. if ( gutter.sequence || gutter.single ) { show = self.checkGutterOptionVisibility( gutter.sequence, passedArray, gutter.single ); } else { show = self.checkTOVisibility( passedArray ); } self.hideShowOption( show, optionName, self.$targetEl ); // Process children jQuery.each( self.dependencies[ optionName ], function( childIndex, childValue ) { childOptionName = childValue.option; childDependencies = self.currentOptions[ childOptionName ][ self.dependencyKey ]; show = false; childGutter = {}; childPassedArray = []; // Check for fusion-or-gutter. childGutter = self.toGutterCheck( self.currentOptions[ childOptionName ] ); // Check each dependent option for that id. childPassedArray = self.buildPassedArray( childDependencies, childGutter.check ); // Show / Hide option. if ( childGutter.sequence || childGutter.single ) { show = self.checkGutterOptionVisibility( childGutter.sequence, childPassedArray, childGutter.single ); } else { show = self.checkTOVisibility( childPassedArray ); } // Show / Hide option self.hideShowOption( show, childOptionName ); } ); } else if ( 'PO' === self.type || 'EO' === self.type ) { // Check each dependent option for that id. passedArray = self.buildPassedArray( dependentOn, gutter.check ); // Show / Hide option. show = self.checkOptionVisibility( passedArray, value ); self.hideShowOption( show, optionName ); } } ); }, /** * Compares option value with dependency value to determine if it passes or not. * * @since 2.0.0 * @param {mixed} [parentValue] The first value in the check. * @param {mixed} [checkValue] The 2nd value in the check. * @param {string} [operation] The check we want to perform. * @return {boolean} */ doesTestPass: function( parentValue, checkValue, operation ) { var show = false, arr, media; // If dependencies are disabled, always show the option. if ( 'undefined' !== FusionApp.settings.dependencies_status && 0 === parseInt( FusionApp.settings.dependencies_status ) ) { return true; } switch ( operation ) { case '=': case '==': case 'equals': if ( Array.isArray( parentValue ) ) { jQuery( parentValue[ 0 ] ).each( function( idx, val ) { if ( Array.isArray( checkValue ) ) { jQuery( checkValue ).each( function( i, v ) { if ( val == v ) { // jshint ignore: line show = true; return true; } } ); } else if ( val == checkValue ) { // jshint ignore: line show = true; return true; } } ); } else if ( Array.isArray( checkValue ) ) { jQuery( checkValue ).each( function( i, v ) { if ( parentValue == v ) { // jshint ignore: line show = true; } } ); } else if ( parentValue == checkValue ) { // jshint ignore: line show = true; } break; case '!=': case 'not': if ( Array.isArray( parentValue ) ) { jQuery( parentValue ).each( function( idx, val ) { if ( Array.isArray( checkValue ) ) { jQuery( checkValue ).each( function( i, v ) { if ( val != v ) { // jshint ignore: line show = true; return true; } } ); } else if ( val != checkValue ) { // jshint ignore: line show = true; return true; } } ); } else if ( Array.isArray( checkValue ) ) { jQuery( checkValue ).each( function( i, v ) { if ( parentValue != v ) { // jshint ignore: line show = true; } } ); } else if ( parentValue != checkValue ) { // jshint ignore: line show = true; } break; case '>': case 'greater': case 'is_larger': if ( parseFloat( parentValue ) > parseFloat( checkValue ) ) { show = true; } break; case '>=': case 'greater_equal': case 'is_larger_equal': if ( parseFloat( parentValue ) >= parseFloat( checkValue ) ) { show = true; } break; case '<': case 'less': case 'is_smaller': if ( parseFloat( parentValue ) < parseFloat( checkValue ) ) { show = true; } break; case '<=': case 'less_equal': case 'is_smaller_equal': if ( parseFloat( parentValue ) <= parseFloat( checkValue ) ) { show = true; } break; case 'contains': if ( jQuery.isPlainObject( parentValue ) ) { checkValue = Object.keys( checkValue ).map( function( key ) { return [ key, checkValue[ key ] ]; } ); parentValue = arr; } if ( jQuery.isPlainObject( checkValue ) ) { arr = Object.keys( checkValue ).map( function( key ) { return checkValue[ key ]; } ); checkValue = arr; } if ( Array.isArray( checkValue ) ) { jQuery( checkValue ).each( function( idx, val ) { var breakMe = false, toFind = val[ 0 ], findVal = val[ 1 ]; jQuery( parentValue ).each( function( i, v ) { var toMatch = v[ 0 ], matchVal = v[ 1 ]; if ( toFind === toMatch ) { if ( findVal == matchVal ) { // jshint ignore: line show = true; breakMe = true; return false; } } } ); if ( true === breakMe ) { return false; } } ); } else if ( -1 !== parentValue.toString().indexOf( checkValue ) ) { show = true; } break; case 'doesnt_contain': case 'not_contain': if ( jQuery.isPlainObject( parentValue ) ) { arr = Object.keys( parentValue ).map( function( key ) { return parentValue[ key ]; } ); parentValue = arr; } if ( jQuery.isPlainObject( checkValue ) ) { arr = Object.keys( checkValue ).map( function( key ) { return checkValue[ key ]; } ); checkValue = arr; } if ( Array.isArray( checkValue ) ) { jQuery( checkValue ).each( function( idx, val ) { if ( -1 === parentValue.toString().indexOf( val ) ) { show = true; } } ); } else if ( -1 === parentValue.toString().indexOf( checkValue ) ) { show = true; } break; case 'is_empty_or': if ( '' === parentValue || parentValue == checkValue ) { // jshint ignore: line show = true; } break; case 'not_empty_and': if ( '' !== parentValue && parentValue != checkValue ) { // jshint ignore: line show = true; } break; case 'is_empty': case 'empty': case '!isset': if ( ! parentValue || '' === parentValue || null === parentValue ) { show = true; } break; case 'not_empty': case '!empty': case 'isset': if ( parentValue && '' !== parentValue && null !== parentValue ) { show = true; } break; case 'is_media': if ( parentValue ) { media = 'string' === typeof parentValue ? JSON.parse( parentValue ) : parentValue; if ( media && media.url ) { show = true; } } break; case 'is_transparent': if ( parentValue && 0 === jQuery.AWB_Color( parentValue ).alpha() ) { show = true; } break; case 'is_not_transparent': if ( parentValue && 0 !== jQuery.AWB_Color( parentValue ).alpha() ) { show = true; } break; } return show; }, /** * Check page options & element options visibility. * * @since 2.0.0 * @return bool */ checkOptionVisibility: function( passedArray, value ) { var visible = false; if ( -1 === jQuery.inArray( false, passedArray ) && _.isUndefined( value.or ) ) { visible = true; } else if ( -1 !== jQuery.inArray( true, passedArray ) && ! _.isUndefined( value.or ) ) { visible = true; } return visible; }, /** * Check Global Option visibility. * * @since 2.0.0 * @return bool */ checkTOVisibility: function( passedArray ) { var visible = false; if ( -1 === jQuery.inArray( 0, passedArray ) ) { visible = true; } return visible; }, /** * Check option visibility for fusion-or-gutter options. * * @since 2.0.0 * @return bool */ checkGutterOptionVisibility: function( gutterSequence, passedArray, singleOrGutter ) { var overallDependencies = [], total = 0, show = false, i; if ( singleOrGutter ) { overallDependencies = passedArray; } else if ( 0 < gutterSequence.length ) { for ( i = 0; i < passedArray.length; i++ ) { if ( 0 === i ) { overallDependencies.push( passedArray[ i ] ); } else if ( 'and' === gutterSequence[ i - 1 ] ) { overallDependencies[ overallDependencies.length - 1 ] = overallDependencies[ overallDependencies.length - 1 ] * passedArray[ i ]; } else { overallDependencies.push( passedArray[ i ] ); } } } for ( i = 0; i < overallDependencies.length; i++ ) { total += overallDependencies[ i ]; } if ( 1 <= total ) { show = true; } else { show = false; } show = Boolean( show ); return show; }, /** * Convert option values. * * @since 2.0.0 * @return string */ fixPoToValue: function( value ) { switch ( value ) { case 'hide': case '0': value = 'no'; break; case 'show': case '1': value = 'yes'; break; } return value; }, /** * Convert option values. * * @since 3.7.0 * @return string */ fixEoToValue: function( value ) { switch ( value ) { case '0': value = 'no'; break; case '1': value = 'yes'; break; } return value; }, /** * Process element option default values. * * @since 2.0.0 * @return {void} */ processEoDefaults: function( view ) { var elementType = view.model.get( 'element_type' ), elementDefaults = FusionApp.elementDefaults[ elementType ], toValue; if ( 'object' === typeof elementDefaults && 'object' === typeof elementDefaults.settings_to_params ) { _.each( elementDefaults.settings_to_params, function( eo, to ) { var option, type = '', subset = ''; toValue = FusionApp.settings[ to ]; // Looking for sub value, get parent only. if ( -1 !== to.indexOf( '[' ) ) { toValue = to.split( '[' )[ 0 ]; subset = to.split( '[' )[ 1 ].replace( ']', '' ); toValue = FusionApp.settings[ toValue ]; } // Get param if its an object. if ( 'object' === typeof eo ) { eo = eo.param; } option = view.$el.find( '#' + eo ).closest( '.fusion-builder-option' ); if ( option.length ) { type = jQuery( option ).attr( 'class' ).split( ' ' ).pop(); } if ( ! jQuery( option ).hasClass( 'fusion-builder-option range' ) ) { toValue = FusionApp.sidebarView.fixToValueName( to, toValue, type, subset ); view.$el.find( '.description [data-fusion-option="' + to + '"]' ).html( toValue ); } } ); } }, /** * Process page option default values. * * @since 2.0.0 * @return {void} */ processPoDefaults: function( view ) { var thisEl = view.$el, toValue, poValue, subset, type = '', option; _.each( FusionApp.settingsPoTo, function( to, po ) { toValue = FusionApp.settings[ to ]; if ( ! _.isUndefined( toValue ) ) { option = thisEl.find( '[data-option-id="' + po + '"]' ); poValue = option.val(); if ( option.length ) { type = jQuery( option ).attr( 'class' ).replace( /\s+$/, '' ).split( ' ' ).pop(); } subset = jQuery( option ).data( 'subset' ); if ( 'default' !== poValue ) { toValue = FusionApp.sidebarView.fixToValueName( to, toValue, type, subset ); option.find( '.description a' ).html( toValue ); } } } ); } } ); }( jQuery ) ); ;var FusionPageBuilder = FusionPageBuilder || {}; ( function() { FusionPageBuilder.ViewManager = Backbone.Model.extend( { defaults: { elementCount: 0, views: {} }, getViews: function() { return this.get( 'views' ); }, getView: function( cid ) { return this.get( 'views' )[ cid ]; }, getChildViews: function( parentID ) { var views = this.get( 'views' ), childViews = {}; _.each( views, function( view, key ) { if ( parentID === view.model.attributes.parent ) { childViews[ key ] = view; } } ); return childViews; }, generateCid: function() { var elementCount = this.get( 'elementCount' ) + 1; this.set( { elementCount: elementCount } ); return elementCount; }, addView: function( cid, view ) { var views = this.get( 'views' ); views[ cid ] = view; this.set( { views: views } ); }, removeView: function( cid ) { var views = this.get( 'views' ), updatedViews = {}; _.each( views, function( value, key ) { if ( key != cid ) { // jshint ignore: line updatedViews[ key ] = value; } } ); this.set( { views: updatedViews } ); }, removeViews: function() { var updatedViews = {}; this.set( { views: updatedViews } ); }, countElementsByType: function( elementType ) { var views = this.get( 'views' ), num = 0; _.each( views, function( view ) { if ( view.model.attributes.element_type === elementType ) { num++; } } ); return num; }, clear: function() { var views = this.get( 'views' ); _.each( views, function( view ) { view.unbind(); view.remove(); delete view.$el; delete view.el; } ); this.set( 'elementCount', 0 ); this.set( 'views', {} ); } } ); }( jQuery ) ); ;var FusionPageBuilder = FusionPageBuilder || {}; FusionPageBuilder.fusionActiveStates = { /** * Preview toggle. * * @since 2.0.0 * @param {Object} event - The event. * @param {Object|string} $target - The target element. * @return {void} */ previewToggle: function( event, $target ) { var self = this, type, selector, toggle, append, delay, data, persistent = true; $target = 'undefined' === typeof $target ? jQuery( event.currentTarget ) : $target; type = $target.data( 'type' ); selector = $target.data( 'selector' ); toggle = 'undefined' !== typeof $target.data( 'toggle' ) ? $target.data( 'toggle' ) : ''; append = 'undefined' !== typeof $target.data( 'append' ) ? $target.data( 'append' ) : false; delay = -1 !== selector.indexOf( '$el' ) ? 300 : 0, data = { type: type, selector: selector, toggle: toggle, append: append }; if ( event ) { event.preventDefault(); } // If it is animations we need to remove active state since it is not persistent. if ( 'animation' === type && 'fusion_content_boxes' !== this.model.get( 'element_type' ) ) { persistent = false; } // If target is already active we active, else we deactivate. if ( ! $target.hasClass( 'active' ) ) { // Persistent state, set it active. if ( persistent ) { this.activeStates[ selector + '-' + type + '-' + toggle ] = data; } // If we are targetting the element itself we need a timeout. setTimeout( function() { self.triggerActiveState( data ); }, delay ); } else { // We want to remove it if ( 'undefined' !== typeof this.activeStates[ selector + '-' + type + '-' + toggle ] ) { this.activeStates[ selector + '-' + type + '-' + toggle ] = false; } // If we are targetting the element itself we need a timeout. setTimeout( function() { self.triggerRemoveState( data ); }, delay ); } // Toggle all at same time that are the same. if ( persistent ) { this.$el.find( '[data-type="' + type + '"][data-selector="' + selector + '"][data-toggle="' + toggle + '"]' ).toggleClass( 'active' ); } }, /** * Trigger the actual state change. * * @since 2.0.0 * @param {Object} data - Data for state change. * @return {void} */ triggerActiveState: function( data ) { var self = this, selectors, $targetEl = this.$targetEl && this.$targetEl.length ? this.$targetEl : jQuery( '#fb-preview' ).contents().find( '.fusion-builder-live' ), $target, animationDuration, animationDelay; if ( 'string' === typeof data.selector && -1 !== data.selector.indexOf( '$el' ) ) { $target = $targetEl; } else if ( $targetEl.hasClass( 'fusion-builder-column' ) || $targetEl.hasClass( 'fusion-builder-container' ) ) { $target = $targetEl.find( data.selector ); } else if ( $targetEl.hasClass( 'fusion-builder-live-element' ) ) { $target = $targetEl.find( '.fusion-builder-element-content ' + data.selector ); } else if ( $targetEl.hasClass( 'fusion-builder-live-child-element' ) ) { $target = $targetEl.find( '.fusion-builder-child-element-content ' + data.selector ); } if ( 'PO' === this.model.get( 'type' ) ) { $target = $targetEl.find( data.selector ); } if ( 'undefined' === typeof $target || ! $target.length ) { return; } if ( 'animation' === data.type ) { if ( 'fusion_content_boxes' === this.model.get( 'element_type' ) ) { this.contentBoxAnimations( data ); return; } if ( ( 'fusion_post_cards' === this.model.get( 'element_type' ) || 'fusion_tb_post_card_archives' === this.model.get( 'element_type' ) ) && $target.hasClass( 'fusion-delayed-animation' ) ) { this.postCardsAnimations( data, $target ); return; } $target.each( function() { var $singleTarget = jQuery( this ); data.toggle = $singleTarget.attr( 'data-animationtype' ); animationDuration = $singleTarget.attr( 'data-animationduration' ); animationDelay = $singleTarget.attr( 'data-animationdelay' ); $singleTarget.css( '-moz-animation-duration', animationDuration + 's' ); $singleTarget.css( '-webkit-animation-duration', animationDuration + 's' ); $singleTarget.css( '-o-animation-duration', animationDuration + 's' ); $singleTarget.css( 'animation-duration', animationDuration + 's' ); $singleTarget.css( 'animation-delay', animationDelay + 's' ); $singleTarget.removeClass( _.fusionGetAnimationTypes().join( ' ' ) ); setTimeout( function() { $singleTarget.addClass( data.toggle ); }, 50 ); } ); return; } // Set the state. if ( data.append ) { selectors = data.selector.split( ',' ); _.each( selectors, function( selector ) { $target = $targetEl.find( selector ); if ( $target.length ) { $target.addClass( selector.replace( '.', '' ) + data.toggle ); } } ); } else { $target.addClass( data.toggle ); } // Add one time listener in case use interacts with target. $target.one( 'mouseleave', function() { self.$el.find( '[data-type="' + data.type + '"][data-selector="' + data.selector + '"][data-toggle="' + data.toggle + '"]' ).removeClass( 'active' ); self.activeStates[ data.selector + '-' + data.type + '-' + data.toggle ] = false; self.triggerRemoveState( data ); } ); }, /** * Removes already active state. * * @since 2.0.0 * @param {Object} data - Data for state change. * @return {void} */ triggerRemoveState: function( data ) { var selectors, $targetEl = this.$targetEl && this.$targetEl.length ? this.$targetEl : jQuery( '#fb-preview' ).contents().find( '.fusion-builder-live' ), $target; if ( 'string' === typeof data.selector && -1 !== data.selector.indexOf( '$el' ) ) { $target = $targetEl; } else if ( $targetEl.hasClass( 'fusion-builder-column' ) ) { $target = $targetEl.find( data.selector ); } else if ( $targetEl.hasClass( 'fusion-builder-live-element' ) ) { $target = $targetEl.find( '.fusion-builder-element-content ' + data.selector ); } else if ( $targetEl.hasClass( 'fusion-builder-live-child-element' ) ) { $target = $targetEl.find( '.fusion-builder-child-element-content ' + data.selector ); } if ( 'PO' === this.model.get( 'type' ) ) { $target = $targetEl.find( data.selector ); } if ( 'undefined' === typeof $target || ! $target.length ) { return; } if ( 'animation' === data.type ) { $target.each( function() { var $singleTarget = jQuery( this ); data.toggle = $singleTarget.attr( 'data-animationtype' ); $singleTarget.removeClass( data.toggle ); } ); return; } // Set the state. if ( data.append ) { selectors = data.selector.split( ',' ); _.each( selectors, function( selector ) { $target.removeClass( selector.replace( '.', '' ) + data.toggle ); } ); } else { $target.removeClass( data.toggle ); } }, /** * Adds a temporary state. * * @since 2.0.0 * @param {Object} $option - Option node. * @return {void} */ triggerTemporaryState: function( $option ) { if ( $option.find( '.option-preview-toggle' ).length && ! $option.find( '.option-preview-toggle' ).hasClass( 'active' ) ) { this.previewToggle( false, $option.find( '.option-preview-toggle' ) ); this._tempStateRemove( $option ); } }, /** * Triggers removal of state. * * @since 2.0.0 * @param {Object} $option - Option node. * @return {void} */ tempStateRemove: function( $option ) { if ( $option.find( '.option-preview-toggle' ).length && $option.find( '.option-preview-toggle' ).hasClass( 'active' ) ) { this.previewToggle( false, $option.find( '.option-preview-toggle' ) ); } }, /** * Make sure any active states are set again after render. * * @since 2.0.0 * @return {void} */ triggerActiveStates: function() { var self = this; _.each( this.activeStates, function( state ) { self.triggerActiveState( state ); } ); }, /** * Make sure all states are removed on close. * * @since 2.0.0 * @return {void} */ removeActiveStates: function() { var self = this; _.each( this.activeStates, function( state ) { self.triggerRemoveState( state ); } ); }, contentBoxAnimations: function() { var $delay = 0, $targetEl = this.$targetEl && this.$targetEl.length ? this.$targetEl : jQuery( '#fb-preview' ).contents().find( '.fusion-builder-live' ); $targetEl.find( '.content-box-column' ).each( function() { var $element = jQuery( this ), $target = $element.find( '.fusion-animated' ), $animationType, $animationDuration; setTimeout( function() { $target.css( 'visibility', 'visible' ); // This code is executed for each appeared element $animationType = $target.data( 'animationtype' ); $animationDuration = $target.data( 'animationduration' ); $target.addClass( $animationType ); if ( $animationDuration ) { $target.css( '-moz-animation-duration', $animationDuration + 's' ); $target.css( '-webkit-animation-duration', $animationDuration + 's' ); $target.css( '-o-animation-duration', $animationDuration + 's' ); $target.css( 'animation-duration', $animationDuration + 's' ); } if ( $element.closest( '.fusion-content-boxes' ).hasClass( 'content-boxes-timeline-horizontal' ) || $element.closest( '.fusion-content-boxes' ).hasClass( 'content-boxes-timeline-vertical' ) ) { $element.addClass( 'fusion-appear' ); } setTimeout( function() { $target.removeClass( $animationType ); }, $animationDuration * 1000 ); }, $delay ); $delay += parseInt( jQuery( this ).closest( '.fusion-content-boxes' ).attr( 'data-animation-delay' ), 10 ); } ); }, postCardsAnimations: function( data, $element ) { var $postCards = $element, delay = 0, animationType = $postCards.attr( 'data-animationtype' ), animationDuration = $postCards.attr( 'data-animationduration' ), animationDelay = parseInt( $postCards.attr( 'data-animation-delay' ) * 1000, 10 ); $postCards.find( '.fusion-grid-column' ).css( 'visibility', 'hidden' ).each( function() { var $target = jQuery( this ); setTimeout( function() { $target.css( 'visibility', 'visible' ); $target.addClass( animationType ); if ( animationDuration ) { $target.css( '-moz-animation-duration', animationDuration + 's' ); $target.css( '-webkit-animation-duration', animationDuration + 's' ); $target.css( '-o-animation-duration', animationDuration + 's' ); $target.css( 'animation-duration', animationDuration + 's' ); } setTimeout( function() { $target.removeClass( animationType ); }, animationDuration * 1000 ); }, delay ); delay += animationDelay; } ); } }; ;/* global FusionEvents, FusionApp */ var FusionPageBuilder = FusionPageBuilder || {}; ( function() { FusionPageBuilder.Hotkeys = Backbone.Model.extend( { /** * Init. * * @since 2.0.0 * @return {void} */ initialize: function() { var self = this; jQuery( 'body' ).on( 'keydown', function( event ) { if ( self.isValidTarget( event ) ) { self.checkKey( event ); } } ); }, /** * Reattach listeners for iframe. * * @since 2.0.0 * @return {void} */ attachListener: function() { var self = this; jQuery( document.getElementById( 'fb-preview' ).contentWindow.document ).off( 'keydown' ); jQuery( document.getElementById( 'fb-preview' ).contentWindow.document ).on( 'keydown', function( event ) { if ( self.isValidTarget( event ) ) { self.checkKey( event ); } } ); }, /** * Check combination of keys pressed. * * @since 2.0.0 * @param {Object} [event] Contains event data. * @return {void} */ checkKey: function( event ) { // If disabled. if ( ( 'undefined' !== typeof FusionApp && 'undefined' !== typeof FusionApp.preferencesData && 'undefined' !== typeof FusionApp.preferencesData.keyboard_shortcuts && 'off' === FusionApp.preferencesData.keyboard_shortcuts ) ) { return; } if ( event.ctrlKey || event.metaKey || event.shiftKey ) { // If only Shift key. if ( this.isShiftKey( event ) && ! this.isMetaKey( event ) ) { switch ( event.keyCode ) { // Key Shift + P for Preview. case 80: event.preventDefault(); jQuery( '.fusion-toolbar-nav li.preview a' ).trigger( 'click' ); break; // Key Shift + T to toggle sidebar. case 84: if ( 'undefined' !== typeof FusionApp.sidebarView ) { event.preventDefault(); FusionApp.sidebarView.togglePanel(); } break; } } // If only meta key. if ( this.isMetaKey( event ) && ! this.isShiftKey( event ) ) { switch ( event.keyCode ) { // Return key to close modal. case 13: if ( 0 < jQuery( '.fusion-builder-dialog .ui-dialog-buttonset .ui-button' ).length ) { jQuery( '.fusion-builder-dialog .ui-dialog-buttonset .ui-button' ).trigger( 'click' ); } else { jQuery( '.fusion-builder-dialog .ui-button.ui-dialog-titlebar-close' ).trigger( 'click' ); } break; // Key 1 for large view. case 49: event.preventDefault(); jQuery( '.fusion-builder-preview-desktop' ).trigger( 'click' ); break; // Key 2 for mobile view. case 50: event.preventDefault(); jQuery( '.fusion-builder-preview-mobile.portrait' ).trigger( 'click' ); break; // Key 4 for tablet view. case 51: event.preventDefault(); jQuery( '.fusion-builder-preview-tablet.portrait' ).trigger( 'click' ); break; // Key D to clear layout. case 68: event.preventDefault(); jQuery( '.fusion-builder-clear-layout' ).trigger( 'click' ); break; // Key Q to exit the builder. case 81: event.preventDefault(); jQuery( '.fusion-exit-builder-list .exit-to-back-end a' ).trigger( 'click' ); break; // Key S to save, click rather than save directly so that animations occur. case 83: event.preventDefault(); if ( ! jQuery( '.fusion-builder-save-page' ).data( 'disabled' ) ) { jQuery( '.fusion-builder-save-page' ).trigger( 'click' ); } break; // Key Y to redo builder change. case 89: event.preventDefault(); FusionEvents.trigger( 'fusion-history-redo' ); break; // Key Z to undo builder change. case 90: event.preventDefault(); FusionEvents.trigger( 'fusion-history-undo' ); break; } } // If both shift and meta key. if ( this.isMetaKey( event ) && this.isShiftKey( event ) ) { switch ( event.keyCode ) { // Key C to open custom css panel. case 67: if ( 0 === jQuery( 'body' ).find( '.ui-dialog' ).length && 'undefined' !== typeof FusionApp.sidebarView ) { event.preventDefault(); FusionApp.sidebarView.openOption( '_fusion_builder_custom_css', 'po' ); } break; // Key S to save, click rather than save directly so that animations occur. case 83: event.preventDefault(); if ( 0 === jQuery( 'body' ).find( '.ui-dialog' ).length ) { jQuery( '.fusion-builder-save-template' ).trigger( 'click' ); } break; } } } }, /** * Checks if meta key is pressed. * * @since 2.0.0 * @param {Object} [event] Contains event data. * @return {boolean} - Returns the bool value. */ isMetaKey: function( event ) { if ( event.ctrlKey || event.metaKey ) { return true; } return false; }, /** * Checks if shift key is pressed. * * @since 2.0.0 * @param {Object} [event] Contains event data. * @return {boolean} - Returns the bool value. */ isShiftKey: function( event ) { if ( event.shiftKey ) { return true; } return false; }, /** * Checks if target is valid. * * @since 2.0.0 * @param {Object} [event] Contains event data. * @return {boolean} - Returns the bool value. */ isValidTarget: function( event ) { //Ctrl+S/CMD+S always valid if ( ( 83 === event.keyCode && event.metaKey && !event.altKey ) || // CMD + s MacOS ( 83 === event.keyCode && event.ctrlKey && !event.altKey ) // Ctrl + s Windows ) { return true; } if ( jQuery( event.target ).is( 'input' ) || jQuery( event.target ).is( 'textarea' ) || jQuery( event.target ).is( '.fusion-live-editable' ) ) { return false; } return true; } } ); }( jQuery ) ); ;var FusionPageBuilder = FusionPageBuilder || {}; ( function() { /** * Fetch a JavaScript template for an id, and return a templating function for it. * * @param {string} id A string that corresponds to a DOM element * @return {Function} A function that lazily-compiles the template requested. */ FusionPageBuilder.template = _.memoize( function( html ) { var compiled, /* * Underscore's default ERB-style templates are incompatible with PHP * when asp_tags is enabled, so WordPress uses Mustache-inspired templating syntax. */ options = { evaluate: /<#([\s\S]+?)#>/g, interpolate: /\{\{\{([\s\S]+?)\}\}\}/g, escape: /\{\{([^\}]+?)\}\}(?!\})/g // eslint-disable-line no-useless-escape }; return function( data ) { compiled = compiled || _.template( html, null, options ); return compiled( data ); }; } ); }( jQuery ) ); ;/* global builderConfig, awbTypoData, FusionPageBuilder, builderId, fusionSettings, FusionPageBuilderApp, fusionAllElements, fusionAppConfig, FusionApp, fusionOptionName, fusionBuilderText, fusionIconSearch */ /* jshint -W020 */ var FusionEvents = _.extend( {}, Backbone.Events ); ( function() { jQuery( document ).ready( function() { var fusionApp = Backbone.Model.extend( { // jshint ignore: line initialize: function() { this.builderId = builderId; // User is logged in and connected to back-end. this.connected = true; // This data is set by preview_data(); this.initialData = {}; this.callback = new FusionPageBuilder.Callback(); this.dialog = new FusionPageBuilder.Dialog(); this.inlineEditor = new FusionPageBuilder.inlineEditor(); this.validate = new FusionPageBuilder.Validate(); this.hotkeys = new FusionPageBuilder.Hotkeys(); this.settings = 'undefined' !== typeof fusionSettings ? fusionSettings : false; // Store TO changed (for multilingual). this.elementDefaults = 'undefined' !== typeof fusionAllElements ? jQuery.extend( true, {}, fusionAllElements ) : {}; this.editedDefaults = {}; this.editedTo = {}; // Content changed status for save button. this.contentChanged = {}; // Current data this.data = {}; this.data.postMeta = {}; this.data.samePage = true; this.builderActive = false; this.hasEditableContent = true; // This can have data added from external to pass on for save. this.customSave = {}; // Objects to map TO changes to defaults of others. this.settingsPoTo = false; this.settingsToPo = false; this.settingsToParams = false; this.settingsToExtras = false; this.storedPoCSS = {}; this.storedToCSS = {}; // UI this.toolbarView = new FusionPageBuilder.Toolbar( { fusionApp: this } ); this.builderToolbarView = false; this.sidebarView = false; this.postLockView = false; this.renderUI(); // Preview size. this.previewWindowSize = 'large'; // Hold scripts which are being added to frame. this.scripts = {}; // Font Awesome stuff. this.listenTo( FusionEvents, 'fusion-preview-update', this.toggleFontAwesomePro ); this.listenTo( FusionEvents, 'fusion-to-status_fontawesome-changed', this.FontAwesomeSubSets ); // Preview updates. this.listenTo( FusionEvents, 'awb-update-studio-item-preview', this.previewColors ); this.setHeartbeatListeners(); this.correctLayoutTooltipPosition(); this.initStudioPreview(); // Cache busting var. this.refreshCounter = 0; // Track changes made this.hasChange = false; this.showLoader(); this.modifierActive = false; window.onkeydown = this.keyActive.bind( this ); window.onkeyup = this.keyInactive.bind( this ); document.getElementById( 'fb-preview' ).contentWindow.onkeydown = this.keyActive.bind( this ); document.getElementById( 'fb-preview' ).contentWindow.onkeyup = this.keyInactive.bind( this ); // If page switch has been triggered manually. this.manualSwitch = false; this.linkSelectors = 'td.tribe-events-thismonth a, .tribe-events-month-event-title a,.fusion-menu a, .fusion-secondary-menu a, .fusion-logo-link, .fusion-imageframe > a, .widget a, .woocommerce-tabs a, .fusion-posts-container a:not(.fusion-rollover-gallery), .fusion-rollover .fusion-rollover-link, .project-info-box a, .fusion-meta-info-wrapper a, .related-posts a, .related.products a, .woocommerce-page .products .product a, #tribe-events-content a, .fusion-breadcrumbs a, .single-navigation a, .fusion-column-inner-bg a'; }, /** * SIframe loaded event. * * @since 2.0.0 * @return {void} */ iframeLoaded: function() { this.linkListeners(); FusionEvents.trigger( 'fusion-iframe-loaded' ); }, /** * Sets active key modifier * * @since 2.0.0 * @return {void} */ keyActive: function( event ) { if ( event.ctrlKey || 17 == event.keyCode || 91 == event.keyCode || 93 == event.keyCode ) { this.modifierActive = true; } }, /** * Resets active key modifier * * @since 2.0.0 * @return {void} */ keyInactive: function( event ) { if ( event.ctrlKey || 17 == event.keyCode || 91 == event.keyCode || 93 == event.keyCode ) { this.modifierActive = false; } }, /** * Hides frame loader. * * @since 2.0.0 * @return {void} */ hideLoader: function() { jQuery( '#fb-preview-loader' ).removeClass( 'fusion-loading' ); jQuery( '#fusion-frontend-builder-toggle-global-panel, #fusion-frontend-builder-toggle-global-page-settings' ).css( 'pointer-events', 'auto' ); }, /** * Shows frame loader. * * @since 2.0.0 * @return {void} */ showLoader: function() { if ( jQuery( 'body' ).hasClass( 'expanded' ) ) { jQuery( '#fb-preview-loader' ).css( 'width', 'calc(100% - ' + jQuery( '#customize-controls' ).width() + 'px)' ); } else { jQuery( '#fb-preview-loader' ).css( 'width', '100%' ); } jQuery( '#fusion-frontend-builder-toggle-global-panel, #fusion-frontend-builder-toggle-global-page-settings' ).css( 'pointer-events', 'none' ); jQuery( '#fb-preview-loader' ).addClass( 'fusion-loading' ); }, /** * Corrects the position of builder layout tooltips when they would overflow the modals. * * @since 2.1 * @return {void} */ correctLayoutTooltipPosition: function() { jQuery( document ).on( 'mouseenter', '.fusion-layout-buttons .fusion-builder-layout-button-load-dialog', function() { var tooltip = jQuery( this ).find( '.fusion-builder-load-template-dialog-container' ), tooltipOffsetLeft = tooltip.offset().left, tooltipWidth = tooltip.outerWidth(), tooltipOffsetRight = tooltipOffsetLeft + tooltipWidth, modalContentWrapper = jQuery( this ).closest( '.ui-dialog-content' ), modalContentWrapperOffsetLeft = modalContentWrapper.offset().left, modalContentWrapperWidth = modalContentWrapper.outerWidth(), modalContentWrapperOffsetRight = modalContentWrapperOffsetLeft + modalContentWrapperWidth; if ( tooltipOffsetRight > modalContentWrapperOffsetRight ) { jQuery( this ).find( '.fusion-builder-load-template-dialog' ).css( 'left', '-' + ( tooltipOffsetRight - modalContentWrapperOffsetRight + 20 ) + 'px' ); } } ); jQuery( document ).on( 'mouseleave', '.fusion-layout-buttons .fusion-builder-layout-button-load-dialog', function() { jQuery( this ).find( '.fusion-builder-load-template-dialog' ).css( 'left', '' ); } ); }, /** * Inits studio previews. * * @since 3.5 * @return {void} */ initStudioPreview: function() { // Studio preview. jQuery( 'body' ).on( 'click', '.studio-wrapper .fusion-page-layout:not(.awb-demo-pages-layout) img', function( event ) { var $item = jQuery( event.currentTarget ).closest( '.fusion-page-layout' ), url = $item.data( 'url' ), $wrapper = $item.closest( '.studio-wrapper' ), layoutID = $item.data( 'layout-id' ); $wrapper.addClass( 'loading fusion-studio-preview-active' ); $wrapper.find( '.fusion-loader' ).show(); $wrapper.append( '' ); $wrapper.find( '.awb-import-options' ).addClass( 'open' ); $wrapper.data( 'layout-id', layoutID ); } ); // Remove studio preview. jQuery( 'body' ).on( 'click', '.fusion-studio-preview-back', function( event ) { var $wrapper = jQuery( event.currentTarget ).closest( '.studio-wrapper' ); event.preventDefault(); $wrapper.removeClass( 'fusion-studio-preview-active' ); $wrapper.find( '.awb-studio-preview-frame' ).remove(); $wrapper.find( '.awb-import-options' ).removeClass( 'open' ); $wrapper.removeData( 'layout-id' ); } ); // Import in preview. jQuery( 'body' ).on( 'click', '.fusion-studio-preview-active .awb-import-studio-item-in-preview', function( event ) { var $wrapper = jQuery( event.currentTarget ).closest( '.studio-wrapper ' ), dataID = $wrapper.data( 'layout-id' ); event.preventDefault(); jQuery( '.fusion-studio-preview-active .fusion-studio-preview-back' ).trigger( 'click' ); jQuery( '.fusion-page-layout[data-layout-id="' + dataID + '"]' ).find( '.awb-import-studio-item' ).trigger( 'click' ); } ); }, /** * Actions to perform when studio preview is loaded. * * @since 3.5 * @return {void} */ studioPreviewLoaded: function() { if ( 'object' === typeof FusionApp ) { this.previewColors(); } else { jQuery( '.studio-wrapper' ).removeClass( 'loading' ).find( '.fusion-loader' ).hide(); } }, /** * Trigger preview colors to update on preview. * * @since 3.7 * @return {void} */ previewColors: function() { var styleObject = getComputedStyle( document.getElementById( 'fb-preview' ).contentWindow.document.documentElement ), overWriteType = jQuery( '.awb-import-options input[name="overwrite-type"]:checked' ).val(), shouldInvert = jQuery( '.awb-import-options input[name="invert"]:checked' ).val(), varData = { color_palette: {}, typo_sets: {}, shouldInvert: shouldInvert }; varData = this.getOverWritePalette( varData, styleObject, overWriteType, shouldInvert ); varData = this.getOverWriteTypography( varData, styleObject, overWriteType ); jQuery( '.awb-studio-preview-frame' )[ 0 ].contentWindow.postMessage( varData, '*' ); // Remove loading from preview. jQuery( '.studio-wrapper' ).removeClass( 'loading' ).find( '.fusion-loader' ).hide(); }, /** * Gets overwrite palette. * * @since 3.7 * @param {Object} varData The var data. * @param {Object} styleObject The style object. * @param {String} overWriteType The overwrite type. * @param {String} shouldInvert If should invert or not. * @return {object} */ getOverWritePalette: function( varData, styleObject, overWriteType, shouldInvert ) { if ( 'inherit' === overWriteType ) { switch ( shouldInvert ) { case 'dont-invert': for ( let step = 1; 9 > step; step++ ) { varData.color_palette[ '--awb-color' + step ] = styleObject.getPropertyValue( '--awb-color' + step ); varData.color_palette[ '--awb-color' + step + '-h' ] = styleObject.getPropertyValue( '--awb-color' + step + '-h' ); varData.color_palette[ '--awb-color' + step + '-s' ] = styleObject.getPropertyValue( '--awb-color' + step + '-s' ); varData.color_palette[ '--awb-color' + step + '-l' ] = styleObject.getPropertyValue( '--awb-color' + step + '-l' ); varData.color_palette[ '--awb-color' + step + '-a' ] = styleObject.getPropertyValue( '--awb-color' + step + '-a' ); } break; case 'do-invert': for ( let i = 1, revI = 8; 8 >= i; i++, revI-- ) { varData.color_palette[ '--awb-color' + i ] = styleObject.getPropertyValue( '--awb-color' + revI ); varData.color_palette[ '--awb-color' + i + '-h' ] = styleObject.getPropertyValue( '--awb-color' + revI + '-h' ); varData.color_palette[ '--awb-color' + i + '-s' ] = styleObject.getPropertyValue( '--awb-color' + revI + '-s' ); varData.color_palette[ '--awb-color' + i + '-l' ] = styleObject.getPropertyValue( '--awb-color' + revI + '-l' ); varData.color_palette[ '--awb-color' + i + '-a' ] = styleObject.getPropertyValue( '--awb-color' + revI + '-a' ); } break; } return varData; } return varData; }, /** * Gets typography overwrite. * * @since 3.7 * @param {Object} varData The var data. * @param {Object} styleObject The style object. * @param {String} overWriteType The overwrite type. * @return {object} */ getOverWriteTypography: function( varData, styleObject, overWriteType ) { const subsets = [ 'font-family', 'font-size', 'font-weight', 'font-style', 'font-variant', 'line-height', 'letter-spacing', 'text-transform' ]; if ( 'inherit' !== overWriteType ) { return varData; } // Global typography sets. for ( let step = 1; 6 > step; step++ ) { subsets.forEach( function( subset ) { subset = '--awb-typography' + step + '-' + subset; const value = styleObject.getPropertyValue( subset ); if ( '' !== value ) { varData.typo_sets[ subset ] = value; } } ); } // Headings typography. for ( let step = 1; 7 > step; step++ ) { subsets.forEach( function( subset ) { subset = '--h' + step + '_typography-' + subset; const value = styleObject.getPropertyValue( subset ); if ( '' !== value ) { varData.typo_sets[ subset ] = value; } } ); } return varData; }, /** * Listen for heartbeat changes to ensure user is logged in. * * @since 2.0.0 * @return {void} */ setHeartbeatListeners: function() { var self = this; // Refresh nonces if they have signed back in. jQuery( document ).on( 'heartbeat-tick', function( event, data ) { // We have newly lost connection, set state and fire event. if ( 'undefined' !== typeof data[ 'wp-auth-check' ] && false === data[ 'wp-auth-check' ] && FusionApp.connected ) { self.connected = false; FusionEvents.trigger( 'fusion-disconnected' ); window.adminpage = 'post-php'; } // We have regained connection - refresh nonces, set state and fire event. if ( 'undefined' !== typeof data.fusion_builder ) { fusionAppConfig.fusion_load_nonce = data.fusion_builder.fusion_load_nonce; self.connected = true; delete window.adminpage; FusionEvents.trigger( 'fusion-reconnected' ); } } ); }, renderUI: function() { // Panel. if ( 'undefined' !== typeof FusionPageBuilder.SidebarView ) { this.sidebarView = new FusionPageBuilder.SidebarView(); jQuery( '.fusion-builder-panel-main' ).append( this.sidebarView.render().el ); } // Icon picker pre-init. this.iconPicker(); }, /** * Main init setup trigger for app. Fired from preview frame. * * @since 2.0.0 * @return {void} */ setup: function() { this.previewWindow = jQuery( '#fb-preview' )[ 0 ].contentWindow; this.updateData(); jQuery( 'body' ).append( this.toolbarView.render( ).el ); // Start Builder if ( 'undefined' !== typeof FusionPageBuilder.AppView && this.getPost( 'post_type' ) && this.isEditable() ) { this.builderActive = true; // eslint-disable-next-line vars-on-top var hasOverrideContent = this.data.template_override && this.data.template_override.content, overrideContent = hasOverrideContent && this.data.template_override.content.post_content; if ( 'fusion_tb_section' !== this.data.query.post_type && hasOverrideContent && overrideContent && ! overrideContent.includes( 'fusion_tb_content' ) ) { this.hasEditableContent = false; } if ( 'undefined' === typeof FusionPageBuilderApp ) { window.FusionPageBuilderApp = new FusionPageBuilder.AppView( { // jshint ignore: line el: jQuery( '#fb-preview' ).contents().find( '.fusion-builder-live' ) } ); // Builder toolbar if ( 'undefined' !== typeof FusionPageBuilder.BuilderToolbar ) { this.builderToolbarView = new FusionPageBuilder.BuilderToolbar(); this.toolbarView.render(); } // Post Lock if ( 'undefined' !== typeof FusionPageBuilder.postLock ) { this.postLockView = new FusionPageBuilder.postLock(); this.postLockView.render(); } } else { FusionPageBuilderApp.fusionBuilderReset(); FusionPageBuilderApp.$el = jQuery( '#fb-preview' ).contents().find( '.fusion-builder-live' ); FusionPageBuilderApp.render(); } FusionPageBuilderApp.initialBuilderLayout( this.data ); this.listenTo( FusionEvents, 'fusion-builder-loaded', this.hideLoader ); } else { this.builderActive = false; jQuery( document.getElementById( 'fb-preview' ).contentWindow.document ).ready( this.hideLoader ); } FusionEvents.trigger( 'fusion-app-setup' ); this.listenForLeave(); if ( this.sidebarView || 'undefined' !== typeof FusionPageBuilderApp ) { this.createMapObjects(); } jQuery( '#fb-preview' ).removeClass( 'refreshing' ); if ( 'undefined' !== typeof this.hotkeys ) { this.hotkeys.attachListener(); } const context = this; // Add additional data to Heartbeat data. jQuery( document ).on( 'heartbeat-send', function ( event, data ) { data[ 'fusion-post-lock-id' ] = context.initialData.postDetails.post_id; } ); // Release post lock. window.onbeforeunload = function () { if ( ! fusionAppConfig.post_lock_data ) { jQuery.ajax( { type: 'POST', url: fusionAppConfig.ajaxurl, data: { post_id: context.initialData.postDetails.post_id, fusion_load_nonce: fusionAppConfig.fusion_load_nonce, action: 'fusion_release_post_lock' } } ); } }; }, isEditable: function() { return -1 !== builderConfig.allowed_post_types.indexOf( this.getPost( 'post_type' ) ) || 'post_cards' === FusionApp.data.fusion_element_type || 'mega_menus' === FusionApp.data.fusion_element_type || true === FusionApp.data.is_shop; }, linkListeners: function() { var self = this; // Events calendar events page tweaks. jQuery( '#fb-preview' )[ 0 ].contentWindow.jQuery( '#tribe-events' ).off(); if ( 'undefined' !== typeof jQuery( '#fb-preview' )[ 0 ].contentWindow.tribe_ev ) { jQuery( '#fb-preview' )[ 0 ].contentWindow.jQuery( jQuery( '#fb-preview' )[ 0 ].contentWindow.tribe_ev.events ).on( 'post-collect-bar-params.tribe', function() { var linkHref = jQuery( '#fb-preview' )[ 0 ].contentWindow.tribe_ev.state.cur_url; if ( -1 !== linkHref.indexOf( '?' ) ) { linkHref = linkHref + '&builder=true&builder_id=' + self.builderId; } else { linkHref = linkHref + '?builder=true&builder_id=' + self.builderId; } jQuery( '#fb-preview' )[ 0 ].contentWindow.tribe_ev.state.cur_url = linkHref; self.showLoader(); } ); } jQuery( '#fb-preview' ).contents().on( 'click', this.linkSelectors, function( event ) { event.preventDefault(); self.checkLink( event ); } ); }, /** * Listen for closing or history change. * * @since 2.0.0 * @return {void} */ listenForLeave: function() { document.getElementById( 'fb-preview' ).contentWindow.addEventListener( 'beforeunload', this.leavingAlert.bind( this ) ); window.addEventListener( 'beforeunload', this.leavingAlert.bind( this ) ); this.manualSwitch = false; }, /** * Check if we should show a warning message. * * @since 2.0.0 * @return {void} */ leavingAlert: function( event ) { if ( this.hasContentChanged() && ! this.manualSwitch ) { event.returnValue = fusionBuilderText.changes_will_be_lost; } }, /** * Saves the post-content. * * @since 2.0.0 * @param {Object} successAction - Action object, containing action name and params. * @return {void} */ savePostContent: function( successAction ) { var self = this, postData = this.getAjaxData( 'fusion_app_save_post_content' ), width = jQuery( '.fusion-builder-save-page' ).outerWidth() + jQuery( '.fusion-exit-builder' ).outerWidth(), button = jQuery( '.fusion-builder-save-page' ); button.toggleClass( 'sending' ).blur(); if ( 'object' === typeof successAction && 'undefined' !== typeof successAction.action && ( 'switch_page' === successAction.action || 'exit_builder' === successAction.action ) ) { jQuery( '#fusion-builder-confirmation-modal-dark-overlay' ).css( 'top', '54px' ); jQuery( '#fusion-builder-confirmation-modal-dark-overlay' ).before( '
' ); jQuery( '.fusion-builder-confirmation-modal-save' ).attr( 'style', 'width:calc(100% - ' + width + 'px);' ); } jQuery.ajax( { type: 'POST', url: fusionAppConfig.ajaxurl, dataType: 'json', data: postData } ) .done( function( data ) { if ( 'object' !== typeof data ) { return; } if ( data.success && 'undefined' === typeof data.data.failure ) { // Save was successful. button.removeClass( 'sending' ).blur(); button.addClass( 'success' ); // Switch to new page after content was saved. if ( 'object' === typeof successAction && 'undefined' !== typeof successAction.action && 'switch_page' === successAction.action ) { self.switchPage( successAction.builderid, successAction.linkhref, successAction.linkhash ); } else if ( 'object' === typeof successAction && 'undefined' !== typeof successAction.action && 'exit_builder' === successAction.action ) { self.manualSwitch = true; window.location.href = successAction.link; } else { setTimeout( function() { button.removeClass( 'success' ); FusionApp.contentReset(); }, 2000 ); FusionEvents.trigger( 'fusion-app-saved' ); } } else if ( 'undefined' !== typeof data.data.failure && ( 'logged_in' === data.data.failure || 'nonce_check' === data.data.failure ) ) { // Save failed because user is not logged in, trigger heartbeat for log in form. jQuery( '#fusion-builder-confirmation-modal-dark-overlay' ).css( 'top', '' ); jQuery( '.fusion-builder-confirmation-modal-save' ).remove(); self.hideLoader(); button.removeClass( 'sending' ).blur(); button.addClass( 'failed' ); if ( 'undefined' !== typeof wp && 'undefined' !== typeof wp.heartbeat ) { FusionApp.confirmationPopup( { action: 'hide' } ); wp.heartbeat.connectNow(); } else { // No heartbeat warning. FusionApp.confirmationPopup( { title: fusionBuilderText.page_save_failed, content: fusionBuilderText.authentication_no_heartbeat, type: 'error', icon: '', actions: [ { label: fusionBuilderText.ok, classes: 'save yes', callback: function() { // Try again just in case. if ( 'undefined' !== typeof wp && 'undefined' !== typeof wp.heartbeat ) { wp.heartbeat.connectNow(); } FusionApp.confirmationPopup( { action: 'hide' } ); } } ] } ); } } else { // Save failed for another reason, provide details. jQuery( '#fusion-builder-confirmation-modal-dark-overlay' ).css( 'top', '' ); jQuery( '.fusion-builder-confirmation-modal-save' ).remove(); self.hideLoader(); button.removeClass( 'sending' ).blur(); button.addClass( 'failed' ); setTimeout( function() { button.removeClass( 'failed' ); }, 2000 ); FusionApp.confirmationPopup( { title: fusionBuilderText.problem_saving, content: fusionBuilderText.changes_not_saved + self.getSaveMessages( data.data ), type: 'error', icon: '', actions: [ { label: fusionBuilderText.ok, classes: 'save yes', callback: function() { if ( 'undefined' !== typeof wp && 'undefined' !== typeof wp.heartbeat ) { wp.heartbeat.connectNow(); } FusionApp.confirmationPopup( { action: 'hide' } ); } } ] } ); } } ); }, /** * List out the save data. * * @since 2.0.0 * @param {Object} data - The success/fail data. * @return {string} - Returns HTML. */ getSaveMessages: function( data ) { var returnMessages = ''; if ( 'object' === typeof data.failure ) { _.each( data.failure, function( messages ) { if ( 'string' === typeof messages ) { returnMessages += '
  • ' + messages + '
  • '; } else if ( 'object' === typeof messages ) { _.each( messages, function( message ) { if ( 'string' === typeof message ) { returnMessages += '
  • ' + message + '
  • '; } } ); } } ); } if ( 'object' === typeof data.success ) { _.each( data.success, function( messages ) { if ( 'string' === typeof messages ) { returnMessages += '
  • ' + messages + '
  • '; } else if ( 'object' === typeof messages ) { _.each( messages, function( message ) { if ( 'string' === typeof message ) { returnMessages += '
  • ' + message + '
  • '; } } ); } } ); } if ( '' !== returnMessages ) { return ''; } return ''; }, /** * Maps settings to params & page-options. * * @since 2.0.0 * @return {void} */ createMapObjects: function() { // Create the settings to params object. if ( ! this.settingsToParams && 'undefined' !== typeof FusionPageBuilderApp ) { this.createSettingsToParams(); } // Create the settings to extras object. if ( ! this.settingsToExtras && 'undefined' !== typeof FusionPageBuilderApp ) { this.createSettingsToExtras(); } // Create the settings to page options object. if ( ! this.settingsToPo ) { this.createSettingsToPo(); } }, /** * Maps settings to settingsToParams. * * @since 2.0.0 * @return {void} */ createSettingsToParams: function() { var settingsToParams = {}, paramObj; _.each( fusionAllElements, function( element, elementID ) { if ( ! _.isUndefined( element.settings_to_params ) ) { _.each( element.settings_to_params, function( param, setting ) { paramObj = { param: _.isObject( param ) && ! _.isUndefined( param.param ) ? param.param : param, callback: param.callback || false, element: elementID }; if ( _.isObject( settingsToParams[ setting ] ) ) { settingsToParams[ setting ].push( paramObj ); } else { settingsToParams[ setting ] = []; settingsToParams[ setting ].push( paramObj ); } } ); } } ); this.settingsToParams = settingsToParams; }, /** * Maps settings to settingsToExtras. * * @since 2.0.0 * @return {void} */ createSettingsToExtras: function() { var settingsToExtras = {}, paramObj; _.each( fusionAllElements, function( element, elementID ) { if ( ! _.isUndefined( element.settings_to_extras ) ) { _.each( element.settings_to_extras, function( param, setting ) { paramObj = { param: _.isObject( param ) && ! _.isUndefined( param.param ) ? param.param : param, callback: param.callback || false, element: elementID }; if ( _.isObject( settingsToExtras[ setting ] ) ) { settingsToExtras[ setting ].push( paramObj ); } else { settingsToExtras[ setting ] = []; settingsToExtras[ setting ].push( paramObj ); } } ); } } ); this.settingsToExtras = settingsToExtras; }, /** * Maps settings to settingsToPo. * * @since 2.0.0 * @return {void} */ createSettingsToPo: function() { var settingsToPo = {}, settingsPoTo = {}, paramObj; _.each( this.data.fusionPageOptions, function( tab, tabID ) { _.each( tab.fields, function( option, optionID ) { if ( ! _.isUndefined( option.to_default ) ) { paramObj = { to: _.isObject( option.to_default ) && ! _.isUndefined( option.to_default.id ) ? option.to_default.id : option.to_default, callback: option.to_default.callback || false, option: optionID, tab: tabID }; // Process settingsToPo if ( _.isObject( settingsToPo[ paramObj.to ] ) ) { settingsToPo[ paramObj.to ].push( paramObj ); } else { settingsToPo[ paramObj.to ] = []; settingsToPo[ paramObj.to ].push( paramObj ); } // Process settingsPoTo if ( _.isObject( settingsPoTo[ optionID ] ) ) { settingsPoTo[ optionID ] = paramObj.to; } else { settingsPoTo[ optionID ] = []; settingsPoTo[ optionID ] = paramObj.to; } } } ); } ); this.settingsToPo = settingsToPo; this.settingsPoTo = settingsPoTo; }, /** * Update the app data with preview data on load or page change. * * @since 2.0.0 * @return {void} */ updateData: function() { // Language is different. if ( 'undefined' !== typeof this.data.language && 'undefined' !== typeof this.initialData.languageTo && this.initialData.language !== this.data.language && 'undefined' !== typeof FusionApp.sidebarView ) { this.languageSwitch(); } if ( this.getPost( 'post_id' ) === this.initialData.postDetails.post_id ) { this.data.samePage = true; } else { // Set correct url in browser and history. this.updateURL( this.initialData.postDetails.post_permalink ); this.data = this.initialData; this.data.samePage = false; this.contentReset( 'page' ); this.storedPoCSS = false; this.customSave = {}; FusionEvents.trigger( 'fusion-history-clear' ); // If toolbar exists and language set, update switcher. if ( false !== this.toolbarView && this.data.language ) { this.toolbarView.updateLanguageSwitcher(); } FusionEvents.trigger( 'fusion-data-updated' ); } }, /** * Get post details by key or on its own. * * @since 2.0.0 * @param {string} key - The key we want to get from postDetails. If undefined all postDetails will be fetched. * @return {mixed} - Returns postDetails[ key ] if a key is defined, otherwise return postDetails. */ getPost: function( key ) { if ( 'object' !== typeof this.data.postDetails ) { return false; } if ( 'undefined' === typeof key ) { return jQuery.extend( true, {}, this.data.postDetails ); } if ( 'undefined' === typeof this.data.postDetails[ key ] ) { return false; } return this.data.postDetails[ key ]; }, /** * Get post details by key or on its own. * * @since 2.0.0 * @param {string} key - The key we want to get from postDetails. If undefined all postDetails will be fetched. * @return {mixed} - Returns postDetails[ key ] if a key is defined, otherwise return postDetails. */ getDynamicPost: function( key ) { if ( 'post_meta' === key ) { if ( 'object' !== typeof this.data.examplePostDetails ) { return FusionApp.data.postMeta; } return this.data.examplePostDetails.post_meta; } if ( ( 'fusion_tb_section' === FusionApp.data.postDetails.post_type || 'post_cards' === FusionApp.data.fusion_element_type || 'awb_off_canvas' === FusionApp.data.postDetails.post_type ) && 'undefined' !== typeof FusionApp.data.postMeta._fusion && 'undefined' !== typeof FusionApp.data.postMeta._fusion.dynamic_content_preview_type && 'undefined' !== typeof FusionApp.initialData.dynamicPostID ) { return FusionApp.initialData.dynamicPostID; } if ( 'object' !== typeof this.data.examplePostDetails ) { return this.getPost( key ); } if ( 'undefined' === typeof key ) { return jQuery.extend( true, {}, this.data.examplePostDetails ); } if ( 'undefined' == typeof this.data.examplePostDetails[ key ] ) { return this.getPost( key ); } return this.data.examplePostDetails[ key ]; }, /** * Set post details by key. * * @since 2.0.0 * @param {string} key - The key of the property we want to set. * @param {string} value - The value of the property we want to set. * @return {void} */ setPost: function( key, value ) { if ( 'object' !== typeof this.data.postDetails ) { this.data.postDetails = {}; } this.data.postDetails[ key ] = value; }, /** * Get preview url. * * @since 2.0.0 * @return {string} - URL. */ getPreviewUrl: function() { return FusionApp.previewWindow.location.href.replace( 'builder=true', 'builder=false&fbpreview=true' ); }, /** * Updates language specific options. * * @since 2.0.0 * @return {void} */ languageSwitch: function() { // Save defaults and edited TO. this.editedDefaults[ this.data.language ] = jQuery.extend( true, {}, fusionAllElements ); this.editedTo[ this.data.language ] = jQuery.extend( true, {}, FusionApp.settings ); // Change setting values to those of new language. if ( 'undefined' !== typeof this.editedTo[ this.initialData.language ] ) { FusionApp.settings = this.editedTo[ this.initialData.language ]; } else { FusionApp.settings = this.initialData.languageTo; } // Change option name to option for new language. window.fusionOptionName = this.initialData.optionName; // Restore element defaults, eg button color. if ( 'undefined' !== typeof this.editedDefaults[ this.initialData.language ] ) { window.fusionAllElements = jQuery.extend( true, {}, this.editedDefaults[ this.initialData.language ] ); } else if ( 'undefined' !== typeof this.initialData.languageDefaults ) { window.fusionAllElements = jQuery.extend( true, fusionAllElements, this.initialData.languageDefaults ); } else { window.fusionAllElements = jQuery.extend( true, {}, this.elementDefaults ); } // Rebuilder sidebar views for new values. FusionApp.sidebarView.refreshTo(); }, /** * Triggers a full-refresh of the preview iframe. * * @since 2.0.0 * @param {string} target - Target URL to load. * @param {Object} event - Event on click that triggered. * @param {Object} postDetails - Post details which should be used on refresh. * @return {void} */ fullRefresh: function( target, event, postDetails ) { this.showLoader(); target = 'undefined' === typeof target ? false : target; event = 'undefined' === typeof event ? {} : event; this.setGoogleFonts(); this.reInitIconPicker(); this.doTheFullRefresh( target, event, postDetails ); }, /** * Sets builder status in post meta.. * * @since 2.0.0 * @return {void} */ setBuilderStatus: function() { var builderStatus = false, savedStatus = 'undefined' !== typeof this.data.postMeta.fusion_builder_status ? this.data.postMeta.fusion_builder_status : false; if ( 'undefined' !== typeof FusionPageBuilderApp ) { builderStatus = 'active'; } if ( builderStatus !== savedStatus ) { this.data.postMeta.fusion_builder_status = builderStatus; this.contentChange( 'page', 'page-option' ); } }, /** * Get changed data for ajax requests. * * @since 2.0.0 * @param {string} action - The ajax action. * @param {Object} postDetails - Post details which should be used on refresh. * @return {Object} - Returns the postData. */ getAjaxData: function( action, postDetails ) { var postData = { post_id: this.getPost( 'post_id' ), fusion_load_nonce: fusionAppConfig.fusion_load_nonce, custom: jQuery.param( this.customSave ), builder_id: this.builderId }; if ( 'fusion_app_full_refresh' !== action && 'fusion_app_preview_only' !== action ) { postData.query = FusionApp.data.query; } if ( 'undefined' === typeof postDetails ) { postDetails = {}; } // Set the action if set. if ( 'string' === typeof action ) { postData.action = action; } // If page settings have changed then add them, but without post_content. if ( this.hasContentChanged( 'page', 'page-setting' ) ) { postData.post_details = this.getPost(); if ( 'undefined' !== typeof postData.post_details.post_content ) { delete postData.post_details.post_content; } } // If FB is active and post_content has changed. if ( 'undefined' !== typeof FusionPageBuilderApp && this.hasContentChanged( 'page', 'builder-content' ) ) { if ( 'undefined' !== typeof postDetails.post_content ) { postData.post_content = postDetails.post_content; } else { FusionPageBuilderApp.builderToShortcodes(); postData.post_content = this.getPost( 'post_content' ); // eslint-disable-line camelcase } this.setGoogleFonts(); } this.setBuilderStatus(); // If Avada panel exists and either TO or PO has changed. if ( this.sidebarView && ( this.hasContentChanged( 'global', 'theme-option' ) || this.hasContentChanged( 'page', 'page-option' ) ) ) { this.reInitIconPicker(); if ( this.hasContentChanged( 'global', 'theme-option' ) ) { postData.fusion_options = jQuery.param( this.maybeEmptyArray( FusionApp.settings ) ); // eslint-disable-line camelcase } if ( this.hasContentChanged( 'page', 'page-option' ) ) { postData.meta_values = jQuery.param( this.data.postMeta ); // eslint-disable-line camelcase } } if ( 'object' === typeof postData.post_details ) { postData.post_details = jQuery.param( postData.post_details ); // eslint-disable-line camelcase } // Option name for multilingual saving. if ( 'undefined' !== typeof fusionOptionName ) { postData.option_name = fusionOptionName; } if ( 'object' === typeof FusionApp.data.examplePostDetails && 'undefined' !== typeof FusionApp.data.examplePostDetails.post_id ) { postData.target_post = FusionApp.data.examplePostDetails.post_id; } return postData; }, /** * Triggers a full-refresh of the preview iframe. * * @since 2.0.0 * @param {string} target - Target URL to load. * @param {Object} event - Event on click that triggered. * @param {Object} postDetails - Post details which should be used on refresh. * @return {void} */ doTheFullRefresh: function( target, event, postDetails ) { var postData = this.getAjaxData( 'fusion_app_full_refresh', postDetails ); this.refreshCounter = this.refreshCounter + 1; if ( jQuery( '.ui-dialog-content' ).length ) { jQuery( '.ui-dialog-content' ).dialog( 'close' ); } jQuery( '#fb-preview' ).addClass( 'refreshing' ); FusionEvents.trigger( 'fusion-preview-refreshed' ); this.formPost( postData ); }, formPost: function( postData, newSrc, target ) { var $form = jQuery( '#refresh-form' ), src = 'undefined' === typeof newSrc || ! newSrc ? jQuery( '#fb-preview' ).attr( 'src' ) : newSrc; $form.empty(); if ( 'string' !== typeof target ) { target = jQuery( '#fb-preview' ).attr( 'name' ); this.previewWindow.name = target; } $form.attr( 'target', target ); $form.attr( 'action', src ); _.each( postData, function( value, id ) { if ( 'post_content' === id ) { value = window.encodeURIComponent( value ); } $form.append( '' ); } ); this.manualSwitch = true; $form.submit().empty(); }, /** * Refreshes the preview frame. * * @since 2.0.0 * @return {void} */ previewRefresh: function() { var self = this, originalCount = self.refreshCounter - 1, refreshString = '&refresh=' + originalCount; this.manualSwitch = true; jQuery( '#fb-preview' ).attr( 'src', function( i, val ) { if ( -1 === val.indexOf( '&post_id=' ) ) { val += '&post_id=' + self.getPost( 'post_id' ); } // Make sure to add unique refresh parameter. if ( -1 === val.indexOf( refreshString ) ) { val += '&refresh=' + self.refreshCounter; } else { val = val.replace( refreshString, '&refresh=' + self.refreshCounter ); } return val; } ); FusionEvents.trigger( 'fusion-preview-refreshed' ); }, /** * Checks links. * * @since 2.0.0 * @param {Object} event - The jQuery event. * @param {string} href - URL. * @return {void} */ checkLink: function( event, href ) { var self = this, linkHref = 'undefined' === typeof href ? jQuery( event.currentTarget ).attr( 'href' ) : href, linkHash = '', targetPathname = '', targetHostname = '', $targetEl = this.previewWindow.jQuery( jQuery( event.currentTarget ) ), link, linkParts; event.preventDefault(); // Split hash and move to end of URL. if ( -1 !== linkHref.indexOf( '#' ) ) { linkParts = linkHref.split( '#' ); linkHref = linkParts[ 0 ]; linkHash = '#_' + linkParts[ 1 ]; } // Get path name from event (link). if ( 'object' === typeof event ) { targetPathname = event.currentTarget.pathname; targetHostname = event.currentTarget.hostname; } // If manually passing a url, get pathname from that instead. if ( 'undefined' !== typeof href ) { link = document.createElement( 'a' ); link.href = href; targetPathname = link.pathname; targetHostname = link.hostname; } // Check for scroll links on same page and return. if ( '#' === linkHref.charAt( 0 ) || ( '' !== linkHash && targetPathname === location.pathname ) ) { if ( 'function' === typeof $targetEl.fusion_scroll_to_anchor_target && ! $targetEl.parent().parent().hasClass( 'wc-tabs' ) ) { $targetEl.fusion_scroll_to_anchor_target(); } return; } // Check if flyout submenus are enabled and menu item has submenu. if ( $targetEl.parent().hasClass( 'menu-item' ) && $targetEl.parent().hasClass( 'menu-item-has-children' ) && $targetEl.closest( '.awb-menu' ).hasClass( 'awb-menu_flyout' ) ) { return; } // Check link is on same site or manually being triggered. if ( location.hostname === targetHostname || 'undefined' !== typeof href ) { this.showLoader(); // Make user confirm. if ( this.hasContentChanged( 'page' ) ) { FusionApp.confirmationPopup( { title: fusionBuilderText.unsaved_changes, content: fusionBuilderText.changes_will_be_lost, class: 'fusion-confirmation-unsaved-changes', actions: [ { label: fusionBuilderText.cancel, classes: 'cancel no', callback: function() { self.hideLoader(); FusionApp.confirmationPopup( { action: 'hide' } ); } }, { label: fusionBuilderText.just_leave, classes: 'dont-save yes', callback: function() { self.switchPage( self.builderId, linkHref, linkHash ); } }, { label: fusionBuilderText.leave, classes: 'save yes', callback: function() { var successAction = {}; successAction.action = 'switch_page'; successAction.builderid = self.builderId; successAction.linkhref = linkHref; successAction.linkhash = linkHash; self.savePostContent( successAction ); } } ] } ); } else { self.switchPage( self.builderId, linkHref, linkHash ); } } }, switchPage: function( builderId, linkHref, linkHash ) { var postData = {}; if ( jQuery( '.ui-dialog-content' ).length ) { jQuery( '.ui-dialog-content' ).dialog( 'close' ); } jQuery( '#fb-preview' ).addClass( 'refreshing' ); this.manualSwitch = true; if ( this.hasContentChanged( 'global', 'theme-option' ) ) { postData = { fusion_load_nonce: fusionAppConfig.fusion_load_nonce, builder_id: this.builderId, action: 'fusion_app_switch_page', fusion_options: jQuery.param( FusionApp.settings ), // eslint-disable-line camelcase option_name: fusionOptionName // eslint-disable-line camelcase }; jQuery( '#fb-preview' ).addClass( 'refreshing' ); if ( -1 !== linkHref.indexOf( '?' ) ) { linkHref = linkHref + '&builder=true&builder_id=' + builderId + linkHash; } else { linkHref = linkHref + '?builder=true&builder_id=' + builderId + linkHash; } this.formPost( postData, linkHref ); } else { this.goToURL( builderId, linkHref, linkHash ); } }, /** * Goes to a URL. * * @param {string} builderId - The builder-ID. * @param {string} linkHref - The URL. * @param {string} linkHash - The hash part of the URL. * @return {void} */ goToURL: function( builderId, linkHref, linkHash ) { var newPage; this.manualSwitch = true; // Close dialogs. if ( jQuery( '.ui-dialog-content' ).length ) { jQuery( '.ui-dialog-content' ).dialog( 'close' ); } if ( jQuery( '#fusion-close-element-settings' ).length ) { jQuery( '#fusion-close-element-settings' ).trigger( 'click' ); } jQuery( '#fusion-builder-confirmation-modal' ).hide(); jQuery( '#fusion-builder-confirmation-modal-dark-overlay' ).hide(); // Add necessary details to URL. if ( -1 !== linkHref.indexOf( '?' ) ) { newPage = linkHref + '&builder=true&builder_id=' + builderId + linkHash; } else { newPage = linkHref + '?builder=true&builder_id=' + builderId + linkHash; } // Change iframe URL. jQuery( '#fb-preview' ).attr( 'src', newPage ); }, /** * Updates the URL. * * @since 2.0.0 * @return {void} */ updateURL: function( newURL ) { var frameWindow = document.getElementById( 'fb-preview' ).contentWindow, frameDocument = frameWindow.document; if ( '' === newURL || '?fb-edit=1' === newURL ) { newURL = jQuery( '#fb-preview' ).attr( 'src' ).split( '?' )[ 0 ] + '?fb-edit=1'; } window.history.replaceState( { url: newURL }, frameDocument.title, newURL ); document.title = frameDocument.title; }, /** * Removes scripts from markup and stores separately. * * @since 2.0.0 * @return {void} */ removeScripts: function( content, cid ) { var $markup = jQuery( '
    ' + content + '
    ' ), $scripts = $markup.find( 'script' ), $injection = []; if ( $scripts.length ) { $scripts.each( function() { // Add script markup to injection var. if ( jQuery( this ).attr( 'src' ) ) { $injection.push( { type: 'src', value: jQuery( this ).attr( 'src' ) } ); } else { $injection.push( { type: 'inline', value: jQuery( this ).html() } ); } // Remove script from render. jQuery( this ).remove(); } ); this.scripts[ cid ] = $injection; return $markup.html(); } return $markup.html(); }, /** * Injects stored scripts. * * @since 2.0.0 * @return {void} */ injectScripts: function( cid ) { var $body = jQuery( '#fb-preview' ).contents().find( 'body' )[ 0 ], scripts = this.scripts[ cid ], frameDocument = document.getElementById( 'fb-preview' ).contentWindow.document, oldWrite = frameDocument.write, // jshint ignore:line self = this, el, elId; // Turn document write off before partial request. frameDocument.write = function() {}; // eslint-disable-line no-empty-function if ( 'undefined' !== typeof scripts && scripts.length ) { _.each( scripts, function( script, id ) { elId = 'fusion-script-' + cid + '-' + id; // If it already exists, remove it. if ( jQuery( '#fb-preview' ).contents().find( 'body' ).find( '#' + elId ).length ) { jQuery( '#fb-preview' ).contents().find( 'body' ).find( '#' + elId ).remove(); } // Create script on iframe. el = document.createElement( 'script' ); el.setAttribute( 'type', 'text/javascript' ); el.setAttribute( 'id', 'fusion-script-' + cid + '-' + id ); if ( 'src' === script.type ) { el.setAttribute( 'src', script.value ); } else { el.innerHTML = script.value; } // If this is a hubspot form, wait and then add to element. if ( 'inline' === script.type && -1 !== script.value.indexOf( 'hbspt.forms.create' ) ) { self.initHubSpotForm( script, cid, el ); return; } $body.appendChild( el ); } ); } frameDocument.write = oldWrite; // jshint ignore:line }, /** * Init hubspot embed form. * * @since 2.2 * @return {void} */ initHubSpotForm: function( script, cid, el ) { var self = this, timeoutValue = 'undefined' !== typeof FusionApp.previewWindow.hbspt ? 0 : 500, $element = jQuery( '#fb-preview' ).contents().find( 'div[data-cid="' + cid + '"]' ).find( '.fusion-builder-element-content' ).first(); // Keep a count of repetitions to avoid. this.hubspotRepeat = 'undefined' === this.hubspotRepeat ? 0 : this.hubspotRepeat + 1; if ( 5 < this.hubspotRepeat ) { return; } setTimeout( function() { if ( 'undefined' === typeof FusionApp.previewWindow.hbspt ) { self.initHubSpotForm( script, cid, el ); return; } if ( $element.length ) { self.hubspotRepeat = 0; $element.find( '.hbspt-form' ).remove(); $element[ 0 ].appendChild( el ); } }, timeoutValue ); }, /** * Deletes scripts from DOM when element is removed. * * @since 2.0.0 * @return {void} */ deleteScripts: function( cid ) { var scripts = this.scripts[ cid ]; if ( scripts ) { _.each( scripts, function( script, id ) { var elId = 'fusion-script-' + cid + '-' + id; // If it already exists, remove it. if ( jQuery( '#fb-preview' ).contents().find( 'body' ).find( '#' + elId ).length ) { jQuery( '#fb-preview' ).contents().find( 'body' ).find( '#' + elId ).remove(); } } ); delete this.scripts[ cid ]; } }, /** * Filters elements on search. * * @since 2.0.0 * @param {Object} thisEl - jQuery DOM element. * @return {void} */ elementSearchFilter: function( thisEl ) { var name, value; thisEl.find( '.fusion-elements-filter' ).on( 'change paste keyup', function() { if ( jQuery( this ).val() ) { value = jQuery( this ).val().toLowerCase(); thisEl.find( '.fusion-builder-all-modules li, .studio-imports li' ).each( function() { var shortcode = jQuery( this ).find( '.fusion_module_label' ).length ? jQuery( this ).find( '.fusion_module_label' ).text().trim().toLowerCase() : ''; name = jQuery( this ).find( '.fusion_module_title' ).text().trim().toLowerCase(); // Also show portfolio on recent works search if ( 'portfolio' === name ) { name += ' recent works'; } if ( 'fusion_imageframe' === shortcode ) { name += ' ' + fusionBuilderText.logo.trim().toLowerCase(); } if ( -1 !== name.search( value ) || jQuery( this ).hasClass( 'spacer' ) ) { jQuery( this ).show(); } else { jQuery( this ).hide(); } } ); } else { thisEl.find( '.fusion-builder-all-modules li' ).show(); thisEl.find( '.studio-imports li' ).show(); } } ); setTimeout( function() { jQuery( '.fusion-elements-filter' ).focus(); }, 50 ); }, /** * Checks page content for element font families. * * @since 2.0.0 * @param object googleFonts * @return {Object} */ setElementFonts: function( googleFonts ) { var postContent = this.getPost( 'post_content' ), regexp, fontProps, tempFonts = {}, saveFonts = []; if ( 'string' === typeof postContent && '' !== postContent && -1 !== postContent.indexOf( 'fusion_font_' ) ) { regexp = new RegExp( '(fusion_font_[^=]*=")([^"]*)"', 'g' ); fontProps = this.getPost( 'post_content' ).match( regexp ); // Iterate through all font properties in post content and build font objects. _.each( fontProps, function( prop ) { var config = prop.slice( 0, -1 ).split( '="' ), key = config[ 0 ], value = config[ 1 ], optionId = key.replace( /fusion_font_(family|variant)_/, '' ), fontProperty = ( key.includes( 'fusion_font_variant_' ) && 'variant' ) || 'family'; if ( '' === key && 'family' === fontProperty ) { return; } if ( 'object' !== typeof tempFonts[ optionId ] ) { tempFonts[ optionId ] = {}; } else if ( 'family' === fontProperty && tempFonts[ optionId ].family ) { // If we are setting family again for something already in process, then save out incomplete and start fresh saveFonts.push( tempFonts[ optionId ] ); tempFonts[ optionId ] = {}; } tempFonts[ optionId ][ fontProperty ] = value; // If all three are set, add to save fonts and delete from temporary holder so others can be collected with same ID. if ( 'undefined' !== typeof tempFonts[ optionId ].family && 'undefined' !== typeof tempFonts[ optionId ].variant ) { saveFonts.push( tempFonts[ optionId ] ); delete tempFonts[ optionId ]; } } ); // Check for incomplete ones with family and add them too. _.each( tempFonts, function( font, option ) { if ( 'undefined' !== typeof font.family && '' !== font.family ) { saveFonts.push( tempFonts[ option ] ); } } ); // Look all fonts for saving and save. _.each( saveFonts, function( font ) { if ( 'undefined' === typeof font.family || '' === font.family ) { return; } if ( 'undefined' === typeof googleFonts[ font.family ] ) { googleFonts[ font.family ] = { variants: [] }; } // Add the variant if it does not exist already. if ( 'string' === typeof font.variant && ! googleFonts[ font.family ].variants.includes( font.variant ) ) { googleFonts[ font.family ].variants.push( font.variant ); } } ); } return googleFonts; }, /** * Checks page content for font dependencies. * * @since 2.0.0 * @return {Object} */ setGoogleFonts: function() { var self = this, googleFonts = {}, fontFamily, $fontNodes = jQuery( '#fb-preview' ).contents().find( '[data-fusion-google-font]' ); googleFonts = this.setElementFonts( googleFonts ); if ( $fontNodes.length ) { $fontNodes.each( function() { if ( 'undefined' === typeof googleFonts[ jQuery( this ).attr( 'data-fusion-google-font' ) ] ) { googleFonts[ jQuery( this ).attr( 'data-fusion-google-font' ) ] = { variants: [] }; } // Add the variant. if ( jQuery( this ).attr( 'data-fusion-google-variant' ) ) { googleFonts[ jQuery( this ).attr( 'data-fusion-google-font' ) ].variants.push( jQuery( this ).attr( 'data-fusion-google-variant' ) ); } } ); } // Delete global typographies. If is studio, then parse overwrite typography to add to meta. for ( fontFamily in googleFonts ) { if ( fontFamily.includes( 'var(' ) ) { // awbOriginalPalette is a variable present only on studio plugin. if ( window.awbOriginalPalette ) { addOverwriteTypographyToMeta( fontFamily ); } } } // Check each has a variant selected _.each( googleFonts, function( font, family ) { if ( 'object' !== typeof font.variants || ! font.variants.length ) { googleFonts[ family ].variants = [ 'regular' ]; } } ); if ( 'object' === typeof this.data.postMeta._fusion_google_fonts ) { _.each( this.data.postMeta._fusion_google_fonts, function( fontData, currentFontFamily ) { _.each( fontData, function( values, key ) { self.data.postMeta._fusion_google_fonts[ currentFontFamily ][ key ] = _.values( values ); } ); } ); // We have existing values and existing value is not the same as new. if ( ! _.isEqual( this.data.postMeta._fusion_google_fonts, googleFonts ) ) { if ( _.isEmpty( googleFonts ) ) { googleFonts = ''; } this.data.postMeta._fusion_google_fonts = googleFonts; // eslint-disable-line camelcase this.contentChange( 'page', 'page-option' ); } } else if ( ! _.isEmpty( googleFonts ) ) { // We do not have existing values and we do have fonts now. this.data.postMeta._fusion_google_fonts = googleFonts; // eslint-disable-line camelcase this.contentChange( 'page', 'page-option' ); } function addOverwriteTypographyToMeta( globalVar ) { var typoMatch = globalVar.match( /--awb-typography(\d)/ ), fontName, fontVariant, uniqueFontVariant, variantMatch, i, typoId; if ( ! typoMatch[ 1 ] || ! Array.isArray( googleFonts[ globalVar ].variants ) ) { delete googleFonts[ globalVar ]; return; } // Get the font family. typoId = typoMatch[ 1 ]; fontName = awbTypoData.data[ 'typography' + typoId ][ 'font-family' ]; fontVariant = []; // Get the global font variants and merge with non-global ones. for ( i = 0; i < googleFonts[ globalVar ].variants.length; i++ ) { if ( googleFonts[ globalVar ].variants[ i ].includes( 'var(' ) ) { variantMatch = googleFonts[ globalVar ].variants[ i ].match( /--awb-typography(\d)/ ); if ( variantMatch[ 1 ] ) { if ( awbTypoData.data[ 'typography' + variantMatch[ 1 ] ].variant ) { fontVariant.push( awbTypoData.data[ 'typography' + variantMatch[ 1 ] ].variant ); } else { fontVariant.push( '400' ); } } } else { fontVariant.push( googleFonts[ globalVar ].variants[ i ] ); } } // Update the font variant. If exist then concat them. if ( googleFonts[ fontName ] ) { if ( googleFonts[ fontName ].variants ) { googleFonts[ fontName ].variants = googleFonts[ fontName ].variants.concat( fontVariant ); } else { googleFonts[ fontName ].variants = fontVariant; } } else { googleFonts[ fontName ] = {}; googleFonts[ fontName ].variants = fontVariant; } // Remove duplicate variants. uniqueFontVariant = []; googleFonts[ fontName ].variants.forEach( function( el ) { if ( ! uniqueFontVariant.includes( el ) ) { uniqueFontVariant.push( el ); } } ); googleFonts[ fontName ].variants = uniqueFontVariant; // Finally, delete global variant. delete googleFonts[ globalVar ]; } }, /** * Adds font awesome relative stylesheets. * * @since 2.0.0 * @return {Object} */ toggleFontAwesomePro: function( id ) { if ( 'status_fontawesome_pro' === id || ( 'fontawesome_v4_compatibility' === id && 0 === jQuery( '#fontawesome-shims-css' ).length ) ) { jQuery.ajax( { type: 'GET', url: fusionAppConfig.ajaxurl, dataType: 'json', data: { action: 'fusion_font_awesome', fusion_load_nonce: fusionAppConfig.fusion_load_nonce, pro_status: FusionApp.settings.status_fontawesome_pro } } ) .done( function( response ) { fusionAppConfig.fontawesomeicons = response.icons; jQuery( '#fontawesome-css' ).attr( 'href', response.css_url ); if ( 'fontawesome_v4_compatibility' === id ) { jQuery( 'body' ).append( '' ); } else { jQuery( '#fontawesome-shims-css' ).attr( 'href', response.css_url ); } FusionApp.reInitIconPicker(); } ); } }, /** * Re inits icon picker on subset value change. * * @since 2.0.0 * @return {Object} */ FontAwesomeSubSets: function() { FusionApp.reInitIconPicker(); }, /** * Checks for a context of content change. * * @since 2.0 * @return {void} */ hasContentChanged: function( context, name ) { var status = false; if ( 'undefined' !== typeof context ) { if ( 'undefined' !== typeof name ) { status = 'undefined' !== typeof this.contentChanged[ context ] && 'undefined' !== typeof this.contentChanged[ context ][ name ] && true === this.contentChanged[ context ][ name ]; } else { status = 'undefined' !== typeof this.contentChanged[ context ] && ! _.isEmpty( this.contentChanged[ context ] ); } } else { _.each( this.contentChanged, function( scopedContext ) { if ( ! _.isEmpty( scopedContext ) ) { status = true; } } ); } return status; }, /** * When content has been changed. * * @since 2.0 * @return {void} */ contentChange: function( context, name ) { if ( 'object' !== typeof this.contentChanged[ context ] ) { this.contentChanged[ context ] = {}; } this.contentChanged[ context ][ name ] = true; FusionApp.set( 'hasChange', true ); }, /** * Preinit for icon pickers. * * @since 2.0 * @return {void} */ iconPicker: function() { var icons = fusionAppConfig.fontawesomeicons, output = ''; jQuery( 'body' ).append( output + outputNav ); jQuery( '.fusion-icon-picker-save' ).trigger( 'click' ); if ( 'undefined' !== typeof window[ 'fusion-fontawesome-free-shims' ] ) { _.each( window[ 'fusion-fontawesome-free-shims' ], function( shim ) { if ( null !== shim[ 0 ] && null !== shim[ 2 ] ) { jQuery( '.fusion-icons-rendered' ).find( 'i.fa-' + shim[ 2 ] ).attr( 'data-alt-name', shim[ 0 ] ); } } ); } }, /** * Reinit icon picker. * * @since 2.0 * @return {void} */ reInitIconPicker: function() { jQuery( '.fusion-icons-rendered' ).remove(); jQuery( '.fusion-icon-picker-nav-rendered' ).remove(); this.iconPicker(); }, checkLegacyAndCustomIcons: function( icon ) { var oldIconName; if ( '' !== icon ) { if ( 'fusion-prefix-' === icon.substr( 0, 14 ) ) { // Custom icon, we need to remove prefix. icon = icon.replace( 'fusion-prefix-', '' ); } else { icon = icon.split( ' ' ), oldIconName = ''; // Legacy FontAwesome 4.x icon, so we need check if it needs to be updated. if ( 'undefined' === typeof icon[ 1 ] ) { icon[ 1 ] = 'fas'; if ( 'undefined' !== typeof window[ 'fusion-fontawesome-free-shims' ] ) { oldIconName = icon[ 0 ].substr( 3 ); jQuery.each( window[ 'fusion-fontawesome-free-shims' ], function( i, shim ) { if ( shim[ 0 ] === oldIconName ) { // Update icon name. if ( null !== shim[ 2 ] ) { icon[ 0 ] = 'fa-' + shim[ 2 ]; } // Update icon subset. if ( null !== shim[ 1 ] ) { icon[ 1 ] = shim[ 1 ]; } return false; } } ); } icon = icon[ 0 ] + ' ' + icon[ 1 ]; } } } return icon; }, /** * When content has been reset to default. * * @since 2.0 * @return {void} */ contentReset: function( context, name ) { if ( 'undefined' !== typeof name ) { // Reset for specific name. if ( 'undefined' !== typeof this.contentChanged[ context ] && 'undefined' !== typeof this.contentChanged[ context ][ name ] ) { delete this.contentChanged[ context ][ name ]; } } else if ( 'undefined' !== typeof context ) { // Reset entire context. this.contentChanged[ context ] = {}; } else { // Reset all. this.contentChanged = {}; } if ( ! this.hasContentChanged() ) { FusionApp.set( 'hasChange', false ); } }, /** * Creates and handles confirmation popups. * * @param {Object} args - The popup arguments. * @param {string} args.title - The title. * @param {string} args.content - The content for this popup. * @param {string} args.type - Can be "info" or "warning". Changes the color of the icon. * @param {string} args.icon - HTML for the icon. * @param {string} args.class - Additional CSS classes for the popup.. * @param {string} args.action - If "hide", it hides the popup. * @param {Array} args.actions - An array of actions. These get added as buttons. * @param {Object} args.actions[0] - Each item in the actions array is an object. * @param {string} args.actions[0].label - The label that will be used for the button. * @param {string} args.actions[0].classes - The CSS class that will be added to the button. * @param {Function} args.actions[0].callback - A function that will be executed when the button gets clicked. */ confirmationPopup: function( args ) { if ( 'hide' === args.action ) { // Hide elements. jQuery( '#fusion-builder-confirmation-modal-dark-overlay' ).hide(); jQuery( '#fusion-builder-confirmation-modal' ).hide(); // Early exit. return; } // Early exit if no content & title, or if there's no actions defined. if ( ( ! args.content && ! args.title ) || ( ! args.actions || ! args.actions[ 0 ] ) ) { return; } // Use default icon (exclamation mark) if no custom icon is defined. if ( ! args.icon ) { args.icon = '