File: //var/www/mussarq_bak2/wp-content/plugins/us-header-builder/admin/js/header-builder.js
if (window.$ushb === undefined) window.$ushb = {};
$ushb.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
!function($){
if (window.$ushb.mixins === undefined) window.$ushb.mixins = {};
// TODO: replace AJAX URL;
$ushb.ajaxUrl = $('.us-bld').data('ajaxurl');
/**
* $ushb.Tabs class
*
* Boundable events: beforeShow, afterShow, beforeHide, afterHide
*
* @param container
* @constructor
*/
$ushb.Tabs = function(container){
this.$container = $(container);
this.$list = this.$container.find('.usof-tabs-list:first');
this.$items = this.$list.children('.usof-tabs-item');
this.$sections = this.$container.find('.usof-tabs-section');
this.items = this.$items.toArray().map($);
this.sections = this.$sections.toArray().map($);
this.active = 0;
this.items.forEach(function($elm, index){
$elm.on('click', this.open.bind(this, index));
}.bind(this));
};
$.extend($ushb.Tabs.prototype, $usof.mixins.Events, {
open: function(index){
if (index == this.active || this.sections[index] == undefined) return;
if (this.sections[this.active] !== undefined) {
this.trigger('beforeHide', this.active, this.sections[this.active], this.items[this.active]);
this.sections[this.active].hide();
this.items[this.active].removeClass('active');
this.trigger('afterHide', this.active, this.sections[this.active], this.items[this.active]);
}
this.trigger('beforeShow', index, this.sections[index], this.items[index]);
this.sections[index].show();
this.items[index].addClass('active');
this.trigger('afterShow', index, this.sections[index], this.items[index]);
this.active = index;
}
});
/**
* $ushb.EForm class
* @param container
* @constructor
*/
$ushb.EForm = function(container){
this.$container = $(container);
this.$tabs = this.$container.find('.usof-tabs');
if (this.$tabs.length) {
this.tabs = new $ushb.Tabs(this.$tabs);
}
this.initFields(this.$container);
};
$.extend($ushb.EForm.prototype, $usof.mixins.Fieldset);
/**
* $ushb.Elist class: A popup with elements list to choose from. Behaves as a singleton.
* Boundable events: beforeShow, afterShow, beforeHide, afterHide, select
* @constructor
*/
$ushb.EList = function(){
if ($ushb.elist !== undefined) return $ushb.elist;
this.$container = $('.us-bld-window.for_adding');
if (this.$container.length > 0) {
this.$container.appendTo($(document.body));
this.init();
}
};
$.extend($ushb.EList.prototype, $usof.mixins.Events, {
init: function(){
this.$closer = this.$container.find('.us-bld-window-closer');
this.$list = this.$container.find('.us-bld-window-list');
this._events = {
select: function(event){
var $item = $(event.target).closest('.us-bld-window-item');
this.hide();
this.trigger('select', $item.data('name'));
}.bind(this),
hide: this.hide.bind(this)
};
this.$closer.on('click', this._events.hide);
this.$list.on('click', '.us-bld-window-item', this._events.select);
},
show: function(){
if (this.$container.length == 0) {
// Loading elements list html via ajax
$.ajax({
type: 'post',
url: $ushb.ajaxUrl,
data: {
action: 'ushb_get_elist_html'
},
success: function(html){
this.$container = $(html).css('display', 'none').appendTo($(document.body));
this.init();
this.show();
}.bind(this)
});
return;
}
this.trigger('beforeShow');
this.$container.css('display', 'block');
this.trigger('afterShow');
},
hide: function(){
this.trigger('beforeHide');
this.$container.css('display', 'none');
this.trigger('afterHide');
}
});
// Singleton instance
$ushb.elist = new $ushb.EList;
/**
* $ushb.EBuilder class: A popup with loadable elements forms
* Boundable events: beforeShow, afterShow, beforeHide, afterHide, save
* @constructor
*/
$ushb.EBuilder = function(){
this.$container = $('.us-bld-window.for_editing');
this.loaded = false;
if (this.$container.length != 0) {
this.$container.appendTo($(document.body));
this.init();
}
};
$.extend($ushb.EBuilder.prototype, $usof.mixins.Events, {
init: function(){
this.$title = this.$container.find('.us-bld-window-title');
this.titles = this.$title[0].onclick() || {};
this.$title.removeAttr('onclick');
this.$closer = this.$container.find('.us-bld-window-closer, .us-bld-window-btn.for_close');
this.$header = this.$container.find('.us-bld-window-header');
// EForm containers and class instances
this.$eforms = {};
this.eforms = {};
// Set of default values for each elements form
this.defaults = {};
this.$container.find('.usof-form').each(function(index, eform){
var $eform = $(eform).css('display', 'none'),
name = $eform.usMod('for');
this.$eforms[name] = $eform;
}.bind(this));
this.$btnSave = this.$container.find('.us-bld-window-btn.for_save');
// Actve element
this.active = false;
this._events = {
hide: this.hide.bind(this),
save: this.save.bind(this)
};
this.$closer.on('click', this._events.hide);
this.$btnSave.on('click', this._events.save);
},
/**
* Show element form for a specified element name and initial values
* @param {String} name
* @param {Object} values
*/
show: function(name, values){
if (this.$container.css('display') == 'block') {
// If some other form is already shown, hiding it before proceeding
this.hide();
}
if (!this.loaded) {
this.$title.html(this.titles[name] || '');
this.$container.css('display', 'block');
// Loading ebuilder and initial form's html
$.ajax({
type: 'post',
url: $ushb.ajaxUrl,
data: {
action: 'ushb_get_ebuilder_html'
},
success: function(html){
if (html == '') return;
// Removing additionally appended assets
var regexp = /(\<link rel=\'stylesheet\' id=\'([^\']+)\'[^\>]+?\>)|(\<style type\=\"text\/css\"\>([^\<]*)\<\/style\>)|(\<script type=\'text\/javascript\' src=\'([^\']+)\'\><\/script\>)|(\<script type\=\'text\/javascript\'\>([^`]*?)\<\/script\>)/g;
html = html.replace(regexp, '');
this.$container.remove();
this.$container = $(html).css('display', 'none').addClass('loaded').appendTo($(document.body));
this.loaded = true;
this.init();
this.show(name, values);
}.bind(this)
});
return;
}
if (this.eforms[name] === undefined) {
// Initializing EForm on the first show
if (this.$eforms[name] === undefined) return;
this.eforms[name] = new $ushb.EForm(this.$eforms[name]);
this.defaults[name] = this.eforms[name].getValues();
}
// Filling missing values with defaults
values = $.extend({}, this.defaults[name], values);
this.eforms[name].setValues(values);
if (this.eforms[name].tabs !== undefined) {
this.eforms[name].tabs.$list.appendTo(this.$header);
this.eforms[name].tabs.open(0);
}
this.$container.toggleClass('with_tabs', this.eforms[name].tabs !== undefined);
this.$eforms[name].css('display', 'block');
this.$title.html(this.titles[name] || '');
this.active = name;
this.trigger('beforeShow');
this.$container.css('display', 'block');
this.trigger('afterShow');
},
hide: function(){
this.trigger('beforeHide');
this.$container.css('display', 'none');
if (this.$eforms[this.active] !== undefined) this.$eforms[this.active].css('display', 'none');
this.trigger('afterHide');
if (this.eforms[this.active].tabs !== undefined) {
this.eforms[this.active].tabs.$list.prependTo(this.eforms[this.active].$tabs);
}
},
/**
* Get values of the active form
* @return {Object}
*/
getValues: function(){
return (this.eforms[this.active] !== undefined) ? this.eforms[this.active].getValues() : {};
},
/**
* Get default values of the active form
* @return {Object}
*/
getDefaults: function(){
return (this.defaults[this.active] || {});
},
save: function(){
this.hide();
this.trigger('save', this.getValues(), this.getDefaults());
}
});
// Singletone instance
$ushb.ebuilder = new $ushb.EBuilder;
/**
* $ushb.ExportImport class: a popup with Export/Import dialog
* Boundable events: beforeShow, afterShow, beforeHide, afterHide, import
* @constructor
*/
$ushb.ExportImport = function(){
this.$body = $(document.body);
this.$container = $('.us-bld-window.for_export_import');
if (this.$container.length != 0) {
this.$container.appendTo(this.$body);
this.init();
}
};
$.extend($ushb.ExportImport.prototype, $usof.mixins.Events, {
init: function(){
this.$closer = this.$container.find('.us-bld-window-closer');
this.$closeButton = this.$container.find('.us-bld-window-btn.for_close');
this.$importButton = this.$container.find('.us-bld-window-btn.for_save');
this.$row = this.$container.find('.usof-form-row').first();
this.$rowState = this.$row.find('.usof-form-row-state');
this.$textarea = this.$row.find('textarea');
this.error = false;
this._events = {
import: function(event){
var data = this.$textarea.val();
if (data.charAt(0) == '{') {
try {
data = JSON.parse(data);
if (data) {
this.trigger('import', 'import', data);
this.hide();
}
} catch (error) {
this.error = true;
}
} else {
this.error = true;
}
if (this.error) {
this.$row.addClass('validate_error');
}
}.bind(this),
hide: this.hide.bind(this)
};
this.$closer.on('click', this._events.hide);
this.$closeButton.on('click', this._events.hide);
this.$importButton.on('click', this._events.import);
},
show: function(value){
this.$textarea.val(value);
this.trigger('beforeShow');
this.$container.css('display', 'block');
this.trigger('afterShow');
},
hide: function(){
this.trigger('beforeHide');
this.$row.removeClass('validate_error');
this.$container.css('display', 'none');
this.trigger('afterHide');
}
});
// Singletone instance
$ushb.exportimport = new $ushb.ExportImport;
/**
* $ushb.HTemplates class: a popup with header templates
* Boundable events: beforeShow, afterShow, beforeHide, afterHide, select
* @constructor
*/
$ushb.HTemplates = function(){
this.$body = $(document.body);
this.$container = $('.us-bld-window.for_templates');
this.loaded = false;
if (this.$container.length != 0) {
this.$container.appendTo(this.$body);
this.init();
}
};
$.extend($ushb.HTemplates.prototype, $usof.mixins.Events, {
init: function(){
this.$closer = this.$container.find('.us-bld-window-closer');
this.$list = this.$container.find('.us-bld-window-list');
this._events = {
select: function(event){
var $item = $(event.target).closest('.us-bld-window-item');
if ($ushb.instance.value.data && Object.keys($ushb.instance.value.data).length && !confirm($ushb.instance.translations['template_replace_confirm'])) return;
this.hide();
var data = $item.find('.us-bld-window-item-data')[0].onclick();
this.trigger('select', $item.data('name'), data);
}.bind(this),
hide: this.hide.bind(this)
};
this.$closer.on('click', this._events.hide);
this.$list.on('click', '.us-bld-window-item', this._events.select);
},
show: function(){
if (!this.loaded) {
this.$container.css('display', 'block');
// Loading elements list html via ajax
$.ajax({
type: 'post',
url: $ushb.ajaxUrl,
data: {
action: 'ushb_get_htemplates_html'
},
success: function(html){
this.$container.remove();
this.$container = $(html).css('display', 'none').addClass('loaded').appendTo($(document.body));
this.loaded = true;
this.init();
this.show();
}.bind(this)
});
return;
}
this.trigger('beforeShow');
this.$container.css('display', 'block');
this.$body.addClass('us-popup');
this.trigger('afterShow');
},
hide: function(){
this.trigger('beforeHide');
this.$body.removeClass('us-popup');
this.$container.css('display', 'none');
this.trigger('afterHide');
}
});
// Singletone instance
$ushb.htemplates = new $ushb.HTemplates;
/**
* Side settings
*/
var HBOptions = function(container){
this.$container = $(container);
this.$sections = this.$container.find('.us-bld-options-section');
this.$sections.not('.active').children('.us-bld-options-section-content').slideUp();
this.$container.find('.us-bld-options-section-title').click(function(event){
var $parentSection = $(event.target).parent();
if ($parentSection.hasClass('active')) return;
var $previousActive = this.$sections.filter('.active');
this.fireFieldEvent($previousActive, 'beforeHide');
$previousActive.removeClass('active').children('.us-bld-options-section-content').slideUp(function(){
this.fireFieldEvent($previousActive, 'afterHide');
}.bind(this));
this.fireFieldEvent($parentSection, 'beforeShow');
$parentSection.addClass('active').children('.us-bld-options-section-content').slideDown(function(){
this.fireFieldEvent($parentSection, 'afterShow');
}.bind(this));
}.bind(this));
this.$container.find('.usof-subform-row, .usof-subform-wrapper').each(function(index, elm){
elm.className = elm.className.replace('usof-subform-', 'usof-form-');
});
this.initFields(this.$container);
var activeSection = this.$sections.filter('.active');
this.fireFieldEvent(activeSection, 'beforeShow');
this.fireFieldEvent(activeSection, 'afterShow');
};
$.extend(HBOptions.prototype, $usof.mixins.Fieldset, {
getValue: function(id){
if (id == 'state') return $ushb.instance.state;
if (this.fields[id] === undefined) return undefined;
return this.fields[id].getValue();
}
});
/**
* USOF Field: Header Builder
*/
$usof.field['header_builder'] = {
init: function(options){
$ushb.instance = this;
this.parentInit(options);
this.$container = this.$row.find('.us-bld');
this.$workspace = this.$container.find('.us-bld-workspace');
this.$body = $(document.body);
this.$window = $(window);
this.$editor = $('.us-bld-editor');
this.$dragshadow = $('<div class="us-bld-editor-dragshadow"></div>');
this.$rows = this.$container.find('.us-bld-editor-row');
this.$stateTabs = this.$container.find('.us-bld-state');
this.params = this.$container.find('.us-bld-params')[0].onclick() || {};
this.elmsDefaults = this.$container.find('.us-bld-defaults')[0].onclick() || {};
this.translations = this.$container.find('.us-bld-translations')[0].onclick() || {};
this.value = this.$container.find('.us-bld-value')[0].onclick() || {};
this.states = ['default', 'tablets', 'mobiles'];
this.state = 'default';
this._events = {
_maybeDragMove: this._maybeDragMove.bind(this),
_dragMove: this._dragMove.bind(this),
_dragEnd: this._dragEnd.bind(this)
};
this.$places = {hidden: this.$editor.find('.us-bld-editor-row.for_hidden > .us-bld-editor-row-h')};
this.$editor.find('.us-bld-editor-cell').each(function(index, cell){
var $cell = $(cell);
this.$places[$cell.parent().parent().usMod('at') + '_' + $cell.usMod('at')] = $cell;
}.bind(this));
this.$wrappers = {};
this.$editor.find('.us-bld-editor-wrapper').each(function(index, wrapper){
var $wrapper = $(wrapper);
this.$wrappers[$wrapper.data('id')] = $wrapper;
}.bind(this));
this.$elms = {};
this.$editor.find('.us-bld-editor-elm').each(function(index, elm){
var $elm = $(elm);
this.$elms[$elm.data('id')] = $elm;
}.bind(this));
this.$templatesBtn = $('.usof-control.for_templates').on('click', this._showTemplatesBtnClick.bind(this));
$('.usof-control.for_import').on('click', this._showExportImportBtnClick.bind(this));
// Elements modification events
this.$container.on('click', '.us-bld-editor-add, .us-bld-editor-control.type_add, .us-bld-editor-wrapper-content:empty', this._addBtnClick.bind(this));
this.$container.on('click', '.us-bld-editor-control.type_edit', this._editBtnClick.bind(this));
this.$container.on('click', '.us-bld-editor-control.type_clone', this._cloneBtnClick.bind(this));
this.$container.on('mousedown', '.us-bld-editor-elm, .us-bld-editor-wrapper', this._dragStart.bind(this));
this.$container.on('click', '.us-bld-editor-control.type_delete', this._deleteBtnClick.bind(this));
// Preventing browser native drag event
this.$container.on('dragstart', function(event){
event.preventDefault();
});
// Options that has no responsive values
this.sharedOptions = ['top_fullwidth', 'middle_fullwidth', 'bottom_fullwidth'];
this.sideOptions = new HBOptions(this.$container.find('.us-bld-options:first'));
$.each(this.sideOptions.fields, function(fieldId, field){
field.on('change', this._optionChanged.bind(this));
}.bind(this));
this.sideOptions.fields.orientation.$row.find('label').on('click', function(event){
if (!confirm(this.translations['orientation_change_confirm'])) event.preventDefault();
}.bind(this));
// State togglers
this.$stateTabs.on('click', function(event){
var $stateTab = $(event.target),
newState = $stateTab.usMod('for');
this.setState(newState);
}.bind(this));
// Highlight rows on side options hover
this.$container.find('.us-bld-options-section').each(function(index, section){
var $section = $(section),
id = $section.data('id');
$section.hover(function(){
this.$editor.addClass('highlight_' + id);
}.bind(this), function(){
this.$editor.removeClass('highlight_' + id);
}.bind(this));
}.bind(this));
// Showing templates for empty case
if (!this.value.data || !Object.keys(this.value.data).length) this.$templatesBtn.addClass('start');
},
setValue: function(value){
// Fixing missing datas
if (!value) value = {};
if (value.data === undefined) value.data = {};
if (value.default === undefined) value.default = {};
if (value.default.options === undefined) value.default.options = {};
this.value = $.extend({}, value);
this.setState('default', true);
},
getValue: function(){
return this.value;
},
/**
* Buttons events
*/
_addBtnClick: function(event){
var $target = $(event.target),
placeType, place;
if ($target.hasClass('us-bld-editor-add')) {
var $cell = $target.closest('.us-bld-editor-cell');
place = $cell.parent().parent().usMod('at') + '_' + $cell.usMod('at');
placeType = 'cell';
} else {
place = $target.closest('.us-bld-editor-wrapper').data('id');
placeType = place.split(':')[0];
}
$ushb.elist.off('beforeShow').on('beforeShow', function(){
$ushb.elist.$container
.toggleClass('hide_search', this.value.data['search:1'] !== undefined)
.toggleClass('hide_cart', this.value.data['cart:1'] !== undefined)
.usMod('orientation', this.value[this.state].options.orientation)
.usMod('addto', placeType);
}.bind(this));
$ushb.elist.off('select').on('select', function(elist, type){
var elmId = this.createElement(place, type);
// Opening editing form for standard elements
if (type.substr(1) != 'wrapper') {
this.$elms[elmId].find('.us-bld-editor-control.type_edit').trigger('click');
}
}.bind(this));
$ushb.elist.show();
},
_editBtnClick: function(event){
var $target = $(event.target),
$elm = $target.closest('.us-bld-editor-elm, .us-bld-editor-wrapper'),
id = $elm.data('id'),
type = id.split(':')[0],
values = (this.value.data[id] || {});
$ushb.ebuilder.off('save').on('save', function(ebuilder, values, defaults){
this.updateElement(id, values);
}.bind(this));
$ushb.ebuilder.show(type, values);
},
_cloneBtnClick: function(event){
var $target = $(event.target),
$elm = $target.closest('.us-bld-editor-elm, .us-bld-editor-wrapper'),
id = $elm.data('id'),
type = id.split(':')[0];
// createElement: function(place, type, index, values){
var newId = this.createElement('top_left', type, undefined, this.value.data[id] || {});
this.states.forEach(function(state){
this.moveElement(newId, id, 'after', state);
}.bind(this));
},
_deleteBtnClick: function(event){
var $target = $(event.target);
if (!confirm(this.translations['element_delete_confirm'])) return;
var id = $target.parent().parent().data('id');
this.deleteElement(id);
},
_showTemplatesBtnClick: function(event){
if (event !== undefined) event.preventDefault();
$ushb.htemplates.off('select').on('select', function(dialog, name, data){
this.setValue(data);
this.trigger('change', this.value);
}.bind(this));
$ushb.htemplates.show();
this.$templatesBtn.removeClass('start');
},
_showExportImportBtnClick: function(event){
event.preventDefault();
$ushb.exportimport.off('import').on('import', function(dialog, name, data){
this.setValue(data);
this.trigger('change', this.value);
}.bind(this));
$ushb.exportimport.show(JSON.stringify(this.getValue()));
},
// Drag'n'drop functions
_dragStart: function(event){
event.stopPropagation();
this.$draggedElm = $(event.target).closest('.us-bld-editor-elm, .us-bld-editor-wrapper');
this.elmType = this.$draggedElm.data('id').split(':')[0];
this.detached = false;
this._updateBlindSpot(event);
this.elmPointerOffset = [parseInt(event.pageX), parseInt(event.pageY)];
this.$body.on('mousemove', this._events._maybeDragMove);
this.$window.on('mouseup', this._events._dragEnd);
},
_updateBlindSpot: function(event){
this.blindSpot = [event.pageX, event.pageY];
},
_isInBlindSpot: function(event){
return Math.abs(event.pageX - this.blindSpot[0]) <= 20 && Math.abs(event.pageY - this.blindSpot[1]) <= 20;
},
_maybeDragMove: function(event){
event.stopPropagation();
if (this._isInBlindSpot(event)) return;
this.$body.off('mousemove', this._events._maybeDragMove);
this._detach();
this.$body.on('mousemove', this._events._dragMove);
},
_dragMove: function(event){
event.stopPropagation();
this.$draggedElm.css({
left: event.pageX - this.elmPointerOffset[0],
top: event.pageY - this.elmPointerOffset[1]
});
if (this._isInBlindSpot(event)) return;
var elm = event.target;
// Checking two levels up
for (var level = 0; level <= 2; level++, elm = elm.parentNode) {
if (this._isShadow(elm)) return;
// Workaround for IE9-10 that don't support css pointer-events property
if (this._hasClass(elm, 'detached')) {
this.$draggedElm.detach();
break;
}
var parentType;
if (this._isSortable(elm)) {
parentType = this._isWrapperContent(elm.parentNode) ? ($(elm).parent().parent().usMod('type')[0] + 'wrapper') : 'cell';
if (!this._canBeDropped(this.elmType, parentType)) break;
// Dropping element before or after sortables based on their relative position in DOM
var nextElm = elm.previousSibling,
shadowAtLeft = false;
while (nextElm) {
if (nextElm == this.$dragshadow[0]) {
shadowAtLeft = true;
break;
}
nextElm = nextElm.previousSibling;
}
this.$dragshadow[shadowAtLeft ? 'insertAfter' : 'insertBefore'](elm);
this._dragDrop(event);
break;
} else if (this._isWrapperContent(elm)) {
if ($.contains(elm, this.$dragshadow[0])) break;
parentType = $(elm).parent().usMod('type')[0] + 'wrapper';
if (!this._canBeDropped(this.elmType, parentType)) break;
// Cannot drop a wrapper to the wrapper of the same type
this.$dragshadow.appendTo(elm);
this._dragDrop(event);
break;
} else if (this._isControls(elm)) {
if (!this._canBeDropped(this.elmType, 'cell')) break;
// Always dropping element before controls
this.$dragshadow.insertBefore(elm);
this._dragDrop(event);
break;
} else if (this._hasClass(elm, 'us-bld-editor-cell')) {
if (!this._canBeDropped(this.elmType, 'cell')) break;
// If not already in this cell, moving to it
var $shadowCell = this.$dragshadow.closest('.us-bld-editor-cell');
if ($shadowCell.length == 0 || $shadowCell[0] != elm) {
this.$dragshadow.insertBefore($(elm).find('.us-bld-editor-add'));
this._dragDrop(event);
}
break;
} else if (this._hasClass(elm, 'us-bld-editor-row for_hidden')) {
// Moving to hidden elements container directly
if (!this.$dragshadow.closest('.us-bld-editor-row').hasClass('for_hidden')) {
this.$dragshadow.appendTo($(elm).children('.us-bld-editor-row-h'));
this._dragDrop(event);
}
break;
}
}
},
_detach: function(event){
var offset = this.$draggedElm.offset();
this.elmPointerOffset[0] -= offset.left;
this.elmPointerOffset[1] -= offset.top;
this.$dragshadow.css({
width: this.$draggedElm.outerWidth(),
height: this.$draggedElm.outerHeight()
}).insertBefore(this.$draggedElm);
this.$draggedElm.addClass('detached').css({
position: 'absolute',
'pointer-events': 'none',
zIndex: 10000,
width: this.$draggedElm.width(),
height: this.$draggedElm.height()
}).css(offset).appendTo(this.$body);
this.$editor.addClass('dragstarted');
this.detached = true;
},
/**
* Complete drop
* @param event
*/
_dragDrop: function(event){
this.$container.find('.us-bld-editor-wrapper').removeClass('empty').find('.us-bld-editor-wrapper-content:empty').parent().addClass('empty');
this._updateBlindSpot(event);
},
_dragEnd: function(event){
this.$body.off('mousemove', this._events._maybeDragMove).off('mousemove', this._events._dragMove);
this.$window.off('mouseup', this._events._dragEnd);
if (this.detached) {
this.$draggedElm.removeClass('detached').removeAttr('style').insertBefore(this.$dragshadow);
this.$dragshadow.detach();
this.$editor.removeClass('dragstarted');
// Getting the new element position and performing the actual drag
var elmId = this.$draggedElm.data('id'),
$prev = this.$draggedElm.prev();
if ($prev.length == 0) {
var $parent = this.$draggedElm.parent().closest('.us-bld-editor-cell, .us-bld-editor-wrapper, .us-bld-editor-row.for_hidden'),
place = 'hidden';
if ($parent.hasClass('us-bld-editor-cell')) {
place = $parent.parent().parent().usMod('at') + '_' + $parent.usMod('at');
} else if ($parent.hasClass('us-bld-editor-wrapper')) {
place = $parent.data('id');
}
this.moveElement(elmId, place, 'first_child')
} else {
this.moveElement(elmId, $prev.data('id'), 'after');
}
}
},
_hasClass: function(elm, cls){
return (' ' + elm.className + ' ').indexOf(' ' + cls + ' ') > -1;
},
_isShadow: function(elm){
return this._hasClass(elm, 'us-bld-editor-dragshadow');
},
_isSortable: function(elm){
return this._hasClass(elm, 'us-bld-editor-elm') || this._hasClass(elm, 'us-bld-editor-wrapper');
},
_isWrapperContent: function(elm){
return this._hasClass(elm, 'us-bld-editor-wrapper-content');
},
_isControls: function(elm){
return this._hasClass(elm, 'us-bld-editor-add');
},
_canBeDropped: function(elmType, placeType){
if (elmType == 'hwrapper') {
if (placeType == 'hwrapper') return false;
if (placeType == 'cell' && this.value[this.state].options.orientation == 'hor') return false;
}
else if (elmType == 'vwrapper') {
if (placeType == 'vwrapper') return false;
if (placeType == 'cell' && this.value[this.state].options.orientation == 'ver') return false;
}
return true;
},
setState: function(newState, force){
if (newState == this.state && !force) return;
// Changing the active tab setting
this.$stateTabs.removeClass('active').filter('.for_' + newState).addClass('active');
this.$workspace.usMod('for', newState);
this.state = newState;
// Changing side options view
if (this.value[newState].options !== undefined) {
var options = $.extend({}, this.value[newState].options);
if (newState != 'default') {
for (var i = 0; i < this.sharedOptions.length; i++) {
options[this.sharedOptions[i]] = this.value.default.options[this.sharedOptions[i]];
}
}
this.setOptions(options);
}
this.renderLayout();
},
/**
* Create element at the end of the specified place
* @param {String} place Place Cell name or wrapper ID
* @param {String} type Element type Element type
* @param {Number} [index] Element index, starting from 1. If not set will be generated automatically.
* @param {Object} [values] Element values
* @returns {String} New element ID
* @private
*/
createElement: function(place, type, index, values){
if (index === undefined) {
// If index is not defined generating a spare one
index = 1;
while (this.value.data[type + ':' + index] !== undefined) index++;
}
var id = type + ':' + index;
for (var i = 0, state = this.states[i]; i < this.states.length; state = this.states[++i]) {
if (this.value[state] === undefined) this.value[state] = {};
if (this.value[state].layout === undefined) this.value[state].layout = {};
if (this.value[state].layout[place] === undefined) this.value[state].layout[place] = [];
this.value[state].layout[place].push(id);
if (type.substr(1) == 'wrapper') this.value[state].layout[id] = [];
}
this.value.data[id] = $.extend({}, this.elmsDefaults[type] || {}, values || {});
this.renderLayout();
this.trigger('change', this.value);
return id;
},
/**
* Move a specified element to a specified place
* @param {String} id Element ID
* @param {String} place Cell name or element ID
* @param {String} [position] Relation to place: "last_child" / "first_child" / "before" / "after"
* @param {String} [state] If not specified, the current active state will be used
* @private
*/
moveElement: function(id, place, position, state){
if (this.value.data[id] === undefined) return;
position = position || 'last_child';
state = state || this.state;
if (this.value[state] === undefined) this.value[state] = {};
if (this.value[state].layout === undefined) this.value[state].layout = {};
// Cropping out the element from the previous place ...
var plc, elmPos;
for (plc in this.value[state].layout) {
if (!this.value[state].layout.hasOwnProperty(plc)) continue;
elmPos = this.value[state].layout[plc].indexOf(id);
if (elmPos != -1) {
this.value[state].layout[plc].splice(elmPos, 1);
break;
}
}
// ... and placing it to the new one
if (position == 'first_child' || position == 'last_child') {
if (this.value[state].layout[place] === undefined) this.value[state].layout[place] = [];
this.value[state].layout[place][(position == 'first_child') ? 'unshift' : 'push'](id);
} else if (position == 'before' || position == 'after') {
for (plc in this.value[state].layout) {
if (!this.value[state].layout.hasOwnProperty(plc)) continue;
elmPos = this.value[state].layout[plc].indexOf(place);
if (elmPos != -1) {
this.value[state].layout[plc].splice(elmPos + ((position == 'after') ? 1 : 0), 0, id);
break;
}
}
}
this.renderLayout();
this.trigger('change', this.value);
},
/**
* Update the specified element's values
* @param {String} id Element ID
* @param {Object} values Element values
* @private
*/
updateElement: function(id, values){
var type = id.split(':')[0];
this.value.data[id] = $.extend({}, this.elmsDefaults[type] || {}, values);
var $elm = this[(type.substr(1) == 'wrapper') ? '$wrappers' : '$elms'][id];
if ($elm !== undefined) {
this._updateElementPlaceholder($elm, id, this.value.data[id]);
}
this.trigger('change', this.value);
},
/**
* Delete the specified element
* @param {String} id Element ID
* @private
*/
deleteElement: function(id){
var type = id.split(':')[0];
for (var i = 0, state = this.states[i]; i < this.states.length; state = this.states[++i]) {
if (this.value[state] === undefined) this.value[state] = {};
if (this.value[state].layout === undefined) this.value[state].layout = {};
if (this.value[state].layout.hidden === undefined) this.value[state].layout.hidden = [];
if (id.substr(1, 7) == 'wrapper' && this.value[state].layout[id] !== undefined) {
// Moving wrapper's inner elements to hidden block
this.value[state].layout.hidden = this.value[state].layout.hidden.concat(this.value[state].layout[id]);
delete this.value[state].layout[id];
}
for (var plc in this.value[state].layout) {
if (!this.value[state].layout.hasOwnProperty(plc)) continue;
var elmPos = this.value[state].layout[plc].indexOf(id);
if (elmPos != -1) {
this.value[state].layout[plc].splice(elmPos, 1);
break;
}
}
}
if (this.value.data[id] !== undefined) delete this.value.data[id];
this.renderLayout();
this.trigger('change', this.value);
},
/**
* Load attachments withing the given jQuery DOM object
* @param {jQuery} $html
*/
_loadAttachments: function($html){
$html.find('img[data-wpattachment]').each(function(index, elm){
var $elm = $(elm),
id = $elm.data('wpattachment'),
attachment = wp.media.attachment(id);
if (!attachment || !attachment.attributes.id) return '';
var renderAttachmentImage = function(){
var src = attachment.attributes.url;
if (attachment.attributes.sizes !== undefined) {
var size = (attachment.attributes.sizes.medium !== undefined) ? 'medium' : 'full';
src = attachment.attributes.sizes[size].url;
}
$elm.attr('src', src).removeAttr('data-wpattachment');
};
if (attachment.attributes.url !== undefined) {
renderAttachmentImage();
} else {
// Loading missing data via ajax
attachment.fetch({success: renderAttachmentImage});
}
}.bind(this));
},
/**
* Create a base part of elements DOM placeholder: the one that doesn't depend on values
* @param {String} id
* @returns {jQuery} Created (but not placed to document) placeholder's DOM element
* @private
*/
_createElementPlaceholderBase: function(id){
var type = id.split(':')[0],
html = '';
if (type.substr(1) == 'wrapper') {
// Wrappers
html += '<div class="us-bld-editor-wrapper type_' + ((type == 'hwrapper') ? 'horizontal' : 'vertical') + ' empty">';
html += '<div class="us-bld-editor-wrapper-content"></div>';
html += '<div class="us-bld-editor-wrapper-controls">';
html += '<a title="' + this.translations['add_element'] + '" class="us-bld-editor-control type_add" href="javascript:void(0)"></a>';
html += '<a title="' + this.translations['edit_wrapper'] + '" class="us-bld-editor-control type_edit" href="javascript:void(0)"></a>';
html += '<a title="' + this.translations['delete_wrapper'] + '" class="us-bld-editor-control type_delete" href="javascript:void(0)"></a>';
html += '</div>';
html += '</div>';
this.$wrappers[id] = $(html).data('id', id);
return this.$wrappers[id];
} else {
// Standard elements
html += '<div class="us-bld-editor-elm type_' + type + '">';
html += '<div class="us-bld-editor-elm-content"></div>';
html += '<div class="us-bld-editor-elm-controls">';
html += '<a href="javascript:void(0)" class="us-bld-editor-control type_edit" title="' + this.translations['edit_element'] + '"></a>';
html += '<a href="javascript:void(0)" class="us-bld-editor-control type_clone" title="' + this.translations['clone_element'] + '"></a>';
html += '<a href="javascript:void(0)" class="us-bld-editor-control type_delete" title="' + this.translations['delete_element'] + '"></a>';
html += '</div>';
html += '</div>';
this.$elms[id] = $(html).data('id', id);
return this.$elms[id];
}
},
/**
* Update element DOM placeholder with the current values
* @param {jQuery} $elm
* @param {String} id
* @param {Object} values
* @private
*/
_updateElementPlaceholder: function($elm, id, values){
if (id.substr(1, 7) == 'wrapper') return;
values = $.extend({}, this.elmsDefaults[type] || {}, values || {});
var type = id.split(':')[0],
$content = $elm.find('.us-bld-editor-elm-content:first'),
content = '';
if (type == 'text' && (values.text || values.icon)) {
if (values.icon) {
content += $usof.instance.prepareIconTag(values.icon);
}
// Strip tags
content += values.text.replace(/(\r\n|\n|\r)+/gm, ' ').replace(/<\/?([^>]+)?>/gi, '');
} else if (type == 'image') {
if (values.img) {
var imgValue = values.img;
if (imgValue.indexOf('|') != -1) imgValue = imgValue.substr(0, imgValue.indexOf('|'));
if ($.isNumeric(imgValue)) {
content += '<img src="" data-wpattachment="' + imgValue + '" />';
} else {
content += '<img src="' + imgValue + '" />';
}
} else {
content += '<i class="fa fa-image"></i>';
}
} else if (type == 'menu') {
if (values.source) {
content += this.params.navMenus[values.source] || values.source;
} else {
content += this.translations['menu'];
}
} else if (type == 'additional_menu') {
if (values.source) {
content += this.params.navMenus[values.source] || values.source;
} else {
content += this.translations['additional_menu'];
}
} else if (type == 'search' && values.text) {
if (values.icon) {
content += $usof.instance.prepareIconTag(values.icon);
}
content += values.text.replace(/(\r\n|\n|\r)+/gm, ' ').replace(/<\/?([^>]+)?>/gi, '');
} else if (type == 'dropdown') {
if (values.source == 'wpml') {
content += 'WPML';
} else if (values.source == 'polylang') {
content += 'Polylang';
} else if (values.source == 'qtranslate') {
content += 'qTranslate X';
} else {
content += values.link_title || this.translations['dropdown'];
}
} else if (type == 'socials') {
var socialsHtml = '';
$.each(values['items'], function(key, value){
if (value['type'] == 'custom') {
var icon_value = value['icon'].trim().split('|'),
icon_set = icon_value[0],
icon_name = icon_value[1];
if ( icon_name != '' ) {
if ( icon_set == 'material' ) {
socialsHtml += '<i class="material-icons">'+icon_name+'</i>';
} else {
socialsHtml += '<i class="'+icon_set+' fa-'+icon_name+'"></i>';
}
}
} else {
socialsHtml += '<i class="fab fa-' + value['type'] + '"></i>';
}
});
if (values.custom_icon && values.custom_url) {
socialsHtml += $usof.instance.prepareIconTag(values.custom_icon);
}
content += socialsHtml || this.translations['social_links'];
} else if (type == 'btn') {
if (values.icon) {
content += $usof.instance.prepareIconTag(values.icon);
}
if (values.label) {
content += values.label.replace(/(\r\n|\n|\r)+/gm, ' ').replace(/<\/?([^>]+)?>/gi, '');
} else {
content += this.translations['button'];
}
} else if (type == 'html') {
content += 'HTML';
} else if (type == 'cart') {
if (values.icon) {
content += $usof.instance.prepareIconTag(values.icon);
}
content += this.translations['cart'];
} else {
content += type[0].toUpperCase() + type.substr(1);
}
$content.html(content);
this._loadAttachments($content);
},
/**
* Create DOM placeholder element for the specified header builder element / wrapper
* @param {String} id Element ID
* @param {Object} [values]
* @returns {jQuery} Created (but not yet placed to document) jQuery object with the element's DOMElement
* @private
*/
_createElementPlaceholder: function(id, values){
var type = id.split(':')[0],
$elm = this._createElementPlaceholderBase(id);
this._updateElementPlaceholder($elm, id, values);
return $elm;
},
/**
* Delete DOM placeholder for the specified header element / wrapper
* @param {String} id
* @private
*/
_removeElementPlaceholder: function(id){
var container = (id.substr(1, 7) == 'wrapper') ? '$wrappers' : '$elms';
if (this[container][id] === undefined) return;
this[container][id].remove();
delete this[container][id];
},
/**
* Render current layout based on current value and state
*/
renderLayout: function(){
// Making sure the provided data is consistent
if (this.value.data === undefined) this.value.data = {};
if (this.value[this.state].layout === undefined) this.value[this.state].layout = {};
if (this.value[this.state].layout.hidden === undefined) this.value[this.state].layout.hidden = [];
var elmsInNextLayout = [],
plc, i, elmId;
for (plc in this.value[this.state].layout) {
if (!this.value[this.state].layout.hasOwnProperty(plc)) continue;
for (i = 0; i < this.value[this.state].layout[plc].length; i++) {
var id = this.value[this.state].layout[plc][i],
type = id.split(':')[0];
if (this.value.data[id] === undefined) this.value.data[id] = $.extend({}, this.elmsDefaults[type] || {});
elmsInNextLayout.push(this.value[this.state].layout[plc][i]);
}
}
for (elmId in this.value.data) {
if (!this.value.data.hasOwnProperty(elmId)) continue;
if (elmsInNextLayout.indexOf(elmId) == -1) this.value[this.state].layout.hidden.push(elmId);
}
// Retrieving the currently shown layout structure
var prevLayout = {},
parsePlace = function(place, $place){
if ($place.hasClass('us-bld-editor-wrapper')) $place = $place.children('.us-bld-editor-wrapper-content');
prevLayout[place] = [];
$place.children().each(function(index, elm){
var $elm = $(elm),
id = $elm.data('id');
if (!id) return;
prevLayout[place].push(id);
});
};
$.each(this.$places, parsePlace);
$.each(this.$wrappers, parsePlace);
// Iteratively looping through the needed structure
for (plc in this.value[this.state].layout) {
if (!this.value[this.state].layout.hasOwnProperty(plc)) continue;
if (plc.indexOf(':') != -1 && prevLayout[plc] === undefined) {
// Creating the missing wrapper
if (this.$wrappers[plc] === undefined) {
this._createElementPlaceholder(plc, this.value.data[plc]);
}
prevLayout[plc] = [];
}
var $place = (plc.indexOf(':') == -1) ? this.$places[plc] : this.$wrappers[plc].children('.us-bld-editor-wrapper-content');
for (i = 0; i < this.value[this.state].layout[plc].length; i++) {
elmId = this.value[this.state].layout[plc][i];
var $elm = this[(elmId.substr(1, 7) == 'wrapper') ? '$wrappers' : '$elms'][elmId];
if ($elm === undefined) {
$elm = this._createElementPlaceholder(elmId, this.value.data[elmId]);
}
if (prevLayout[plc][i] != elmId) {
if (i == 0) {
$elm.prependTo($place);
} else {
var prevElmId = this.value[this.state].layout[plc][i - 1],
$prevElm = this[(prevElmId.substr(1, 7) == 'wrapper') ? '$wrappers' : '$elms'][prevElmId];
$elm.insertAfter($prevElm);
}
prevLayout[plc].splice(i, 0, elmId);
}
}
}
// Removing excess elements
for (plc in prevLayout) {
if (!prevLayout.hasOwnProperty(plc))continue;
for (i = 0, elmId = prevLayout[plc][i]; i < prevLayout[plc].length; i++, elmId = prevLayout[plc][i]) {
if (this.value.data[elmId] === undefined) this._removeElementPlaceholder(elmId);
}
}
// Updating elements' placeholders contents
for (elmId in this.$elms) {
if (!this.$elms.hasOwnProperty(elmId)) continue;
this._updateElementPlaceholder(this.$elms[elmId], elmId, this.value.data[elmId]);
}
// Fixing wrappers
this.$container.find('.us-bld-editor-wrapper').removeClass('empty').find('.us-bld-editor-wrapper-content:empty').parent().addClass('empty');
},
/**
* Event that is called on manual side option change
* @param {$usof.Field} field
* @private
*/
_optionChanged: function(field){
if (this.ignoreOptionsChanges) return;
var fieldId = field.name,
value = field.getValue(),
state = ($.inArray(fieldId, this.sharedOptions) != -1) ? 'default' : this.state;
if (this.value[state] === undefined) this.value[state] = {};
if (this.value[state].options === undefined) this.value[state].options = {};
this.value[state].options[fieldId] = value;
this.renderOptions();
this.trigger('change', this.value);
},
/**
* Change side options
* @param options
*/
setOptions: function(options){
this.ignoreOptionsChanges = true;
this.sideOptions.setValues(options);
this.ignoreOptionsChanges = false;
this.renderOptions();
},
/**
* Render current options
*/
renderOptions: function(){
var prevOrientation = this.$editor.usMod('type'),
nextOrientation = this.value[this.state].options.orientation || 'hor';
if (nextOrientation != prevOrientation) {
this.$editor.usMod('type', nextOrientation);
if (nextOrientation == 'ver') {
// Moving elements from removed cells to remaining ones
if (this.value[this.state].layout.hidden === undefined) this.value[this.state].layout.hidden = [];
for (var place in this.value[this.state].layout) {
if (!this.value[this.state].layout.hasOwnProperty(place)) continue;
if (place.indexOf(':') != -1 || place == 'hidden' || place.substr(place.length - 5) == '_left') continue;
var align = place.split('_'),
newPlace = (align.length == 2) ? (align[0] + '_left') : 'hidden';
if (this.value[this.state].layout[newPlace] === undefined) this.value[this.state].layout[newPlace] = [];
this.value[this.state].layout[newPlace] = this.value[this.state].layout[newPlace].concat(this.value[this.state].layout[place]);
this.value[this.state].layout[place] = [];
}
this.renderLayout();
}
}
$.each(['top', 'bottom'], function(index, vpos){
var $row = this.$rows.filter('.at_' + vpos),
prevShown = !$row.hasClass('disabled'),
nextShown = !!parseInt(this.value[this.state].options[vpos + '_show']);
if (prevShown != nextShown) {
$row.toggleClass('disabled', !nextShown);
}
}.bind(this));
}
};
}(jQuery);
jQuery(function($){
var USHB = function(container){
this.$container = $(container);
if (!this.$container.length) return;
this.initFields(this.$container);
this.fireFieldEvent(this.$container, 'beforeShow');
this.fireFieldEvent(this.$container, 'afterShow');
// Save action
this.$saveControl = this.$container.find('.usof-control.for_save');
this.$saveBtn = this.$saveControl.find('.usof-button').on('click', this.save.bind(this));
this.$saveMessage = this.$saveControl.find('.usof-control-message');
this.valuesChanged = {};
this.saveStateTimer = null;
for (var fieldId in this.fields) {
if (!this.fields.hasOwnProperty(fieldId)) continue;
this.fields[fieldId].on('change', function(field, value){
if ($.isEmptyObject(this.valuesChanged)) {
clearTimeout(this.saveStateTimer);
this.$saveControl.usMod('status', 'notsaved');
}
this.valuesChanged[field.name] = value;
}.bind(this));
}
$(window).bind('keydown', function(event) {
if (event.ctrlKey || event.metaKey) {
if (String.fromCharCode(event.which).toLowerCase() == 's') {
event.preventDefault();
this.save();
}
}
}.bind(this));
};
$.extend(USHB.prototype, $usof.mixins.Fieldset, {
/**
* Save the new values
*/
save: function(){
if ($.isEmptyObject(this.valuesChanged)) return;
clearTimeout(this.saveStateTimer);
this.$saveMessage.html('');
this.$saveControl.usMod('status', 'loading');
$.ajax({
type: 'POST',
url: $usof.ajaxUrl,
dataType: 'json',
data: {
action: 'ushb_save',
ID: this.$container.data('id'),
post_title: this.getValue('post_title'),
post_content: JSON.stringify(this.getValue('post_content')),
_wpnonce: this.$container.find('[name="_wpnonce"]').val(),
_wp_http_referer: this.$container.find('[name="_wp_http_referer"]').val()
},
success: function(result){
if (result.success) {
this.valuesChanged = {};
this.$saveMessage.html(result.data.message);
this.$saveControl.usMod('status', 'success');
this.saveStateTimer = setTimeout(function(){
this.$saveMessage.html('');
this.$saveControl.usMod('status', 'clear');
}.bind(this), 4000);
} else {
this.$saveMessage.html(result.data.message);
this.$saveControl.usMod('status', 'error');
this.saveStateTimer = setTimeout(function(){
this.$saveMessage.html('');
this.$saveControl.usMod('status', 'notsaved');
}.bind(this), 4000);
}
}.bind(this)
});
}
});
new USHB('.usof-container.type_builder');
// Pencil icon hear the header edit
var $headerTitle = $('input[name="post_title"]'),
$headerEditIcon = $('<span class="usof-form-row-control-icon"></span>').text($headerTitle.val()).insertAfter($headerTitle);
$headerTitle.on('change keyup', function(){
$headerEditIcon.text($headerTitle.val() || $headerTitle.attr('placeholder'));
});
});