/* global ogf_repeater */
jQuery( document ).ready( function() {
const themeControls = jQuery( '#customize-theme-controls' );
/**
* This adds a new box to repeater
*/
themeControls.on( 'click', '.customizer-repeater-new-field', function() {
const parent = jQuery( this ).closest( '.customize-control' );
if ( typeof parent !== 'undefined' ) {
/* Clone the first box*/
const field = parent.find( '.customizer-repeater-general-control-repeater-container:first' ).clone( true, true );
if ( typeof field !== 'undefined' ) {
/*Show delete box button because it's not the first box*/
field.find( '#ogf-repeater-control-remove-field' ).show();
/*Remove value from text field*/
field.find( '.customizer-repeater-control' ).val( '' );
/*Append new box*/
parent.find( '.customizer-repeater-general-control-repeater-container:first' ).parent().append( field );
/*Refresh values*/
customizerRepeaterRefreshValues();
}
}
return false;
} );
themeControls.on( 'click', '#ogf-repeater-control-remove-field', function() {
const control = jQuery( this ).closest( '.customizer-repeater-general-control-repeater-container' );
if ( typeof control !== 'undefined' ) {
control.hide( 250, function() {
control.remove();
customizerRepeaterRefreshValues();
} );
}
return false;
} );
themeControls.on( 'keyup', '.customizer-repeater-control', function() {
customizerRepeaterRefreshValues();
} );
/**
* Save elements and refresh the customizer.
*/
themeControls.on( 'click', '.ogf_save_elements_button', function() {
jQuery.when( wp.customize.previewer.save() ).done( function() {
window.location.href = ogf_repeater.return_url;
} );
} );
} );
function customizerRepeaterRefreshValues() {
const entityMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
'\'': ''',
'/': '/',
};
function escapeHtml( string ) {
//noinspection JSUnresolvedFunction
string = String( string ).replace( new RegExp( '\r?\n', 'g' ), '
' );
string = String( string ).replace( /\\/g, '\' );
return String( string ).replace( /[&<>"'\/]/g, function( s ) {
return entityMap[ s ];
} );
}
jQuery( '.customizer-repeater-general-control-repeater' ).each( function() {
const values = [];
jQuery( this ).find( '.customizer-repeater-general-control-repeater-container' ).each( function() {
let label = jQuery( this ).find( '.customizer-repeater-label-control' ).val();
const description = jQuery( this ).find( '.customizer-repeater-description-control' ).val();
const selectors = jQuery( this ).find( '.customizer-repeater-selectors-control' ).val();
if ( label !== '' || description !== '' || selectors !== '' ) {
label = ( label !== '' ? label : selectors );
values.push( {
label: escapeHtml( label ),
description: escapeHtml( description ),
selectors: escapeHtml( selectors ),
} );
}
} );
jQuery( this ).find( '.customizer-repeater-collector' ).val( JSON.stringify( values ) );
jQuery( this ).find( '.customizer-repeater-collector' ).trigger( 'change' );
} );
}
// Stores the database ID of
// currently editing slider.
var LS_sliderID = 0,
// Store the indexes of currently
// selected items on the interface.
LS_activeSlideIndex = 0,
LS_activeLayerIndexSet = [0],
LS_activeLayerPageIndex = 0,
LS_activeLayerTransitionTab = 0,
LS_activeScreenType = 'desktop',
// Stores all preview items using an object
// to easily add and modify items.
//
// NOTE: it's not a jQuery collection, but a
// collection of jQuery-enabled elements.
LS_previewItems = [],
// Object references, pointing to the currently selected
// slide/layer data. These are not working copies, any
// change made will affect the main data object. This makes
// possible to avoid issues caused by inconsistent data.
LS_activeSlideData = {},
LS_activeLayerDataSet = [],
LS_activeStaticLayersDataSet = [],
// These objects will be filled with the default slide/layer
// properties when needed. They purpose as a caching mechanism
// for bulk slide/layer creation.
LS_defaultSliderData = {},
LS_defaultSlideData = {},
LS_defaultLayerData = {},
// Stores all previous editing sessions
// to cache results and speed up operations.
LS_editorSessions = [],
// Flag for unsaved changes on site.
// We use this to display a warning
// for the user when leaving the page.
LS_editorIsDirty = false,
// Stores default UI settings of
// editing sessions.
LS_defaultEditorSession = {
slideIndex: LS_activeSlideIndex,
layerIndex: LS_activeLayerIndexSet,
zoomSlider: 100,
zoomAutoFit: true
},
// Stores temporary data for all
// copy & pate operations.
LS_clipboard = {},
// Stores the main UI elements
LS_previewZoom = 1,
LS_previewArea,
LS_previewHolder,
LS_previewWrapper,
LS_transformStyles = [
'rotation',
'rotationX',
'rotationY',
'scaleX',
'scaleY',
'skewX',
'skewY'
];
var $lasso = jQuery();
// Utility functions to perform
// commonly used tasks.
var LS_Utils = {
convertDateToUTC: function(date) {
return new Date(
date.getUTCFullYear(),
date.getUTCMonth(),
date.getUTCDate(),
date.getUTCHours(),
date.getUTCMinutes(),
date.getUTCSeconds()
);
},
dataURItoBlob: function(dataURI) {
var binary = atob(dataURI.split(',')[1]);
var array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: 'image/png'});
},
moveArrayItem: function(array, from, to) {
if( to === from ) return;
var target = array[from];
var increment = to < from ? -1 : 1;
for(var k = from; k != to; k += increment){
array[k] = array[k + increment];
}
array[to] = target;
},
toAbsoluteURL: function(url) {
// Handle absolute URLs (with protocol-relative prefix)
// Example: //domain.com/file.png
if (url.search(/^\/\//) != -1) {
return window.location.protocol + url;
}
// Handle absolute URLs (with explicit origin)
// Example: http://domain.com/file.png
if (url.search(/:\/\//) != -1) {
return url;
}
// Handle absolute URLs (without explicit origin)
// Example: /file.png
if (url.search(/^\//) != -1) {
return window.location.origin + url;
}
// Handle relative URLs
// Example: file.png
var base = window.location.href.match(/(.*\/)/)[0];
return base + url;
},
// credits: http://phpjs.org/functions/strip_tags/
stripTags: function(input, allowed) {
allowed = (((allowed || '') + '')
.toLowerCase()
.match(/<[a-z][a-z0-9]*>/g) || [])
.join('');
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
commentsAndPhpTags = /|<\?(?:php)?[\s\S]*?\?>/gi;
return input.replace(commentsAndPhpTags, '')
.replace(tags, function($0, $1) {
return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
});
},
// credits: http://phpjs.org/functions/nl2br/
nl2br: function(str, is_xhtml) {
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '
' : '
'; // Adjust comment to avoid issue on phpjs.org display
return (str + '')
.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
},
// credits: http://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript
removeTextSelection: function() {
var selection = window.getSelection ? window.getSelection() : document.selection ? document.selection : null;
if(!!selection) selection.empty ? selection.empty() : selection.removeAllRanges();
},
// credits: http://locutus.io/php/stripslashes/
stripslashes: function(str) {
return (str + '')
.replace(/\\(.?)/g, function (s, n1) {
switch (n1) {
case '\\':
return '\\'
case '0':
return '\u0000'
case '':
return ''
default:
return n1
}
});
},
// credits: http://locutus.io/php/parse_url/
parse_url: function(str, component) {
var query;
var mode = (typeof require !== 'undefined' ? require('../info/ini_get')('locutus.parse_url.mode') : undefined) || 'php';
var key = [
'source',
'scheme',
'authority',
'userInfo',
'user',
'pass',
'host',
'port',
'relative',
'path',
'directory',
'file',
'query',
'fragment'
];
// For loose we added one optional slash to post-scheme to catch file:/// (should restrict this)
var parser = {
php: new RegExp([
'(?:([^:\\/?#]+):)?',
'(?:\\/\\/()(?:(?:()(?:([^:@\\/]*):?([^:@\\/]*))?@)?([^:\\/?#]*)(?::(\\d*))?))?',
'()',
'(?:(()(?:(?:[^?#\\/]*\\/)*)()(?:[^?#]*))(?:\\?([^#]*))?(?:#(.*))?)'
].join('')),
strict: new RegExp([
'(?:([^:\\/?#]+):)?',
'(?:\\/\\/((?:(([^:@\\/]*):?([^:@\\/]*))?@)?([^:\\/?#]*)(?::(\\d*))?))?',
'((((?:[^?#\\/]*\\/)*)([^?#]*))(?:\\?([^#]*))?(?:#(.*))?)'
].join('')),
loose: new RegExp([
'(?:(?![^:@]+:[^:@\\/]*@)([^:\\/?#.]+):)?',
'(?:\\/\\/\\/?)?',
'((?:(([^:@\\/]*):?([^:@\\/]*))?@)?([^:\\/?#]*)(?::(\\d*))?)',
'(((\\/(?:[^?#](?![^?#\\/]*\\.[^?#\\/.]+(?:[?#]|$)))*\\/?)?([^?#\\/]*))',
'(?:\\?([^#]*))?(?:#(.*))?)'
].join(''))
};
var m = parser[mode].exec(str);
var uri = {};
var i = 14;
while (i--) {
if (m[i]) {
uri[key[i]] = m[i];
}
}
if (component) {
return uri[component.replace('PHP_URL_', '').toLowerCase()];
}
if (mode !== 'php') {
var name = (typeof require !== 'undefined' ? require('../info/ini_get')('locutus.parse_url.queryKey') : undefined) || 'queryKey';
parser = /(?:^|&)([^&=]*)=?([^&]*)/g;
uri[name] = {};
query = uri[key[12]] || '';
query.replace(parser, function ($0, $1, $2) {
if ($1) {
uri[name][$1] = $2;
}
});
}
delete uri.source;
return uri;
}
};
var LS_GUI = {
updateImagePicker: function( $picker, image, updateProperties ) {
updateProperties = updateProperties || {};
if( typeof $picker === 'string' ) {
$picker = jQuery('input[name="'+$picker+'"]').next();
}
if( image === 'useCurrent' ) {
image = $picker.find('img').attr('src');
}
if( image && image.indexOf('blank.gif') !== -1 ) {
if( ! updateProperties.fromPost ) {
image = false;
}
}
$picker
.removeClass('has-image not-set')
.addClass( image ? 'has-image' : 'not-set' )
.find('img').attr('src', image || lsTrImgPath+'/blank.gif' );
}
};
// Slide specific undo & redo operations.
// Uses callbacks to run any code stored by
// other methods. Supports custom parameters.
var LS_UndoManager = {
index: -1,
stack: [],
limit: 50,
add: function(cmd, name, updateInfo) {
// Invalidate items higher on the stack when
// called from an undo-ed position
this.stack.splice(this.index + 1, this.stack.length - this.index);
this.index = this.stack.length - 1;
// Maintain stack limit
if(this.stack.length > this.limit) {
this.stack.shift();
}
// Verify 'history' object in slide
if(!LS_activeSlideData.hasOwnProperty('history')) {
LS_activeSlideData.history = [];
}
// Prepare updateInfo
this.prepareUpdateInfo( updateInfo );
// Add item
this.stack.push({
cmd: cmd,
name: name,
updateInfo: updateInfo
});
// Maintain buttons and stack index
this.index = this.stack.length - 1;
this.maintainButtons();
// Mark unsaved changes on page
LS_editorIsDirty = true;
},
// Replace this.stack when switching slides
// to support slide-specific history.
update: function() {
// Verify 'history' object in slide
if(!LS_activeSlideData.hasOwnProperty('history')) {
LS_activeSlideData.history = [];
}
this.stack = LS_activeSlideData.history;
this.index = this.stack.length - 1;
if( LS_activeSlideData.meta && LS_activeSlideData.meta.undoStackIndex ) {
this.index = LS_activeSlideData.meta.undoStackIndex;
}
this.maintainButtons();
},
// Merges new changes into the last item in
// the UndoManager stack.
merge: function( cmd, name, updateInfo ) {
var lastItem = this.stack[ this.stack.length - 1 ];
this.prepareUpdateInfo( updateInfo );
jQuery.extend(true, lastItem.updateInfo, updateInfo);
},
// Empties the current slide's history and reset
// every UndoManager-related properties
empty: function() {
LS_activeSlideData.history = [];
if( LS_activeSlideData.meta && LS_activeSlideData.meta.undoStackIndex ) {
LS_activeSlideData.meta.undoStackIndex = -1;
}
this.update();
},
undo: function() {
if(this.stack[this.index]) {
this.execute('undo', this.stack[this.index]);
this.index--;
this.maintainButtons();
}
},
redo: function() {
if(this.stack[this.index+1]) {
this.index++;
this.execute('redo', this.stack[this.index]);
this.maintainButtons();
}
},
prepareUpdateInfo: function( updateInfo ) {
if( updateInfo && typeof updateInfo === 'object') {
jQuery.each(updateInfo, function(key, val) {
if( typeof val === 'object') {
LS_UndoManager.prepareUpdateInfo( val );
return true;
}
if( val === null || typeof val === 'undefined') {
updateInfo[key] = '';
}
});
}
},
maintainButtons: function(itemIndex) {
var undoButton = jQuery('.ls-editor-undo'),
redoButton = jQuery('.ls-editor-redo');
LS_activeSlideData.meta.undoStackIndex = this.index;
if(this.index !== -1) { undoButton.removeClass('disabled'); }
else { undoButton.addClass('disabled'); }
if(this.index < this.stack.length-1) { redoButton.removeClass('disabled'); }
else { redoButton.addClass('disabled'); }
},
execute: function(action, item) {
// Convert object to array to easily
// handle multi-action steps.
if( jQuery.type(item.updateInfo) === 'object' ) {
item.updateInfo = [item.updateInfo];
}
// Iterate through actions in step.
for(var c = 0; c < item.updateInfo.length; c++) {
this.executeItem(
item.cmd,
item.updateInfo[c].itemIndex,
item.updateInfo[c][action],
item.updateInfo[c]
);
}
},
executeItem: function(command, itemIndex, updateInfo, item) {
switch(command) {
case 'slide.general':
this.updateOptions(LS_activeSlideData.properties, itemIndex, updateInfo, 'slide');
LayerSlider.updateSlideInterfaceItems();
LayerSlider.generatePreview();
break;
case 'slide.layers':
if(jQuery.isEmptyObject(updateInfo.data)) {
LayerSlider.removeLayer(itemIndex, { histroyEvent: true, requireConfirmation: false });
} else {
LayerSlider.addLayer(updateInfo.data, itemIndex, { histroyEvent: true });
LayerSlider.selectLayer( item.selectIndex );
}
LS_DataSource.buildLayersList();
LayerSlider.generatePreview();
break;
case 'layer.order':
LS_Utils.moveArrayItem(LS_activeSlideData.sublayers, updateInfo.from, updateInfo.to);
LS_DataSource.buildLayersList();
LayerSlider.generatePreview();
break;
case 'layer.general':
this.updateOptions(LS_activeSlideData.sublayers[itemIndex], itemIndex, updateInfo);
LayerSlider.updateLayerInterfaceItems(itemIndex);
LayerSlider.generatePreviewItem(itemIndex);
LayerSlider.updatePreviewSelection();
break;
case 'layer.transition':
this.updateOptions(LS_activeSlideData.sublayers[itemIndex].transition, itemIndex, updateInfo);
LayerSlider.generatePreviewItem(itemIndex);
break;
case 'layer.style':
this.updateOptions(LS_activeSlideData.sublayers[itemIndex].styles, itemIndex, updateInfo);
LayerSlider.generatePreviewItem(itemIndex);
LayerSlider.updatePreviewSelection();
break;
}
},
// Iterates over the updateInfo object,
// overrides the keys in the provided
// data object.
updateOptions: function( data, index, updateInfo, area ) {
area = area || 'layers';
var parent = (area === 'slide') ? '.ls-slide-options' : '.ls-layers-table';
jQuery.each(updateInfo, function(key, val) {
//if( data.hasOwnProperty(key) ) {
if( typeof val === 'object' ) {
LS_UndoManager.updateOptions( data[key], index, updateInfo[key], area);
return true;
}
// Update data
data[key] = val;
// Handle UI if it's the active layer
if( area === 'slide' || (LS_activeLayerIndexSet.length == 1 && index == LS_activeLayerIndexSet[0]) ) {
var $target = jQuery(parent+' '+'[name="'+key+'"]'),
eventType = 'input';
$target.val(val).trigger('input').trigger('keyup');
if($target.is(':checkbox')) {
if(val) { $target.next().addClass('on').removeClass('off'); }
else { $target.next().addClass('off').removeClass('on'); }
return;
} else if($target.is('select')) {
eventType = 'change';
}
var jqEvent = jQuery.Event(eventType, { target: $target[0], UndoManagerAction: true });
jQuery('#ls-layers').triggerHandler(jqEvent);
}
//}
});
},
trackInputs: function( event, element ) {
event = event || { type: 'change' };
if( event.UndoManagerAction ) { return false; }
var $input = jQuery(element),
cmd, name, index;
if( event.type.toLowerCase() == 'click' && $input.is('input,textarea') ) {
return;
}
if( event.type.toLowerCase() !== 'change' ) {
$input.data('prevVal', $input.val() );
return;
}
// Skip colorpickers, as they rapidly send change events
if( $input.hasClass('ls-colorpicker') ) {
return;
}
if( $input.closest('.ls-sublayer-pages').length ) {
cmd = 'layer.general';
name = LS_l10n.SBUndoLayer;
index = LS_activeLayerIndexSet[0];
if($input.hasClass('sublayerprop')) { cmd = 'layer.transition'; }
else if($input.hasClass('auto')) { cmd = 'layer.style'; }
} else if( $input.closest('.ls-slide-options').length ) {
cmd = 'slide.general';
name = LS_l10n.SBUndoSlide;
index = LS_activeSlideIndex;
} else {
return true;
}
var optionName = $input.attr('name'),
optionValue = $input.is(':checkbox') ? $input.prop('checked') : $input.val(),
prevValue = $input.is(':checkbox') ? ! $input.prop('checked') : $input.data('prevVal'),
action = $input.hasClass('undomanager-merge') ? 'merge': 'add';
if( ! optionName ) {
return false;
}
var undo = {}, redo = {};
undo[ optionName ] = prevValue;
redo[ optionName ] = optionValue;
if( prevValue !== optionValue ) {
LS_UndoManager[action](cmd, name, {
itemIndex: index,
undo: undo,
redo: redo
});
}
}
};
var LayerSlider = {
uploadInput: null,
dragIndex: 0,
timeout: 0,
mediaCheckTimeout: 0,
isSlidePreviewActive: false,
isLayerPreviewActive: false,
selectableTimeout: 0,
selectMainTab: function(el) {
// Select new tab
jQuery(el).addClass('active').siblings().removeClass('active');
// Show new tab contents
jQuery('#ls-pages .ls-page').removeClass('active');
jQuery('#ls-pages .ls-page').eq( jQuery(el).index() ).addClass('active');
// Init CodeMirror
if(jQuery(el).hasClass('callbacks')) {
if(jQuery('.ls-callback-page .CodeMirror-code').length === 0) {
LS_CodeMirror.init({ mode: 'javascript', autofocus : false, styleActiveLine : false });
jQuery(window).scrollTop(0);
}
}
},
selectSettingsTab: function(li) {
var index = jQuery(li).index();
jQuery(li).addClass('active').siblings().removeClass('active');
jQuery('div.ls-settings-contents tbody.active').removeClass('active');
jQuery('div.ls-settings-contents tbody').eq(index).addClass('active');
},
addSlide: function() {
// Get default data objects for slides and layers
var newSlideData = jQuery.extend(true, {}, LS_DataSource.getDefaultSlideData()),
newLayerData = jQuery.extend(true, {}, LS_DataSource.getDefaultLayerData());
newLayerData.subtitle = LS_l10n.SBLayerTitle.replace('%d', '1');
// Add new slide data to data source
window.lsSliderData.layers.push({
properties: newSlideData,
sublayers: [newLayerData]
});
// Add new slide tab
var newIndex = window.lsSliderData.layers.length + 1,
title = LS_l10n.SBSlideTitle.replace('%d', newIndex),
tab = jQuery(''+title+'').insertBefore('#ls-add-layer');
// Name new slide properly
LayerSlider.reindexSlides();
LayerSlider.addSlideSortables();
LS_activeLayerPageIndex = 0;
// Show new slide, re-initialize
// interactive features
tab.click();
LayerSlider.addLayerSortables();
},
removeSlide: function(el) {
if(confirm(LS_l10n.SBRemoveSlide)) {
// Get tab and menu item index
var index = LS_activeSlideIndex;
var $tab = jQuery(el).parent();
var $newTab = null;
// Open next or prev layer
if($tab.next(':not(.unsortable)').length > 0) {
$newTab = $tab.next();
} else if($tab.prev().length > 0) {
$newTab = $tab.prev();
}
// Remove tab and slide data
window.lsSliderData.layers.splice(index, 1);
$tab.remove();
// Create a new slide if the last one
// was removed
if(window.lsSliderData.layers < 1) {
LayerSlider.addSlide();
return true;
}
// Select new slide. The .click() event will
// maintain the active slide index and data.
LayerSlider.reindexSlides();
$newTab.click();
}
},
selectSlide: function(slideIndex, selectProperties) {
// Set selectProperties to an empty object by default
selectProperties = selectProperties || {};
// Bail out early if it's the currently active layer
if( !selectProperties.forceSelect && LS_activeSlideIndex === slideIndex) { return false; }
// Set active slide, highlight new tab
jQuery('#ls-layer-tabs a')
.eq(slideIndex)
.addClass('active')
.attr('data-help-disabled', '1')
.siblings()
.removeClass('active')
.removeAttr('data-help-disabled');
// Stop live preview
LayerSlider.stopSlidePreview();
LayerSlider.stopLayerPreview();
// Set new slide index & data
LS_activeSlideIndex = slideIndex;
LS_activeSlideData = window.lsSliderData.layers[ slideIndex ];
// Create the 'meta' object if not set
if(!LS_activeSlideData.meta) {
LS_activeSlideData.meta = {};
}
// Make sure to include new slide options in all cases
var defaults = jQuery.extend( true, {}, LS_DataSource.getDefaultSlideData() );
LS_activeSlideData.properties = jQuery.extend( true, defaults, LS_activeSlideData.properties );
// Set active layer index set
LS_activeLayerIndexSet = LS_activeSlideData.meta.activeLayers || [0];
// Add static layers
LS_activeStaticLayersDataSet = LayerSlider.staticLayersForSlide( slideIndex );
// Build slide
LS_DataSource.buildSlide();
LayerSlider.generatePreview();
LayerSlider.selectLayer(LS_activeLayerIndexSet);
LayerSlider.updatePreviewSelection();
LS_UndoManager.update();
},
renameSlide: function(el) {
if( document.location.href.indexOf('ls-revisions') !== -1 ) {
return;
}
var $el = jQuery(el);
var name = jQuery('span:first-child', el).text();
if($el.hasClass('editing')) { return false; }
// Add input
$el.addClass('editing');
$input = jQuery('').appendTo($el).val(name);
$input.focus().select();
// Save changes on Enter
$input.on('keydown', function(e) {
if(e.which == 13) { LayerSlider.renameSlideEnd(el); }
});
// Save changes by clicking away
jQuery('body').one('click', ':not(#ls-layer-tabs a input)', function() {
LayerSlider.renameSlideEnd(el);
});
},
renameSlideEnd: function(el) {
var $el = jQuery(el),
$input = jQuery('input', el),
index = $el.index();
if($el.hasClass('editing')) {
window.lsSliderData.layers[ index ].properties.title = $input.val();
jQuery('span', $el).first().text( $input.val());
$input.remove();
$el.removeClass('editing');
}
},
duplicateSlide: function(el) {
// Duplicate slide by using jQuery.extend()
// to make sure it's a copy instead of an
// object reference.
var newSlideData = jQuery.extend(true, {}, LS_activeSlideData);
// Assign new UUID
newSlideData.properties.uuid = LS_DataSource.generateUUID();
// Rename slide
if(!!newSlideData.properties.title) {
newSlideData.properties.title += ' copy';
} else {
newSlideData.properties.title = LS_l10n.SBSlideCopyTitle.replace('%d', LS_activeSlideIndex+1);
}
// Duplicate slide by using jQuery.extend()
// to make sure it's a copy instead of an
// object reference.
window.lsSliderData.layers.splice(
LS_activeSlideIndex + 1, 0, newSlideData
);
// Insert the duplicate slide tab after the original
var tab = jQuery(''+newSlideData.properties.title+'').insertAfter('#ls-layer-tabs a.active');
LayerSlider.reindexSlides();
LayerSlider.reindexStaticLayers();
// Select new slide
tab.click();
},
toggleAdvancedSlideOptions: function( el ) {
var $el = jQuery(el),
$target = jQuery('.ls-slide-options tr.ls-advanced');
if( $el.hasClass('ls-opened') ) {
$el.removeClass('ls-opened');
$target.addClass('ls-hidden');
} else {
$el.addClass('ls-opened');
$target.removeClass('ls-hidden');
}
},
setPreviewZoom: function( value ) {
LS_previewZoom = value;
jQuery('.ls-editor-slider-val').text(''+Math.round(value * 100)+'%');
jQuery( '.ls-preview-transform' ).css({
transform: 'scale('+value+')'
}).parent().trigger('zoom');
var sliderProps = window.lsSliderData.properties,
width = parseInt(sliderProps.sublayercontainer) || sliderProps.width || 1280,
height = sliderProps.height || 720;
jQuery( '.ls-preview-size' ).css({
width: parseInt(width)* value,
height: parseInt(height) * value
});
LayerSlider.updatePreviewSelection();
},
addPreviewSlider: function(target, value) {
jQuery(target).slider({
value: value, min: 0.5, max: 1.5, step: 0.01,
range: 'min', orientation: 'horizontal',
slide: function(event, ui) {
// Disable auto-fit when resizing manually
if( jQuery('#zoom-fit').prop('checked') ){
jQuery('#zoom-fit').next().click();
}
LayerSlider.setPreviewZoom(ui.value);
// Restart previews (if any)
if( LayerSlider.isSlidePreviewActive ) {
LayerSlider.stopSlidePreview( );
}
if( LayerSlider.isLayerPreviewActive ) {
LayerSlider.stopLayerPreview( true );
}
},
change: function(event, ui) {
LS_previewZoom = ui.value;
LayerSlider.updatePreviewSelection();
}
});
// Resize preview on page load
if( jQuery('#zoom-fit').prop('checked') ) {
LayerSlider.autoFitPreview(target);
// Slide value on page load
} else if(typeof value != "undefined" && value != 1 ) {
jQuery(target).slider('value', parseInt(value));
LayerSlider.setPreviewZoom(value);
}
jQuery(document).on('click','#zoom-fit',function(){
if( jQuery(this).prop('checked') ){
LayerSlider.autoFitPreview(target, 0.75);
}
});
jQuery(window).resize(function( event ){
if( event.target === window ) {
LayerSlider.autoFitPreview(target);
}
});
jQuery('#collapse-menu').click(function() {
LayerSlider.autoFitPreview(target);
});
},
autoFitPreview: function(target, duration){
if( jQuery('#zoom-fit').prop('checked') ){
var sliderProps = window.lsSliderData.properties,
width = parseInt(sliderProps.sublayercontainer) || parseInt(sliderProps.width) || 1280,
height = parseInt(sliderProps.height) || 720,
// 905(px) is the minimum width to keep the slider settings table organized
smallestRatio = 916 / width > 0.5 ? 916 / width : 0.5,
padding = (document.location.href.indexOf('ls-revisions') !== -1) ? 0 : 32,
ratio = ( jQuery('.wrap').eq(0).outerWidth() - padding ) / width;
if( ratio < smallestRatio ){
ratio = smallestRatio;
} else if( ratio > 1 ){
ratio = 1;
}
jQuery(target).slider('value', ratio );
LayerSlider.setPreviewZoom( ratio );
// jQuery('.ls-editor-slider-val').text(ratio+'%');
// if( duration ){
// TweenLite.to(
// jQuery('#ls-preview-layers')[0],
// duration,
// {
// parseTransform: true,
// scale: ratio/100,
// ease: 'Quint.easeInOut',
// onUpdate: function() {
// jQuery('.ls-preview-td').trigger('zoom');
// LayerSlider.updatePreviewSelection();
// }
// }
// );
// TweenLite.to(
// [LS_previewHolder, LS_previewWrapper],
// duration,
// {
// width: width * ratio / 100,
// height: height * ratio / 100,
// ease: 'Quint.easeInOut'
// }
// );
// }else{
// LayerSlider.setPreviewZoom( ratio );
// }
}
},
addLayer: function(layerDataSet, atIndexSet, addProperties) {
var c, len, selectIndexSet = [], updateInfo = [], emptyData, emptyIndex;
// Set removeProperties to an empty object by default
addProperties = addProperties || { selectLayer: true };
// Get default layer data if not provided
emptyData = !layerDataSet;
layerDataSet = layerDataSet || jQuery.extend(true, {}, LS_DataSource.getDefaultLayerData() );
layerDataSet = jQuery.makeArray( layerDataSet );
c = layerDataSet.length;
// Add layer to the top if
// not specified otherwise.
emptyIndex = ! atIndexSet;
atIndexSet = ! emptyIndex ? atIndexSet : [].fill(0, c);
atIndexSet = jQuery.makeArray( atIndexSet );
// Iterate backwards to keep indexes consistent throughout
// the sequence. Don't use .revert() on data sets reference,
// as it will change the original set as well.
while(c--) {
// Add new layer data to data source
LS_activeSlideData.sublayers.splice(atIndexSet[c], 0, layerDataSet[c]);
// Offsetting indexes to follow data storage
// changes in case of multiple additions.
selectIndexSet.push( atIndexSet[c] + c );
// UndoManager
updateInfo.push({
itemIndex: atIndexSet[c],
selectIndex: selectIndexSet[c],
undo: { data: {} },
redo: { data: layerDataSet[c] }
});
}
// Maintain undoManager
if( ! addProperties.histroyEvent) {
LS_UndoManager.add(
'slide.layers',
updateInfo.length > 1 ? LS_l10n.SBUndoNewLayers : LS_l10n.SBUndoNewLayer,
updateInfo
);
}
// Update layers list and preview
LS_DataSource.buildLayersList();
LayerSlider.generatePreview();
// Select new layers
if( addProperties.selectLayer ) {
if( addProperties.hasOwnProperty('selectPage') ) {
LS_activeLayerPageIndex = addProperties.selectPage;
}
LayerSlider.selectLayer( selectIndexSet );
if( emptyData && updateInfo.length === 1 ) {
jQuery('.ls-sublayers li.active .ls-sublayer-title').focus().select();
}
}
},
selectLayer: function(layerIndexSet, selectProperties) {
// Bail out early if the current slide has no layers
if( ! LS_activeSlideData.sublayers.length) {
jQuery('.ls-timeline-switch, .ls-sublayer-nav').hide();
jQuery('.ls-sublayer-pages').empty();
return false;
} else {
jQuery('.ls-timeline-switch, .ls-sublayer-nav').show();
}
// Bail out early if there's no active layer selection
if( ! layerIndexSet || ! layerIndexSet.length) { return false; }
// Bail out early if the current selection is the same
// if( layerIndexSet.length == LS_activeLayerIndexSet.length ) {
// if( layerIndexSet.every(function(v,i) { return v === LS_activeLayerIndexSet[i];}) ) {
// return false;
// }
// }
// Set removeProperties to an empty object by default
selectProperties = selectProperties || {};
// Bail out early if it's already a selected layer
// if( !selectProperties.forceSelect &&
// LS_activeLayerIndexSet.indexOf(layerIndex) !== -1 ) {
// return false;
// }
var $layersList = jQuery('.ls-sublayers li'),
$layerOptions = jQuery('.ls-sublayer-pages-wrapper');
// Stop layer preview session (if any)
LayerSlider.stopLayerPreview();
// Update stored data & preview based on
// the passed selection index set.
LS_activeLayerIndexSet = [];
LS_activeLayerDataSet = [];
$layersList.removeClass('active');
jQuery('#ls-preview-layers > *').removeClass('ui-selected');
jQuery.each(layerIndexSet, function(idx, layerIndex) {
LS_activeLayerIndexSet.push(layerIndex);
LS_activeLayerDataSet.push(
LS_activeSlideData.sublayers[layerIndex]
);
LS_previewItems[layerIndex].addClass('ui-selected');
$layersList.eq(layerIndex).addClass('active');
});
if(!LS_activeLayerDataSet[0].meta) {
LS_activeLayerDataSet[0].meta = {};
}
// Show/Hide layer options depending on
// the number of selected layers
if(LS_activeLayerIndexSet.length > 1) {
$layerOptions.hide().prev().removeClass('ls-hidden');
} else {
$layerOptions.show().prev().addClass('ls-hidden');
}
// Build new layer ...
if(LS_activeLayerIndexSet.length === 1) {
LS_DataSource.buildLayer();
}
// Store selection
LS_Utils.removeTextSelection();
LayerSlider.updatePreviewSelection();
LS_activeSlideData.meta.activeLayers = LS_activeLayerIndexSet;
jQuery('.ls-timeline-switch, .ls-sublayer-nav').show();
// Create layer transition preview animations
layerTransitionPreview.create();
},
selectLayerPage: function(pageIndex) {
// Select new tab
jQuery('.ls-sublayer-nav a').removeClass('active')
.eq(pageIndex).addClass('active');
// Show the corresponding page
jQuery('#ls-layers .ls-sublayer-page').removeClass('active')
.eq( pageIndex ).addClass('active');
// Store lastly selected layer page
LS_activeLayerPageIndex = pageIndex;
// SET: styles
kmUI.smartResize.set();
},
selectTransitionPage: function( td ) {
var $td = jQuery(td),
index = ($td.index() - 1) / 2,
$target = jQuery('#ls-layer-transitions').children().eq(index);
$target.addClass('active').siblings().removeClass('active');
$td.addClass('selected').siblings().removeClass('selected');
jQuery( '#ls-transition-selector' ).val( index );
LS_activeLayerTransitionTab = index;
$target.removeClass('disabled');
if( ! $target.find('.ls-h-button input').prop('checked') ) {
$target.addClass('disabled');
}
},
enableTransitionPage: function( input ) {
LayerSlider.reorderTransitionProperties(
jQuery( input ).closest('section').index()
);
LayerSlider.checkForOpeningTransition();
},
checkForOpeningTransition: function() {
$table = jQuery('#ls-transition-selector-table');
$transitions = jQuery('.ls-opening-transition.active', $table);
$warning = jQuery('#ls-transition-warning');
$warning[ $transitions.length ? 'removeClass' : 'addClass' ]('visible');
},
reorderTransitionProperties: function( sectionIndex ) {
var media = LS_activeLayerDataSet[0].media || '',
index,
$sections = jQuery('#ls-layer-transitions').children(),
$section,
$input,
$td;
if( sectionIndex ) {
$sections = $sections.eq( sectionIndex );
}
$sections.each(function() {
$section = jQuery(this);
index = $section.index();
$input = $section.find('input.toggle').eq(0);
$td = jQuery('#ls-transition-selector-table td:not(.ls-padding)').eq( index );
// Disabled
if( ! $input.prop('checked') ) {
$td.removeClass('active');
$section.addClass('disabled');
$section.find(':input').each(function() {
var $this = jQuery(this),
name = $this.attr('name'),
value = $this.is(':checkbox') ? $this.prop('checked') : $this.val();
if( name && ! $this.is('.toggle') ) {
$this.data('value', value );
delete LS_activeLayerDataSet[0].transition[ name ];
}
});
// Active
} else {
$td.addClass('active');
$section.removeClass('disabled');
$section.find(':input').each(function() {
var $this = jQuery(this),
name = $this.attr('name'),
value = $this.data('value');
if( name && ! $this.is('.toggle') ) {
LS_activeLayerDataSet[0].transition[ name ] = value;
}
});
}
});
},
removeLayer: function(layerIndexSet, removeProperties) {
// Set removeProperties to an empty object by default
removeProperties = removeProperties || { requireConfirmation: true };
// Require confirmation from user
// if it's not a history event.
if( removeProperties.requireConfirmation ) {
if( !confirm( LS_l10n.SBRemoveLayer ) ) {
return false;
}
}
// Get active layers if no index was provided
if( ! layerIndexSet && layerIndexSet !== 0 ) {
layerIndexSet = LS_activeLayerIndexSet;
// Convert a single index to an index set
} else if( typeof layerIndexSet === 'number') {
layerIndexSet = [layerIndexSet];
}
// Get layer(s)
var c = layerIndexSet.length, $layers = jQuery('.ls-sublayers li'),
updateInfo = [], $layer, $newLayer, layerIndex, layerData;
// Iterate backwards to keep indexes consistent throughout the sequence.
// Don't use .revert() on a LS_activeLayerIndexSet reference, as it will
// change the original set as well.
while(c--) {
layerIndex = layerIndexSet[c];
$layer = $layers.eq(layerIndex);
layerData = jQuery.extend(true, {}, LS_activeSlideData.sublayers[layerIndex]);
// Get the next or prev layer
if($layer.next().length > 0) { $newLayer = $layer.next(); }
else if($layer.prev().length > 0) { $newLayer = $layer.prev(); }
// Setup UndoManager updateInfo object
updateInfo.push({
itemIndex: layerIndex,
undo: { data: layerData },
redo: { data: {} }
});
// Remove layer from data source and UI
LS_activeSlideData.sublayers.splice(layerIndex, 1);
$layer.remove();
}
// Empty slide, hide UI items
if(!LS_activeSlideData.sublayers.length) {
jQuery('.ls-timeline-switch, .ls-sublayer-nav').hide();
jQuery('.ls-sublayer-pages').empty();
// Update UI otherwise
// Select new layer. The .click() event will
// maintain the active layer index and data.
} else if($newLayer) {
$newLayer.click();
LayerSlider.reindexLayers();
}
// Update preview
LayerSlider.generatePreview();
LayerSlider.updatePreviewSelection();
// Maintain undoManager only if
// it wasn't a history action
if( !removeProperties.histroyEvent && updateInfo.length) {
LS_UndoManager.add('slide.layers', LS_l10n.SBUndoRemoveLayer, updateInfo);
}
},
hideLayer: function(el) {
// Get layer index
var layerIndex = jQuery(el).closest('li').index();
var layerData = LS_activeSlideData.sublayers[layerIndex];
// Maintain history
LS_UndoManager.add('layer.general', LS_l10n.SBUndoHideLayer, {
itemIndex: layerIndex,
undo: { skip: !!layerData.skip },
redo: { skip: !layerData.skip }
});
// Hide/show layer
layerData.skip = !layerData.skip;
if(layerData.skip) { jQuery(el).addClass('disabled'); }
else { jQuery(el).removeClass('disabled'); }
// Update preview
LayerSlider.generatePreviewItem(layerIndex);
},
lockLayer: function(el) {
// Get layer index
var layerIndex = jQuery(el).closest('li').index(),
layerData = LS_activeSlideData.sublayers[layerIndex],
$previewItem = LayerSlider.previewItemAtIndex(layerIndex);
// Maintain history
LS_UndoManager.add('layer.general', LS_l10n.SBUndoLockLayer, {
itemIndex: layerIndex,
undo: { locked: !!layerData.locked },
redo: { locked: !layerData.locked }
});
// Lock layer
layerData.locked = !layerData.locked;
if(layerData.locked) {
jQuery(el).removeClass('disabled');
$previewItem.addClass('disabled');
$lasso.hide();
// Unlock layer
} else {
jQuery(el).addClass('disabled');
$previewItem.removeClass('disabled');
}
},
setLayerMedia: function(mediaType, $mediaEl, layerData) {
switch(mediaType) {
case 'img':
var src = layerData.imageThumb || pluginPath+'admin/img/blank.gif',
classes = layerData.imageThumb ? '' : ' dashicons dashicons-format-image';
$mediaEl.attr('class', 'ls-sublayer-thumb'+classes).html('');
break;
case 'html':
$mediaEl.addClass('dashicons dashicons-editor-code');
break;
case 'media':
$mediaEl.addClass('dashicons dashicons-video-alt3');
break;
case 'post':
$mediaEl.addClass('dashicons dashicons-admin-post');
break;
default:
$mediaEl.addClass('dashicons dashicons-editor-textcolor');
break;
}
},
setLayerAttributes: function( event, element ) {
if( event.type === 'change' && ! jQuery(element).is(':checkbox') ) {
return;
}
var $tr = jQuery(element).closest('tr'),
$inputs = jQuery('input', $tr ),
innerAttrs = LS_activeLayerDataSet[0].innerAttributes = {},
outerAttrs = LS_activeLayerDataSet[0].outerAttributes = {};
if( ! $inputs.eq(0).val() && ! $inputs.eq(1).val() ) {
$tr.remove();
return;
}
jQuery('.ls-sublayer-custom-attributes tr:not(:last-child)').each(function() {
var $key = jQuery('td.first input', this),
$val = jQuery('td.second input', this),
$chb = jQuery('td.third input', this),
key = $key.val(),
val = $val.val();
if( key && /^[a-zA-Z]([a-zA-Z0-9_-]+)$/.test( key ) ) {
$key.removeClass('error');
if( $chb.prop('checked') ) {
outerAttrs[ key ] = val;
} else {
innerAttrs[ key ] = val;
}
} else {
$key.addClass('error');
}
});
},
updateLayerAttributes: function( layerData ) {
// Make sure to have objects for data
layerData.innerAttributes = layerData.innerAttributes || {};
layerData.outerAttributes = layerData.outerAttributes || {};
var customAttrs = jQuery.extend( {}, layerData.innerAttributes, layerData.outerAttributes),
$customAttributes = jQuery('.ls-sublayer-custom-attributes');
// Sort keys
Object.keys(customAttrs).sort().forEach(function(key) {
var value = customAttrs[key];
delete customAttrs[key];
customAttrs[key] = value;
});
jQuery.each(customAttrs, function(key, val) {
jQuery('tr:last-child input:eq(2)', $customAttributes).prop('checked', key in layerData.outerAttributes );
jQuery('tr:last-child input:eq(1)', $customAttributes).val( val );
jQuery('tr:last-child input:eq(0)', $customAttributes).val( key ).trigger('keyup');
});
},
updateLayerBorderPadding: function(el) {
var $input = jQuery(el),
value = parseInt( $input.val() ),
type = ($input.parent().index() === 1) ? 'border' : 'padding',
edge = $input.closest('tr').data('edge');
sel = '.ls-'+type+'-'+edge+'-value';
jQuery(sel).text( value || '–' );
},
// Iterate through all slides and their layers to
// find the ones appearing on the target slide.
staticLayersForSlide: function( targetSlideIndex ) {
var staticLayers = [];
jQuery.each(window.lsSliderData.layers, function(slideIndex, slideData) {
jQuery.each(slideData.sublayers, function(layerIndex, layerData) {
if( layerData.transition.static ) {
var staticOut = layerData.transition.static;
if( ( staticOut > targetSlideIndex || staticOut === 'forever' ) && slideIndex < targetSlideIndex ) {
staticLayers.push({
slideIndex: slideIndex,
slideData: slideData,
layerIndex: layerIndex,
layerData: layerData
});
}
}
});
});
return staticLayers;
},
reindexStaticLayers: function() {
jQuery.each(window.lsSliderData.layers, function(slideIndex, slideData) {
jQuery.each(slideData.sublayers, function(layerIndex, layerData) {
if( layerData.transition.staticUUID ) {
var staticOut = LS_DataSource.slideForUUID( layerData.transition.staticUUID );
if( staticOut ) {
layerData.transition.static = staticOut + 1;
}
}
});
});
},
setupStaticLayersChooser: function( select ) {
var $select = jQuery(select);
// Remove previously added options
$select.children(':gt(1)').remove();
// Gather slide data
var sliderData = window.lsSliderData,
slideCount = sliderData.layers ? sliderData.layers.length : 0,
markup = '',
slideName;
// Generate markup
for( var s = 0; s < slideCount; s++) {
slideName = sliderData.layers[s].properties.title;
slideName = slideName ? ' ('+slideName+')' : '';
markup += '';
}
// Append select options
$select.append(markup);
var staticVal = parseInt( LS_activeLayerDataSet[0].transition.static );
if( staticVal ) {
$select.children('[value="'+staticVal+'"]').prop('selected', true)
.siblings().prop('selected', false);
}
},
revealStaticLayer: function( el ) {
var $target = jQuery(el).closest('li'),
index = $target.index(),
data = LS_activeStaticLayersDataSet[ index ];
LayerSlider.selectSlide( data.slideIndex );
LayerSlider.selectLayer( [data.layerIndex] );
},
addColorPicker: function(el) {
jQuery(el).minicolors({
opacity: true,
changeDelay: 100,
position: 'bottom right',
change: function(hex, opacity) {
//LayerSlider.willGeneratePreview();
}
}).blur(function( event ) {
event.stopImmediatePropagation();
jQuery(this)
.removeClass('ls-colorpicker')
.trigger('change')
.addClass('ls-colorpicker');
});
},
duplicateLayer: function() {
this.pasteLayer( this.copyLayer(false).layers );
},
copyLayer: function(useStorage, layerDataSet, layerIndexSet, copyProperties) {
// Defaults
useStorage = useStorage || true;
layerDataSet = layerDataSet || LS_activeLayerDataSet;
layerIndexSet = layerIndexSet || LS_activeLayerIndexSet;
copyProperties = copyProperties || { shiftLayers: true };
// Iterate over the data set, clone objects and
// make some visual adjustments on items
var clipboardData = [];
jQuery.each(layerDataSet, function(key, item) {
// Copy layer data object
var copy = jQuery.extend(true, {}, item);
copy.subtitle += ' copy';
if( copyProperties.shiftLayers ) {
// copy.styles.top = (copy.styles.top.indexOf('%') == -1) ? parseInt(copy.styles.top) + 10 + 'px' : parseInt(copy.styles.top) + 1 + '%';
// copy.styles.left = (copy.styles.left.indexOf('%') == -1) ? parseInt(copy.styles.left) + 10 + 'px' : parseInt(copy.styles.left) + 1 + '%';
}
// Add copy to the new set
clipboardData.push(copy);
});
// Build clipboard data
clipboardData = {
layers: clipboardData,
sliderID: copyProperties.sliderID || LS_sliderID,
slideIndex: copyProperties.slideIndex || LS_activeSlideIndex,
layerIndexSet: layerIndexSet
};
// Save to storage and return copies
useStorage && localStorage.setObject('ls-layer-clipboard', clipboardData);
return clipboardData;
},
pasteLayer: function(layerDataSet, layerIndexSet, pasteProperties) {
// Check for provided data, fetch from clipboard if not
var isDataProvided = layerDataSet ? true : false,
clipboardData = localStorage.getObject('ls-layer-clipboard'),
addIndexSet;
if( ! clipboardData ) {
alert(LS_l10n.SBPasteLayerError);
return;
}
layerDataSet = layerDataSet || clipboardData.layers;
layerIndexSet = layerIndexSet || clipboardData.layerIndexSet;
// Warn users when there's nothing on the clipboard
// and halt execution.
if( ! layerDataSet ) {
alert(LS_l10n.SBPasteLayerError);
return;
}
// Set pasteProperties to an empty object by default
pasteProperties = pasteProperties || {};
// If the layer is from the same slide, then
// find the uppermost selected layer index
// and insert everything into that position.
// Otherwise insert at the beginning of the layers list.
// -
// Trying to insert layers before their parents
// individually is complex, and it will fragment
// dupe selection.
if(clipboardData.sliderID !== LS_sliderID || clipboardData.slideIndex !== LS_activeSlideIndex) {
addIndexSet = [].fill( 0, layerIndexSet.length);
} else {
addIndexSet = [].fill( Math.min.apply(Math, layerIndexSet), layerIndexSet.length);
}
// Insert new layers
LayerSlider.addLayer(layerDataSet, addIndexSet, { selectLayer: true } );
// Copy pasted layer to make a new reference
// and update settings like position and name
if( ! isDataProvided) {
this.copyLayer(true, layerDataSet, layerIndexSet, {
sliderID: clipboardData.sliderID,
slideIndex: clipboardData.slideIndex
});
}
},
selectMediaType: function(el, layerIndex) {
// Gather layer data
layerIndex = layerIndex ? layerIndex : LS_activeLayerIndexSet;
layerIndex = (typeof layerIndex === 'object') ? layerIndex[0] : layerIndex;
var layerData = LS_activeSlideData.sublayers[layerIndex],
layer = jQuery(el).closest('.ls-sublayer-page'),
$layerItem = jQuery('.ls-sublayers li').eq(layerIndex),
section = jQuery(el).data('section'),
placeholder = jQuery(el).data('placeholder'),
sections = jQuery('.ls-layer-sections', layer).children();
// Set active class
jQuery(el).attr('class', 'active').siblings().removeAttr('class');
// Store selection
if( section ) {
layerData.media = section;
}
// Show the corresponding sections
sections.hide().removeClass('ls-hidden');
jQuery('.ls-sublayer-element', layer).hide().removeClass('ls-hidden');
jQuery('.ls-html-code .ls-options, .ls-html-code .ls-insert-media', layer).addClass('ls-hidden');
switch(section) {
case 'img':
sections.eq(0).show();
var src = layerData.imageThumb || pluginPath+'admin/img/blank.gif',
classes = layerData.imageThumb ? '' : ' dashicons dashicons-format-image';
jQuery('.ls-sublayer-thumb', $layerItem).attr('class', 'ls-sublayer-thumb'+classes).html('');
break;
case 'text':
sections.eq(1).show();
layer.find('.ls-sublayer-element').show();
jQuery('.ls-html-code textarea').attr('placeholder', placeholder );
jQuery('.ls-sublayer-thumb', $layerItem).attr('class', 'ls-sublayer-thumb dashicons dashicons-editor-textcolor').html('');
break;
case 'html':
sections.eq(1).show();
jQuery('.ls-html-code .ls-options, .ls-html-code .ls-insert-media', layer).addClass('ls-hidden');
jQuery('.ls-html-code textarea').attr('placeholder', placeholder );
jQuery('.ls-sublayer-thumb', $layerItem).attr('class', 'ls-sublayer-thumb dashicons dashicons-editor-code').html('');
break;
case 'media':
sections.eq(1).show();
jQuery('.ls-html-code .ls-options, .ls-html-code .ls-insert-media', layer).removeClass('ls-hidden');
jQuery('.ls-html-code textarea').attr('placeholder', placeholder );
jQuery('.ls-sublayer-thumb', $layerItem).attr('class', 'ls-sublayer-thumb dashicons dashicons-video-alt3').html('');
break;
case 'post':
sections.eq(1).show();
sections.eq(2).show();
jQuery('.ls-html-code textarea').attr('placeholder', placeholder );
jQuery('.ls-sublayer-thumb', $layerItem).attr('class', 'ls-sublayer-thumb dashicons dashicons-admin-post').html('');
break;
}
if( section === 'img' || section === 'media' ) {
jQuery('#ls-layer-transitions .ls-text-transition .ls-checkbox.toggle.on').click();
}
jQuery('.ls-sublayer-pages-wrapper').attr('class', 'ls-sublayer-pages-wrapper ls-layer-type-' + layerData.media);
},
selectElementType: function(el, layerIndex) {
// Layer and properties
layerIndex = layerIndex ? layerIndex : LS_activeLayerIndexSet;
layerIndex = (typeof layerIndex === 'object') ? layerIndex[0] : layerIndex;
var layerData = LS_activeSlideData.sublayers[layerIndex],
layer = jQuery(el).closest('.ls-sublayer-page'),
element = jQuery(el).data('element');
// Set active class
jQuery(el).siblings().removeClass('active');
jQuery(el).addClass('active');
// Store selection
if( element ) {
layerData.type = element;
}
},
copyLayerSettings: function(el) {
var $el = jQuery(el),
$wrapper = $el.closest('[data-storage]'),
storage = $wrapper.attr('data-storage'),
data = { styles: {}, transition: {} };
// Iterate over options, store values
$wrapper.find(':input').each(function() {
if(this.name) {
var $item = jQuery(this),
area = $item.hasClass('sublayerprop') ? 'transition' : 'styles';
data[area][this.name] = $item.is(':checkbox') ? $item.prop('checked') : $item.val();
}
});
// Add data to clipboard
var LS_clipboard = localStorage.getObject('ls-options-clipboard') || {};
LS_clipboard[ storage ] = {
timestamp: Math.floor(Date.now() / 1000),
data: data
};
localStorage.setObject('ls-options-clipboard', LS_clipboard);
// Send feedback to users
$el.css('color', '#fcd116');
setTimeout(function() {
$el.css('color', '#00a0d2');
}, 1000);
},
pasteLayerSettings: function(el) {
var $el = jQuery(el),
$wrapper = $el.closest('[data-storage]'),
storage = $wrapper.attr('data-storage'),
undoObj = {},
redoObj = {},
undoArea;
// Don't allow pasting options when the corresponding
// transition sections is disabled
if( $wrapper.closest('#ls-layer-transitions').length ) {
if( ! $wrapper.find('.ls-h-button input').prop('checked') ) {
$wrapper.find('.overlay').click();
return;
}
}
// Get clipboard data
var LS_clipboard = localStorage.getObject('ls-options-clipboard') || {},
clipboard = LS_clipboard[storage],
timestamp = Math.floor(Date.now() / 1000);
// Validate clipboard data
if( ! clipboard || jQuery.isEmptyObject(clipboard.data) || clipboard.timestamp < timestamp - 60 * 60 * 3 ) {
alert(LS_l10n.SBPasteError);
return false;
}
// Iterate over options, set new values
$wrapper.find(':input').each(function() {
if(this.name && this.name != 'top' && this.name != 'left') { // !!! don't paste left & top style
var $this = jQuery(this),
area = $this.hasClass('sublayerprop') ? 'transition' : 'styles',
curVal = $this.is(':checkbox') ? $this.prop('checked') : $this.val(),
newVal = clipboard.data[area][this.name];
if( curVal != newVal ) {
if($this.is(':checkbox')) {
$this.next().click();
} else {
$this.val( newVal ).trigger('input').trigger('keyup');
}
if( ! undoObj[ area ] ) { undoObj[ area ] = {}; }
if( ! redoObj[ area ] ) { redoObj[ area ] = {}; }
undoObj[ area ][ this.name ] = curVal;
redoObj[ area ][ this.name ] = newVal;
}
// Handle custom CSS field separately
if( this.name === 'style' ) {
LS_activeLayerDataSet[0]['style'] = newVal;
} else {
LS_activeLayerDataSet[0][area][ this.name ] = newVal;
}
}
});
// Add UndoManager action
LS_UndoManager.add('layer.general', LS_l10n.SBUndoPasteSettings, {
itemIndex: LS_activeLayerIndexSet[0],
undo: undoObj,
redo: redoObj
});
$el.css('color', '#90ca77');
setTimeout(function() { $el.css('color', '#00a0d2'); }, 1000);
if( undoArea === 'style' ) {
LayerSlider.generatePreviewItem( LS_activeLayerIndexSet[0] );
}
},
updateSlideInterfaceItems: function() {
var slideData = LS_activeSlideData.properties,
imgSrc = slideData.backgroundThumb ? slideData.backgroundThumb : slideData.background;
LS_GUI.updateImagePicker( 'background', imgSrc );
},
updateLayerInterfaceItems: function(layerIndex) {
var $layer = jQuery('.ls-sublayer-pages'),
$layerItem = jQuery('.ls-sublayers li').eq(layerIndex),
layerData = LS_activeSlideData.sublayers[layerIndex];
if( ! layerData ) { return; }
// Image layer preview
var imgSrc = layerData.imageThumb ? layerData.imageThumb : layerData.image;
LS_GUI.updateImagePicker( 'image', imgSrc );
// Video poster preview
imgSrc = layerData.posterThumb ? layerData.posterThumb : layerData.poster;
LS_GUI.updateImagePicker( 'poster', imgSrc );
// Select layer and media type
if(typeof layerData.media == 'undefined') {
switch(layerData.type) {
case 'img': layerData.media = 'img'; break;
case 'div': layerData.media = 'html'; break;
default: layerData.media = 'text';
}
}
LayerSlider.selectMediaType( $layer.find('.ls-layer-kind li[data-section="'+layerData.media+'"]'), layerIndex );
LayerSlider.selectElementType( $layer.find('.ls-sublayer-element > li[data-element="'+layerData.type+'"]'), layerIndex );
// Skip
if(layerData.skip) { jQuery('.ls-icon-eye', $layerItem).addClass('disabled'); }
else { jQuery('.ls-icon-eye', $layerItem).removeClass('disabled'); }
if(layerData.locked) { jQuery('.ls-icon-lock', $layerItem).removeClass('disabled'); }
else { jQuery('.ls-icon-lock', $layerItem).addClass('disabled'); }
},
changeLayerScreenType: function( $button, updateLayer ) {
jQuery('.ls-set-screen-types button').each(function() {
var layerData = LS_activeLayerDataSet[0],
$item = jQuery(this),
type = $item.data('type');
if( $button && $button.is( $item ) ) {
layerData['hide_on_'+type] = ! layerData['hide_on_'+type];
}
$item[ layerData['hide_on_'+type] ? 'removeClass' : 'addClass' ]('playing');
});
if( updateLayer ) {
LayerSlider.generatePreviewItem( LS_activeLayerIndexSet[0] );
setTimeout(function() {
LS_DataSource.buildLayersListItem( LS_activeLayerIndexSet[0] );
}, 200);
}
},
changeVideoType: function() {
var $input = jQuery('.ls-sublayer-basic input.bgvideo'),
$options = jQuery('.ls-sublayer-basic .ls-media-options');
$overlays = jQuery('.ls-sublayer-basic .ls-bgvideo-options');
if( $input.prop('checked') ) {
$options.hide();
$overlays.show();
LS_activeLayerDataSet[0].locked = true;
} else {
$options.show();
$overlays.hide();
LS_activeLayerDataSet[0].locked = false;
}
},
validateCustomCSS: function( $textarea ) {
var keys = ['mix-blend-mode', 'filter'];
for(var c = 0; c < keys.length; c++) {
if( $textarea.val().indexOf(keys[c]) !== -1 ) {
$textarea.val( $textarea.val().replace( new RegExp(keys[c], 'gi'), '') );
TweenMax.to( jQuery('.ls-sublayer-style :input[name="'+keys[c]+'"]')[0], 0.15, {
yoyo: true,
repeat: 3,
ease: Quad.easeInOut,
scale: 1.2,
backgroundColor: 'rgba(255, 0, 0, 0.2)'
});
}
}
},
willGeneratePreview: function() {
clearTimeout(LayerSlider.timeout);
LayerSlider.timeout = setTimeout(function() {
LayerSlider.generatePreview();
}, 1000);
},
generatePreview: function() {
// –––––––––––––––––––––––––––––––––––––––––––––
// READ-ONLY BLOCK
//
// Group DOM read/access operations together,
// so the browser can cache and apply them in a
// in a single pass, triggering only one reflow.
// ———————————————————————————––––––––––––––––––
// Slider data sets
var sliderProps = window.lsSliderData.properties,
slideIndex = LS_activeSlideIndex,
slideData = LS_activeSlideData,
slideProps = slideData.properties,
layers = slideData.sublayers,
$settings = jQuery('.ls-settings'),
// Preview data
width = parseInt( sliderProps.sublayercontainer) || parseInt(sliderProps.width) || 1280,
height = parseInt(sliderProps.height) || 720,
bgColor = sliderProps.backgroundcolor,
bgImage = sliderProps.backgroundimage,
yourLogo = sliderProps.yourlogo,
yourLogoStyle = sliderProps.yourlogostyle,
posts = window.lsPostsJSON,
postOffset = slideProps.post_offset,
slideBG = slideProps.background,
slideBGSize = slideProps.bgsize,
slideBGPos = slideProps.bgposition,
post;
// --- Adjust default values ---
height = (height.indexOf('%') !== -1) ? 400 : parseInt(height);
postOffset = (postOffset == -1) ? slideIndex : postOffset;
post = posts[ postOffset ] || {};
// –––––––––––––––––––––––––––––––––––––––––––––
// WRITE-ONLY BLOCK
//
// Use only DOM write operations after this comment,
// so the browser can cache and apply them in a
// in a single pass, triggering only one reflow.
// ———————————————————————————––––––––––––––––––
// --- Set preview canvas size ---
LS_previewArea.css({
width : width,
height : height
}).empty();
jQuery('.ls-preview-size').css({
width : width * LS_previewZoom,
height : height * LS_previewZoom
});
// Make sure to follow preview area size changes
jQuery('.ls-ruler').trigger('resize');
LayerSlider.autoFitPreview();
// --- Set global background ---
LS_previewHolder.css({
backgroundColor : bgColor || 'transparent',
backgroundImage : bgImage ? 'url('+bgImage+')' : 'none',
backgroundRepeat: sliderProps.globalBGRepeat,
backgroundAttachment: sliderProps.globalBGAttachment,
backgroundPosition: sliderProps.globalBGPosition,
backgroundSize: sliderProps.globalBGSize
});
// Empty preview items list, so we don't include beyond
// array bounds objects from previous slide in case of
// slide change.
LS_previewItems = [];
// Handle post content
if(slideBG == '[image-url]') {
slideBG = post['image-url'];
LS_GUI.updateImagePicker( 'background', post['image-url'], { fromPost: true });
}
// -- Set slide background && empty previous content ---
if( ! slideBGSize || slideBGSize === 'inherit') {
slideBGSize = sliderProps.slideBGSize;
}
if( ! slideBGPos || slideBGPos === 'inherit') {
slideBGPos = sliderProps.slideBGPosition;
}
LS_previewArea.css({
backgroundImage: slideBG ? 'url('+slideBG+')' : 'none',
backgroundSize: slideBGSize || 'auto',
backgroundPosition: slideBGPos || 'center center',
backgroundColor: slideProps.bgcolor || 'transparent',
backgroundRepeat: 'no-repeat'
});
// -- Set background on slide tab
slideBG = slideBG || pluginPath+'admin/img/blank.gif';
jQuery('#ls-layer-tabs a').eq(slideIndex).data('help', "");
// --- Setup yourLogo ---
LS_previewHolder.parent().find('.yourlogo').remove();
if( yourLogo ) {
var logo = jQuery('').prependTo( LS_previewHolder );
logo.attr('style', yourLogoStyle);
var oL, oR, oT, oB,
logoLeft, logoRight, logoTop, logoBottom;
oL = oR = oT = oB = 'auto';
if( logo.css('left') != 'auto' ){
logoLeft = logo[0].style.left;
}
if( logo.css('right') != 'auto' ){
logoRight = logo[0].style.right;
}
if( logo.css('top') != 'auto' ){
logoTop = logo[0].style.top;
}
if( logo.css('bottom') != 'auto' ){
logoBottom = logo[0].style.bottom;
}
if( logoLeft && logoLeft.indexOf('%') != -1 ){
oL = width / 100 * parseInt( logoLeft ) - logo.width() / 2;
}else{
oL = parseInt( logoLeft );
}
if( logoRight && logoRight.indexOf('%') != -1 ){
oR = width / 100 * parseInt( logoRight ) - logo.width() / 2;
}else{
oR = parseInt( logoRight );
}
if( logoTop && logoTop.indexOf('%') != -1 ){
oT = height / 100 * parseInt( logoTop ) - logo.height() / 2;
}else{
oT = parseInt( logoTop );
}
if( logoBottom && logoBottom.indexOf('%') != -1 ){
oB = height / 100 * parseInt( logoBottom ) - logo.height() / 2;
}else{
oB = parseInt( logoBottom );
}
logo.css({
left : oL,
right : oR,
top : oT,
bottom : oB
});
}
// --- Setup layers ---
for(var c = 0, len = layers.length; c < len; c++) {
LayerSlider.generatePreviewItem( c, post);
}
// --- Setup static layers ---
LayerSlider.generateStaticPreview();
},
generateStaticPreview: function() {
LS_previewArea.children('.ls-static-layer').remove();
jQuery.each(LS_activeStaticLayersDataSet, function(idx, data) {
LayerSlider.generatePreviewItem( idx, false, {
$targetArea: LS_previewArea,
$layerItem: LS_previewArea.children('.ls-static-layer').eq(idx),
layerData: data.layerData,
isStatic: true
});
});
},
willGeneratePreviewItem: function(layerIndex) {
clearTimeout(LayerSlider.timeout);
LayerSlider.timeout = setTimeout(function() {
LayerSlider.generatePreviewItem(layerIndex);
}, 150);
},
generatePreviewItem: function(layerIndex, post, generateProperties) {
if( jQuery.type( layerIndex ) === 'array' ) {
layerIndex = layerIndex[0];
}
generateProperties = generateProperties || {};
generateProperties = jQuery.extend({}, {
$targetArea: LS_previewArea,
$layerItem: LS_previewItems[layerIndex],
layerData: LS_activeSlideData.sublayers[layerIndex],
isStatic: false
}, generateProperties);
// Don't update the editor while live previews are active
if( LayerSlider.isLayerPreviewActive ) { return false; }
// Remove affected item to replace with an updated one
if( generateProperties.$layerItem ) {
generateProperties.$layerItem.remove();
}
// Get layer data sets
var layerData = generateProperties.layerData,
layerCount = LS_activeSlideData.sublayers ? LS_activeSlideData.sublayers.length : 0,
// Get layer attributes
item,
type = layerData.type,
html = layerData.html,
id = layerData.id,
// Get style settings
top = layerData.styles.top,
left = layerData.styles.left,
innerAttrs = layerData.innerAttributes || {},
outerAttrs = layerData.outerAttributes || {};
if( generateProperties.isStatic ) {
layerIndex = layerCount + layerIndex;
}
switch( layerData.media ) {
case 'img': type = 'img'; break;
case 'media':
case 'html':
type = 'div'; break;
case 'post': type = 'post'; break;
}
// Get post content if not passed
if( ! post ) {
var posts = window.lsPostsJSON,
postOffset = LS_activeSlideData.properties.post_offset;
if( postOffset == -1 ) {
postOffset = LS_activeSlideIndex;
}
post = posts[postOffset] || {};
}
// Hidden layer
if(layerData.skip || layerData['hide_on_'+LS_activeScreenType] ) {
item = jQuery('