File: /var/www/html/triad-infosec/wp-content/themes/Avada/includes/lib/inc/fusion-app/fusion-app.js
/* 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( '<iframe class="awb-studio-preview-frame" src="' + url + '" frameBorder="0" scrolling="auto" onload="FusionApp.studioPreviewLoaded();" allowfullscreen=""></iframe>' );
$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( '<div class="fusion-builder-confirmation-modal-save"></div>' );
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: '<i class="fusiona-exclamation-triangle" aria-hidden="true"></i>',
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: '<i class="fusiona-exclamation-triangle" aria-hidden="true"></i>',
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 += '<li class="failure"><i class="fusiona-exclamation-triangle" aria-hidden="true"></i>' + messages + '</li>';
} else if ( 'object' === typeof messages ) {
_.each( messages, function( message ) {
if ( 'string' === typeof message ) {
returnMessages += '<li class="failure"><i class="fusiona-exclamation-triangle" aria-hidden="true"></i>' + message + '</li>';
}
} );
}
} );
}
if ( 'object' === typeof data.success ) {
_.each( data.success, function( messages ) {
if ( 'string' === typeof messages ) {
returnMessages += '<li class="success"><i class="fusiona-check" aria-hidden="true"></i>' + messages + '</li>';
} else if ( 'object' === typeof messages ) {
_.each( messages, function( message ) {
if ( 'string' === typeof message ) {
returnMessages += '<li class="success"><i class="fusiona-check" aria-hidden="true"></i>' + message + '</li>';
}
} );
}
} );
}
if ( '' !== returnMessages ) {
return '<ul class="fusion-save-data-list">' + returnMessages + '</ul>';
}
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( '<input type="hidden" name="' + id + '" value="' + value + '" />' );
} );
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( '<div>' + content + '</div>' ),
$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( '<link rel="stylesheet" id="fontawesome-shims-css" href="' + response.shims_url + '" type="text/css" media="all">' );
} 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 = '<div class="fusion-icons-rendered" style="display:none;position:relative; height:0px; overflow:hidden;">',
outputNav = '<div class="fusion-icon-picker-nav-rendered" style="display:none;height:0px; overflow:hidden;">',
iconSubsets = {
fas: 'Solid',
far: 'Regular',
fal: 'Light',
fab: 'Brands'
},
outputSets = {
fas: '',
fab: '',
far: '',
fal: ''
},
self = this,
isSearchDefined = 'undefined' !== typeof fusionIconSearch && Array.isArray( fusionIconSearch );
if ( jQuery( '.fusion-icons-rendered' ).length || ! Array.isArray( self.settings.status_fontawesome ) ) {
return;
}
// Iterate through all FA icons and divide them into sets (one icon can belong to multiple sets).
_.each( icons, function( icon, key ) {
_.each( icon[ 1 ], function( iconSubset ) {
if ( -1 !== self.settings.status_fontawesome.indexOf( iconSubset ) ) {
outputSets[ iconSubset ] += '<span class="icon_preview ' + key + '" title="' + key + ' - ' + iconSubsets[ iconSubset ] + '"><i class="' + icon[ 0 ] + ' ' + iconSubset + '" data-name="' + icon[ 0 ].substr( 3 ) + '" aria-hidden="true"></i></span>';
}
} );
} );
// Add FA sets to output.
_.each( iconSubsets, function( label, key ) {
if ( -1 !== self.settings.status_fontawesome.indexOf( key ) ) {
outputNav += '<a href="#fusion-' + key + '" class="fusion-icon-picker-nav-item">' + label + '</a>';
output += '<div id="fusion-' + key + '" class="fusion-icon-set">' + outputSets[ key ] + '</div>';
}
} );
// WIP: Add custom icons.
icons = fusionAppConfig.customIcons;
_.each( icons, function( iconSet, IconSetKey ) {
outputNav += '<a href="#' + IconSetKey + '" class="fusion-icon-picker-nav-item">' + iconSet.name + '</a>';
output += '<div id="' + IconSetKey + '" class="fusion-icon-set fusion-custom-icon-set">';
_.each( iconSet.icons, function( icon ) {
if ( isSearchDefined ) {
fusionIconSearch.push( { name: icon } );
}
output += '<span class="icon_preview ' + icon + '" title="' + iconSet.css_prefix + icon + '"><i class="' + iconSet.css_prefix + icon + '" data-name="' + icon + '" aria-hidden="true"></i></span>';
} );
output += '</div>';
} );
outputNav += '</div>';
output += '</div>';
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 = '<i class="fas fa-exclamation" aria-hidden="true">';
}
// Use default type (warning) if no type is defined.
if ( ! args.type ) {
args.type = 'warning';
}
// Show the popup.
jQuery( '#fusion-builder-confirmation-modal-dark-overlay' ).show();
jQuery( '#fusion-builder-confirmation-modal' ).show();
// Add the class.
if ( 'undefined' !== typeof args[ 'class' ] ) {
jQuery( '#fusion-builder-confirmation-modal' ).attr( 'class', args[ 'class' ] );
}
// Add the icon.
jQuery( '#fusion-builder-confirmation-modal span.icon' )
.html( args.icon )
.removeClass( 'type-warning type-error-type-info' )
.addClass( 'type-' + args.type );
// Add the title.
if ( args.title ) {
jQuery( '#fusion-builder-confirmation-modal h3.title' ).show();
jQuery( '#fusion-builder-confirmation-modal h3.title' ).html( args.title );
} else {
jQuery( '#fusion-builder-confirmation-modal h3.title' ).hide();
}
// Add the content.
if ( args.content ) {
jQuery( '#fusion-builder-confirmation-modal span.content' ).show();
jQuery( '#fusion-builder-confirmation-modal span.content' ).html( args.content );
} else {
jQuery( '#fusion-builder-confirmation-modal span.content' ).hide();
}
// Reset the HTML for buttons so we can add anew based on the arguments we have.
jQuery( '#fusion-builder-confirmation-modal .actions' ).html( '' );
// Add buttons.
_.each( args.actions, function( action ) {
var classes = '.' + action.classes;
if ( 0 < action.classes.indexOf( ' ' ) ) {
classes = '.' + action.classes.replace( / /g, '.' );
}
jQuery( '#fusion-builder-confirmation-modal .actions' ).append( '<button class="' + action.classes + '">' + action.label + '</button>' );
jQuery( '#fusion-builder-confirmation-modal .actions ' + classes ).on( 'click', action.callback );
} );
},
/**
* Reset some CSS values, when modal settings dialogs get closed.
*
* @since 2.0
* @param {Object} modalView - View of the closed modal.
* @return {void}
*/
dialogCloseResets: function( modalView ) {
if ( ! modalView.$el.closest( '.ui-dialog.fusion-builder-child-element' ).length ) {
jQuery( 'body' ).removeClass( 'fusion-settings-dialog-default fusion-settings-dialog-large' );
}
this.previewWindow.jQuery( 'body' ).removeClass( 'fusion-dialog-ui-active' );
},
/**
* Shows multiple dialogs notice.
*
* @return {void}
*/
multipleDialogsNotice: function() {
this.confirmationPopup( {
title: fusionBuilderText.multi_dialogs,
content: fusionBuilderText.multi_dialogs_notice,
actions: [
{
label: fusionBuilderText.ok,
classes: 'yes',
callback: function() {
FusionApp.confirmationPopup( {
action: 'hide'
} );
}
}
]
} );
},
/**
* Getter for TO value or it's default if value is not saved yet.
*
* @since 3.0
* @param {string} optionKey - Option key (ID).
* @return {mixed}
*/
getSettingValue: function( settingKey ) {
var flatOptions;
if ( undefined === settingKey ) {
return undefined;
}
if ( 'undefined' !== typeof this.settings[ settingKey ] ) {
return this.settings[ settingKey ];
}
flatOptions = this.sidebarView.getFlatToObject();
if ( 'undefined' !== typeof flatOptions[ settingKey ] && 'undefined' !== typeof flatOptions[ settingKey ][ 'default' ] ) {
return flatOptions[ settingKey ][ 'default' ];
}
return undefined;
},
/**
* Getter for previewWindowSize property.
* Used to get 'custom' screen size, which is used to change correct options in EO.
*
* @since 3.0
* @return {string}
*/
getPreviewWindowSize: function() {
return this.previewWindowSize;
},
/**
* Helper for responsive options.
*
* @since 2.3.0
* @return {string}
*/
getResponsiveOptionKey: function ( key, isFlex = true ) {
var previewSize = FusionApp.getPreviewWindowSize(),
optionKey = ! isFlex || 'large' == previewSize ? key : key + '_' + previewSize;
return optionKey;
},
/**
* Setter for previewWindowSize property.
* Used to set 'custom' screen size, which is used to change correct options in EO.
*
* @since 3.0
* @param {string} newScreenSize - New screen size string.
* @return {void}
*/
setPreviewWindowSize: function( newScreenSize ) {
if ( -1 !== newScreenSize.indexOf( 'mobile' ) ) {
this.previewWindowSize = 'small';
} else if ( -1 !== newScreenSize.indexOf( 'tablet' ) ) {
this.previewWindowSize = 'medium';
} else {
this.previewWindowSize = 'large';
}
},
/**
* Check for empty array values.
* Used to fix issue with jQuery.param omit empty array.
*
* @since 7.2
* @param {object} obj - Object Arrays.
* @return {void}
*/
maybeEmptyArray: function( obj ) {
var key;
for ( key in obj ) {
if ( 'object' === typeof obj[ key ] && 0 == obj[ key ].length ) {
obj[ key ] = [ '' ];
}
}
return obj;
}
} );
if ( 'undefined' === typeof FusionApp ) {
window.FusionApp = new fusionApp(); // jshint ignore: line
}
} );
}( jQuery ) );