(function($){
/**
* Helper class for rendering layout changes via AJAX.
*
* @class FLBuilderAJAXLayout
* @since 1.7
*/
FLBuilderAJAXLayout = function( data, callback )
{
this._data = $.extend( {}, this._defaults, typeof data == 'string' ? JSON.parse( data ) : data );
this._callback = callback;
this._post = FLBuilderConfig.postId;
this._head = $('head').eq(0);
this._body = $('body').eq(0);
// Setup the new CSS vars if we have new CSS.
if ( this._data.css ) {
this._loader = $('');
this._oldCss = $('link[href*="/cache/' + this._post + '-layout"]');
this._newCss = $('');
}
// Setup partial refresh vars.
if ( this._data.partial ) {
if ( this._data.js ) {
this._oldJs = $('#fl-builder-partial-refresh-js');
this._newJs = $('');
}
if ( this._data.nodeId ) {
if ( this._data.oldNodeId ) {
this._oldScriptsStyles = $( '.fl-builder-node-scripts-styles[data-node="' + this._data.oldNodeId + '"]' );
this._content = $( '.fl-node-' + this._data.oldNodeId );
}
else {
this._oldScriptsStyles = $( '.fl-builder-node-scripts-styles[data-node="' + this._data.nodeId + '"]' );
this._content = $( '.fl-node-' + this._data.nodeId ).eq(0);
}
}
}
// Setup full refresh vars.
else {
this._oldJs = $('script[src*="/cache/' + this._post + '"]');
this._newJs = $('');
this._oldScriptsStyles = $( '.fl-builder-layout-scripts-styles' );
this._content = $( FLBuilder._contentClass );
}
this._init();
};
/**
* Prototype for new instances.
*
* @since 1.7.
* @property {Object} prototype
*/
FLBuilderAJAXLayout.prototype = {
/**
* Defaults for the data sent from the server.
*
* @since 1.7
* @access private
* @property {Object} _defaults
*/
_defaults : {
partial : false,
nodeId : null,
nodeType : null,
nodeParent : null,
nodePosition : null,
oldNodeId : null,
html : null,
scriptsStyles : null,
css : null,
js : null
},
/**
* Data from the server for this render.
*
* @since 1.7
* @access private
* @property {Object} _data
*/
_data : null,
/**
* A function to call when the render is complete.
*
* @since 1.7
* @access private
* @property {Function} _callback
*/
_callback : function(){},
/**
* The ID of this post.
*
* @since 1.7
* @access private
* @property {Number} _post
*/
_post : null,
/**
* A jQuery reference to the head element.
*
* @since 1.7
* @access private
* @property {Object} _head
*/
_head : null,
/**
* A jQuery reference to the body element.
*
* @since 1.7
* @access private
* @property {Object} _body
*/
_body : null,
/**
* An jQuery reference to an image element that is used
* to preload the new CSS file using the onerror hack.
*
* @since 1.7
* @access private
* @property {Object} _loader
*/
_loader : null,
/**
* An jQuery reference to the old CSS element.
*
* @since 1.7
* @access private
* @property {Object} _oldCss
*/
_oldCss : null,
/**
* An jQuery reference to the new CSS element.
*
* @since 1.7
* @access private
* @property {Object} _newCss
*/
_newCss : null,
/**
* An jQuery reference to the old JS element.
*
* @since 1.7
* @access private
* @property {Object} _oldJs
*/
_oldJs : null,
/**
* An jQuery reference to the new JS element.
*
* @since 1.7
* @access private
* @property {Object} _newJs
*/
_newJs : null,
/**
* An jQuery reference to the old div that holds scripts
* and styles generated by widgets and shortcodes.
*
* @since 1.7
* @access private
* @property {Object} _oldScriptsStyles
*/
_oldScriptsStyles : null,
/**
* An jQuery reference to the content element.
*
* @since 1.7
* @access private
* @property {Object} _content
*/
_content : null,
/**
* Starts the render by loading the new CSS file.
*
* @since 1.7
* @access private
* @method _init
*/
_init: function()
{
// Set the body height so the page doesn't scroll.
this._body.height( this._body.height() );
// Load the new CSS.
if ( this._loader ) {
// Load CSS using modern methods or fallback to the old way.
if ( 'onload' in document.createElement( 'link' ) ) {
this._newCss.on( 'load', $.proxy( this._finish, this ) );
this._addNewCSS();
} else {
this._loader.on( 'error', $.proxy( this._loadNewCSSFallbackComplete, this ) );
this._body.append( this._loader );
}
} else {
// We don't have new CSS, finish the render.
this._finish();
}
},
/**
* Removes the fallback loader, adds the new CSS once it has loaded,
* and sets a quick timeout to finish the render.
*
* @since 1.7
* @access private
* @method _loadNewCSSFallbackComplete
*/
_loadNewCSSFallbackComplete: function()
{
// Remove the loader.
this._loader.remove();
// Add the new layout css.
this._addNewCSS();
// Set a quick timeout to ensure the css has taken effect.
setTimeout( $.proxy( this._finish, this ), 250 );
},
/**
* Adds the new CSS once it has been loaded.
*
* @since 2.2
* @access private
* @method _addNewCSS
*/
_addNewCSS: function()
{
if ( this._oldCss.length > 0 ) {
this._oldCss.after( this._newCss );
} else {
this._head.append( this._newCss );
}
},
/**
* Finishes the render after the CSS has been loaded.
*
* @since 1.7
* @access private
* @method _finish
*/
_finish: function()
{
// Remove the old content and assets.
this._removeOldContentAndAssets();
// Clean the new HTML.
this._cleanNewHTML();
// Clean up the new JS and CSS assets.
this._cleanNewAssets();
// Add the new HTML.
this._addNewHTML();
// Add widget/shortcode JS and CSS assets.
this._addNewScriptsStyles();
// Add the new layout JS.
this._addNewJS();
// Send the layout rendered event.
$( FLBuilder._contentClass ).trigger( 'fl-builder.layout-rendered' );
// Hide the loader.
FLBuilder.hideAjaxLoader();
// Run the callback.
if ( typeof this._callback != 'undefined' ) {
this._callback();
}
// Fire the complete hook.
FLBuilder.triggerHook( 'didRenderLayoutComplete' );
},
/**
* Removes old content and assets from the page.
*
* @since 1.7
* @access private
* @method _removeOldContentAndAssets
*/
_removeOldContentAndAssets: function()
{
if ( this._content ) {
this._content.empty();
}
if ( this._oldCss ) {
this._oldCss.remove();
}
if ( this._oldJs ) {
this._oldJs.remove();
}
if ( this._oldScriptsStyles ) {
this._oldScriptsStyles.remove();
}
},
/**
* Removes scripts and styles from _data.html that have been added by
* widgets and shortcodes and adds them to _data.scriptsStyles.
*
* @since 1.7
* @access private
* @method _cleanNewHTML
*/
_cleanNewHTML: function()
{
// Only proceed if _data.scriptsStyles is set.
if ( ! this._data.scriptsStyles ) {
return;
}
// Setup vars.
var html = $( '