// 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 = [], LS_activeLayerPageIndex = 0, LS_activeLayerTransitionTab = 0, LS_activeScreenType = 'desktop', LS_lastSelectedLayerIndex = 0, // 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, // Flag for transformed layers due to // combo box preview, which needs to // be updated after closing the combo box. LS_comboBoxIsDirty = false, // Flag for dragging operations to better // handle layer selection in a group-select // scenario. LS_layerWasDragged = 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, // Context menu LS_contextMenuTop = 10, LS_contextMenuLeft = 10, 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, type ) { type = type || 'image/png'; 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: type }); }, 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' ); }, updateLinkPicker: function( $input ) { if( typeof $input === 'string' ) { $input = jQuery('input[name="'+$input+'"]'); } // Do nothing if no input found. Revisions and other pages // might load the Slider Builder script without an active // editor session in place. if( ! $input.length ) { return; } var $holder = $input.closest('.ls-slide-link'), inputName = $input.attr('name'), inputVal = $input.val(), isSlide = $holder.closest('.ls-slide-options').length, dataArea = isSlide ? LS_activeSlideData.properties : LS_activeLayerDataSet[0], $linkIdInput = $holder.find('input[name="linkId"]'), $linkNameInput = $holder.find('input[name="linkName"]'), $linkTypeInput = $holder.find('input[name="linkType"]'), linkId = $linkIdInput.val(), linkName = $linkNameInput.val(), linkType = $linkTypeInput.val(), l10nKey; // Normalize HTML entities linkName = jQuery('