File: /home/adventp/www/wp-content/plugins/js_composer/assets/js/backend/composer-storage.js
/* =========================================================
* composer-storage.js 1
* =========================================================
* Copyright 2013 Wpbakery
*
* WPBakery Page Builder backbone/underscore Storage hidden field
* ========================================================= */
(function ( $ ) {
'use strict';
vc.debug = false;
/**
* Shortcodes mapping settings.
* @type {Object}
*/
vc.map = _.isUndefined( window.vc_mapper ) ? {} : window.vc_mapper; // TODO: check with frontend why in frontend user user_mapper
/**
* Shortcode Roles Based on general settings
* @since 4.5
*
* @type {{}}
*/
vc.roles = _.isUndefined( window.vc_roles ) ? {} : window.vc_roles;
/**
* @constructor
*/
vc.Storage = function () {
this.data = {};
};
/**
* CRUD methods for content management.
* @type {Object}
*/
vc.Storage.prototype = {
url: window.ajaxurl,
checksum: false,
locked: false,
isChanged: false,
/**
* Create new object in storage. Require to define model name because system uses only one hidden field with
* serialized json object.
* @param model - Backbone.Model object
* @return {*} - Backbone.Model object
*/
create: function ( model ) {
if ( !model.id ) {
model.id = model.attributes.id = vc_guid();
}
this.data[ model.id ] = model.toJSON();
// optimize root update
this.setModelRoot( model.id );
this.save();
return model;
},
/**
* Optimization methods
*/
// {{ Methods allows lock/unlock data parsing into shortcodes and saving it in wp editor.
lock: function () {
this.locked = true;
},
unlock: function () {
this.locked = false;
},
// }}
setModelRoot: function ( id ) {
var data = this.data[ id ];
if ( _.isString( data.parent_id ) && _.isObject( this.data[ data.parent_id ] ) ) {
data.root_id = this.data[ data.parent_id ].root_id;
}
if ( _.isObject( this.data[ data.root_id ] ) ) {
this.data[ data.root_id ].html = false;
}
},
/**
* Update object in storage.
* @param model
* @return {*}
*/
update: function ( model ) {
this.data[ model.id ] = model.toJSON();
this.setModelRoot( model.id );
this.save();
return model;
},
/**
* Remove record from storage
* @param model
* @return {*}
*/
destroy: function ( model ) {
if ( !_.isUndefined( this.data[ model.id ] ) && !_.isUndefined( this.data[ model.id ].root_id ) && _.isObject(
this.data[ this.data[ model.id ].root_id ] ) ) {
this.data[ this.data[ model.id ].root_id ].html = false;
}
if ( !_.isUndefined( this.data[ model.id ] ) ) {
delete this.data[ model.id ];
}
this.save();
return model;
},
/**
* Find record by id
* @param model_id - id of model.
* @return {*} - object
*/
find: function ( model_id ) {
return this.data[ model_id ];
},
/**
* Find all records in storage. Used by fetch.
* @return {*}
*/
findAll: function () {
this.fetch();
return _.values( this.data );
},
/**
* Find all root models which are sorted by order field.
* @return {*}
*/
findAllRootSorted: function () {
var models = _.filter( _.values( this.data ), function ( model ) {
return false === model.parent_id;
} );
return _.sortBy( models, function ( model ) {
return model.order;
} );
},
/***
* Escape double quotes and square brackets in params value.
* @param value
* @return string
*/
escapeParam: function ( value ) {
if ( _.isUndefined( value ) || _.isNull( value ) || !value.toString ) {
return '';
}
return value.toString().replace( /"/g, '``' ).replace( /\[/g, '`{`' ).replace( /\]/g, '`}`' );
},
/**
* Unescape double quotes and square brackets in params value.
* @param value
* @return {*}
*/
unescapeParam: function ( value ) {
value = value.replace( /\`{\`/g, '[' ).replace( /\`}\`/g, ']' ).replace( /(\`{2})/g, '"' );
value = vc_wpnop( value );
return value;
},
/**
* Converts model data to wordpress shortcode.
* @param model
* @return {*}
*/
storageCreateShortcodeString: function ( model ) {
var mapped, data, tag, params, content, paramsForString, mergedParams, isContainer;
tag = model.get( 'shortcode' );
params = _.extend( {}, model.get( 'params' ) );
paramsForString = {};
mergedParams = vc.getMergedParams( tag, params );
_.each( mergedParams, function ( value, key ) {
paramsForString[ key ] = this.escapeParam( value );
}, this );
mapped = vc.getMapped( tag );
isContainer = _.isObject( mapped ) && ((_.isBoolean( mapped.is_container ) && true === mapped.is_container) || !_.isEmpty(
mapped.as_parent ));
content = this._storageGetShortcodeContent( model );
data = {
tag: tag,
attrs: paramsForString,
content: content,
type: _.isUndefined( vc.getParamSettings( tag, 'content' ) ) && !isContainer ? 'single' : ''
};
model.trigger( 'stringify', model, data );
return wp.shortcode.string( data );
},
/**
* Save data in hidden field.
* @return {Boolean}
*/
save: function () {
if ( this.locked ) {
this.locked = false;
return false;
}
var content = _.reduce( this.findAllRootSorted(), function ( memo, modelArray ) {
var model = vc.shortcodes.get( modelArray );
return memo + this.storageCreateShortcodeString( model );
}, '', this );
this.setContent( content );
this.checksum = vc_globalHashCode( content );
return this;
},
/**
* If shortcode is container like, gets content of is shortcode in shortcodes style.
* @param parent - shortcode inside which content is.
* @return {*}
* @private
*/
_storageGetShortcodeContent: function ( parent ) {
var models, params;
models = _.sortBy( _.filter( this.data, function ( model ) {
// Filter children
return model.parent_id === parent.get( 'id' );
} ), function ( model ) {
// Sort by `order` field
return model.order;
} );
if ( !models.length ) {
params = _.extend( {}, parent.get( 'params' ) );
return _.isUndefined( params.content ) ? '' : params.content;
}
return _.reduce( models, function ( memo, modelArray ) {
var model = vc.shortcodes.get( modelArray );
return memo + this.storageCreateShortcodeString( model );
}, '', this );
},
/**
* Get content of main editor of current post. Data is used as models collection of shortcodes.
* @return {*}
*/
getContent: function () {
if ( _.isObject( window.tinymce ) && tinymce.editors.content ) {
tinymce.editors.content.save();
}
return window.vc_wpnop( $( '#content' ).val() || '' );
},
addUndo: _.debounce( function ( content ) {
if ( vc.undoRedoApi ) {
vc.undoRedoApi.add( content );
}
}, 100 ),
/**
* Set content of the current_post inside editor.
* @param content
* @private
*/
setContent: function ( content ) {
this.addUndo( content );
var contentTinyMce = window.tinyMCE && window.tinyMCE.get && window.tinyMCE.get( 'content' );
content = vc_wpautop( content );
if (!this.isChanged) {
window.jQuery( window ).on( 'beforeunload.vcSave', function () {
return window.i18nLocale.confirm_to_leave;
} );
}
this.isChanged = true;
$( '#content' ).val( content );
if ( contentTinyMce && contentTinyMce.setContent ) {
contentTinyMce.setContent( content );
contentTinyMce.fire( 'change' );
}
},
/**
* Parse shortcode string into objects.
* @param data
* @param content
* @param parent
* @return {*}
*/
parseContent: function ( data, content, parent ) {
var tags = _.keys( vc.map ).join( '|' ),
reg = window.wp.shortcode.regexp( tags ),
matches = content.trim().match( reg );
if ( _.isNull( matches ) ) {
return data;
}
_.each( matches, function ( raw ) {
var sub_matches = raw.match( this.regexp( tags ) ),
sub_content = sub_matches[ 5 ],
sub_regexp = new RegExp( '^[\\s]*\\[\\[?(' + _.keys( vc.map ).join( '|' ) + ')(?![\\w-])' ),
id = window.vc_guid(),
atts_raw = window.wp.shortcode.attrs( sub_matches[ 3 ] ),
atts = {},
shortcode,
map_settings;
_.each( atts_raw.named, function ( value, key ) {
atts[ key ] = this.unescapeParam( value );
}, this );
shortcode = {
id: id,
shortcode: sub_matches[ 2 ],
order: this.order,
params: _.extend( {}, atts ),
parent_id: (_.isObject( parent ) ? parent.id : false),
root_id: (_.isObject( parent ) ? parent.root_id : id)
};
map_settings = vc.map[ shortcode.shortcode ];
this.order += 1;
if ( _.isArray( data ) ) {
data.push( shortcode );
id = data.length - 1;
} else {
data[ id ] = shortcode;
}
if ( id == shortcode.root_id ) {
data[ id ].html = raw;
}
if ( _.isString( sub_content ) && sub_content.match( sub_regexp ) &&
(
(_.isBoolean( map_settings.is_container ) && true === map_settings.is_container) ||
(!_.isEmpty( map_settings.as_parent ) && false !== map_settings.as_parent)
) ) {
data = this.parseContent( data, sub_content, data[ id ] );
} else if ( _.isString( sub_content ) && sub_content.length && 'vc_row' === sub_matches[ 2 ] ) {
data = this.parseContent( data,
'[vc_column width="1/1"][vc_column_text]' + sub_content + '[/vc_column_text][/vc_column]',
data[ id ] );
} else if ( _.isString( sub_content ) && sub_content.length && 'vc_column' === sub_matches[ 2 ] ) {
data = this.parseContent( data,
'[vc_column_text]' + sub_content + '[/vc_column_text]',
data[ id ] );
} else if ( _.isString( sub_content ) ) {
data[ id ].params.content = sub_content;
}
}, this );
return data;
},
/**
* Checks by checksum is content changed.
* @return {Boolean}
*/
isContentChanged: function () {
return false === this.checksum || this.checksum !== vc_globalHashCode( this.getContent() );
},
/**
* Wrap content which is not inside vc shorcodes.
* @param content
* @return {*}
*/
wrapData: function ( content ) {
var tags = _.keys( vc.map ).join( '|' ),
reg = this.regexp_split( 'vc_row' ),
starts_with_shortcode = new RegExp( '^\\[(\\[?)(' + tags + ')', 'g' );
// hacks: vc_section
var _this = this;
var storage = {};
var i = 0;
content = wp.shortcode.replace( 'vc_section', content, function ( data ) {
var toSave = {
attrs: data.attrs.named,
content: _this.wrapData( data.content )
};
i ++;
var hash = 'vc_pseudo_section_' + i + '_' + VCS4() + VCS4();
storage[ hash ] = {
tag: hash,
data: toSave
};
return '[vc_row][vc_pseudo_section id="' + hash + '"][/vc_pseudo_section][/vc_row]';
} );
var matches = _.filter( content.trim().split( reg ), function ( value ) {
if ( !_.isEmpty( value ) ) {
return value;
}
} );
content = _.reduce( matches, function ( mem, value ) {
if ( value.trim().indexOf(
'vc_pseudo_section_' ) === - 1 && !value.trim().match( starts_with_shortcode ) ) {
value = '[vc_row][vc_column][vc_column_text]' + value + '[/vc_column_text][/vc_column][/vc_row]';
}
var matches_local = value.match( vc_regexp_shortcode() );
/**
* Wrap existed shortcodes that is not containers (why not container too? because VC_ROW!)
*/
if ( _.isArray( matches_local ) && !_.isUndefined( matches_local[ 2 ] ) && matches_local[ 2 ].indexOf(
'vc_pseudo_section_' ) === - 1 && !_.isUndefined( vc.map[ matches_local[ 2 ] ] ) ) {
if ( (_.isUndefined( vc.map[ matches_local[ 2 ] ].is_container ) || !vc.map[ matches_local[ 2 ] ].is_container) && _.isEmpty(
vc.map[ matches_local[ 2 ] ].as_parent ) ) {
value = '[vc_row][vc_column]' + value + '[/vc_column][/vc_row]';
}
}
return mem + value;
}, '' );
// Re wrap back vc_section!
if ( Object.keys( storage ).length > 0 ) {
content = content.replace( /\[vc_row\]\[vc_pseudo_section/g, '[vc_pseudo_section' );
content = content.replace( /\[\/vc_pseudo_section\]\[\/vc_row\]/g, '[/vc_pseudo_section]' );
content = wp.shortcode.replace( 'vc_pseudo_section', content, function ( data ) {
var item = storage[ data.attrs.named.id ];
return wp.shortcode.string( {
tag: 'vc_section',
attrs: item.data.attrs,
content: item.data.content
} );
} );
}
return content;
},
/**
* Get data from hidden field and parse it from string to objects list.
* @return {*}
*/
fetch: function () {
if ( !this.isContentChanged() ) {
return this;
}
this.order = 0;
var content = this.getContent();
this.checksum = vc_globalHashCode( content );
content = this.wrapData( content );
this.data = this.parseContent( {}, content );
},
/**
* Append new data to existing one.
* @param content - string of shortcodes.
*/
append: function ( content ) {
this.data = {};
this.order = 0;
try {
var current_content = this.getContent();
this.setContent( current_content + "" + content );
} catch ( e ) {
if ( window.console && window.console.warn ) {
window.console.warn( 'storage.append error', e );
}
}
},
/**
* Regexp used to split unwrapped data.
*/
regexp_split: _.memoize( function ( tags ) {
return new RegExp( '(\\[(\\[?)[' + tags + ']+' +
'(?![\\w-])' +
'[^\\]\\/]*' +
'[\\/' +
'(?!\\])' +
'[^\\]\\/]*' +
']?' +
'(?:' +
'\\/]' +
'\\]|\\]' +
'(?:' +
'[^\\[]*' +
'(?:\\[' +
'(?!\\/' + tags + '\\])[^\\[]*' +
')*' +
'' +
'\\[\\/' + tags + '\\]' +
')?' +
')' +
'\\]?)', 'g' );
} ),
regexp: _.memoize( function ( tags ) {
return new RegExp( '\\[(\\[?)(' + tags + ')(?![\\w-])([^\\]\\/]*(?:\\/(?!\\])[^\\]\\/]*)*?)(?:(\\/)\\]|\\](?:([^\\[]*(?:\\[(?!\\/\\2\\])[^\\[]*)*)(\\[\\/\\2\\]))?)(\\]?)' );
} )
};
vc.storage = new vc.Storage();
})( window.jQuery );