text: (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  var panels = window.panels;
  
  module.exports = Backbone.Collection.extend( {
  	model: panels.model.cell,
  
  	initialize: function () {
  	},
  
  	/**
  	 * Get the total weight for the cells in this collection.
  	 * @returns {number}
  	 */
  	totalWeight: function () {
  		var totalWeight = 0;
  		this.each( function ( cell ) {
  			totalWeight += cell.get( 'weight' );
  		} );
  
  		return totalWeight;
  	},
  
  } );
  
  },{}],2:[function(require,module,exports){
  var panels = window.panels;
  
  module.exports = Backbone.Collection.extend( {
  	model: panels.model.historyEntry,
  
  	/**
  	 * The builder model
  	 */
  	builder: null,
  
  	/**
  	 * The maximum number of items in the history
  	 */
  	maxSize: 12,
  
  	initialize: function () {
  		this.on( 'add', this.onAddEntry, this );
  	},
  
  	/**
  	 * Add an entry to the collection.
  	 *
  	 * @param text The text that defines the action taken to get to this
  	 * @param data
  	 */
  	addEntry: function ( text, data ) {
  
  		if ( _.isEmpty( data ) ) {
  			data = this.builder.getPanelsData();
  		}
  
  		var entry = new panels.model.historyEntry( {
  			text: text,
  			data: JSON.stringify( data ),
  			time: parseInt( new Date().getTime() / 1000 ),
  			collection: this
  		} );
  
  		this.add( entry );
  	},
  
  	/**
  	 * Resize the collection so it's not bigger than this.maxSize
  	 */
  	onAddEntry: function ( entry ) {
  
  		if ( this.models.length > 1 ) {
  			var lastEntry = this.at( this.models.length - 2 );
  
  			if (
  				(
  					entry.get( 'text' ) === lastEntry.get( 'text' ) && entry.get( 'time' ) - lastEntry.get( 'time' ) < 15
  				) ||
  				(
  					entry.get( 'data' ) === lastEntry.get( 'data' )
  				)
  			) {
  				// If both entries have the same text and are within 20 seconds of each other, or have the same data, then remove most recent
  				this.remove( entry );
  				lastEntry.set( 'count', lastEntry.get( 'count' ) + 1 );
  			}
  		}
  
  		// Make sure that there are not to many entries in this collection
  		while ( this.models.length > this.maxSize ) {
  			this.shift();
  		}
  	}
  } );
  
  },{}],3:[function(require,module,exports){
  var panels = window.panels;
  
  module.exports = Backbone.Collection.extend( {
  	model: panels.model.row,
  
  	/**
  	 * Destroy all the rows in this collection
  	 */
  	empty: function () {
  		var model;
  		do {
  			model = this.collection.first();
  			if ( ! model ) {
  				break;
  			}
  
  			model.destroy();
  		} while ( true );
  	}
  
  } );
  
  },{}],4:[function(require,module,exports){
  var panels = window.panels;
  
  module.exports = Backbone.Collection.extend( {
  	model: panels.model.widget,
  
  	initialize: function () {
  
  	}
  
  } );
  
  },{}],5:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = panels.view.dialog.extend( {
  	dialogClass: 'so-panels-dialog-add-builder',
  
  	render: function () {
  		// Render the dialog and attach it to the builder interface
  		this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-builder' ).html(), {} ) );
  		this.$( '.so-content .siteorigin-panels-builder' ).append( this.builder.$el );
  	},
  
  	initializeDialog: function () {
  		var thisView = this;
  		this.once( 'open_dialog_complete', function () {
  			thisView.builder.initSortable();
  		} );
  
  		this.on( 'open_dialog_complete', function () {
  			thisView.builder.trigger( 'builder_resize' );
  		} );
  	}
  } );
  
  },{}],6:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = panels.view.dialog.extend( {
  
  	historyEntryTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-dialog-history-entry' ).html() ) ),
  
  	entries: {},
  	currentEntry: null,
  	revertEntry: null,
  	selectedEntry: null,
  
  	previewScrollTop: null,
  
  	dialogClass: 'so-panels-dialog-history',
  	dialogIcon: 'history',
  
  	events: {
  		'click .so-close': 'closeDialog',
  		'click .so-restore': 'restoreSelectedEntry'
  	},
  
  	initializeDialog: function () {
  		this.entries = new panels.collection.historyEntries();
  
  		this.on( 'open_dialog', this.setCurrentEntry, this );
  		this.on( 'open_dialog', this.renderHistoryEntries, this );
  	},
  
  	render: function () {
  		var thisView = this;
  
  		// Render the dialog and attach it to the builder interface
  		this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-history' ).html(), {} ) );
  
  		this.$( 'iframe.siteorigin-panels-history-iframe' ).load( function () {
  			var $$ = $( this );
  			$$.show();
  
  			$$.contents().scrollTop( thisView.previewScrollTop );
  		} );
  	},
  
  	/**
  	 * Set the original entry. This should be set when creating the dialog.
  	 *
  	 * @param {panels.model.builder} builder
  	 */
  	setRevertEntry: function ( builder ) {
  		this.revertEntry = new panels.model.historyEntry( {
  			data: JSON.stringify( builder.getPanelsData() ),
  			time: parseInt( new Date().getTime() / 1000 )
  		} );
  	},
  
  	/**
  	 * This is triggered when the dialog is opened.
  	 */
  	setCurrentEntry: function () {
  		this.currentEntry = new panels.model.historyEntry( {
  			data: JSON.stringify( this.builder.model.getPanelsData() ),
  			time: parseInt( new Date().getTime() / 1000 )
  		} );
  
  		this.selectedEntry = this.currentEntry;
  		this.previewEntry( this.currentEntry );
  		this.$( '.so-buttons .so-restore' ).addClass( 'disabled' );
  	},
  
  	/**
  	 * Render the history entries in the sidebar
  	 */
  	renderHistoryEntries: function () {
  		// Set up an interval that will display the time since every 10 seconds
  		var thisView = this;
  
  		var c = this.$( '.history-entries' ).empty();
  
  		if ( this.currentEntry.get( 'data' ) !== this.revertEntry.get( 'data' ) || ! _.isEmpty( this.entries.models ) ) {
  			$( this.historyEntryTemplate( {title: panelsOptions.loc.history.revert, count: 1} ) )
  				.data( 'historyEntry', this.revertEntry )
  				.prependTo( c );
  		}
  
  		// Now load all the entries in this.entries
  		this.entries.each( function ( entry ) {
  
  			var html = thisView.historyEntryTemplate( {
  				title: panelsOptions.loc.history[entry.get( 'text' )],
  				count: entry.get( 'count' )
  			} );
  
  			$( html )
  				.data( 'historyEntry', entry )
  				.prependTo( c );
  		} );
  
  
  		$( this.historyEntryTemplate( {title: panelsOptions.loc.history['current'], count: 1} ) )
  			.data( 'historyEntry', this.currentEntry )
  			.addClass( 'so-selected' )
  			.prependTo( c );
  
  		// Handle loading and selecting
  		c.find( '.history-entry' ).click( function () {
  			var $$ = jQuery( this );
  			c.find( '.history-entry' ).not( $$ ).removeClass( 'so-selected' );
  			$$.addClass( 'so-selected' );
  
  			var entry = $$.data( 'historyEntry' );
  
  			thisView.selectedEntry = entry;
  
  			if ( thisView.selectedEntry.cid !== thisView.currentEntry.cid ) {
  				thisView.$( '.so-buttons .so-restore' ).removeClass( 'disabled' );
  			} else {
  				thisView.$( '.so-buttons .so-restore' ).addClass( 'disabled' );
  			}
  
  			thisView.previewEntry( entry );
  		} );
  
  		this.updateEntryTimes();
  	},
  
  	/**
  	 * Preview an entry
  	 *
  	 * @param entry
  	 */
  	previewEntry: function ( entry ) {
  		var iframe = this.$( 'iframe.siteorigin-panels-history-iframe' );
  		iframe.hide();
  		this.previewScrollTop = iframe.contents().scrollTop();
  
  		this.$( 'form.history-form input[name="live_editor_panels_data"]' ).val( entry.get( 'data' ) );
  		this.$( 'form.history-form input[name="live_editor_post_ID"]' ).val( this.builder.config.postId );
  		this.$( 'form.history-form' ).submit();
  	},
  
  	/**
  	 * Restore the current entry
  	 */
  	restoreSelectedEntry: function () {
  
  		if ( this.$( '.so-buttons .so-restore' ).hasClass( 'disabled' ) ) {
  			return false;
  		}
  
  		if ( this.currentEntry.get( 'data' ) === this.selectedEntry.get( 'data' ) ) {
  			this.closeDialog();
  			return false;
  		}
  
  		// Add an entry for this restore event
  		if ( this.selectedEntry.get( 'text' ) !== 'restore' ) {
  			this.builder.addHistoryEntry( 'restore', this.builder.model.getPanelsData() );
  		}
  
  		this.builder.model.loadPanelsData( JSON.parse( this.selectedEntry.get( 'data' ) ) );
  
  		this.closeDialog();
  
  		return false;
  	},
  
  	/**
  	 * Update the entry times for the list of entries down the side
  	 */
  	updateEntryTimes: function () {
  		var thisView = this;
  
  		this.$( '.history-entries .history-entry' ).each( function () {
  			var $$ = jQuery( this );
  
  			var time = $$.find( '.timesince' );
  			var entry = $$.data( 'historyEntry' );
  
  			time.html( thisView.timeSince( entry.get( 'time' ) ) );
  		} );
  	},
  
  	/**
  	 * Gets the time since as a nice string.
  	 *
  	 * @param date
  	 */
  	timeSince: function ( time ) {
  		var diff = parseInt( new Date().getTime() / 1000 ) - time;
  
  		var parts = [];
  		var interval;
  
  		// There are 3600 seconds in an hour
  		if ( diff > 3600 ) {
  			interval = Math.floor( diff / 3600 );
  			if ( interval === 1 ) {
  				parts.push( panelsOptions.loc.time.hour.replace( '%d', interval ) );
  			} else {
  				parts.push( panelsOptions.loc.time.hours.replace( '%d', interval ) );
  			}
  			diff -= interval * 3600;
  		}
  
  		// There are 60 seconds in a minute
  		if ( diff > 60 ) {
  			interval = Math.floor( diff / 60 );
  			if ( interval === 1 ) {
  				parts.push( panelsOptions.loc.time.minute.replace( '%d', interval ) );
  			} else {
  				parts.push( panelsOptions.loc.time.minutes.replace( '%d', interval ) );
  			}
  			diff -= interval * 60;
  		}
  
  		if ( diff > 0 ) {
  			if ( diff === 1 ) {
  				parts.push( panelsOptions.loc.time.second.replace( '%d', diff ) );
  			} else {
  				parts.push( panelsOptions.loc.time.seconds.replace( '%d', diff ) );
  			}
  		}
  
  		// Return the amount of time ago
  		return _.isEmpty( parts ) ? panelsOptions.loc.time.now : panelsOptions.loc.time.ago.replace( '%s', parts.slice( 0, 2 ).join( ', ' ) );
  
  	}
  
  } );
  
  },{}],7:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = panels.view.dialog.extend( {
  
  	directoryTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-directory-items' ).html() ) ),
  
  	builder: null,
  	dialogClass: 'so-panels-dialog-prebuilt-layouts',
  	dialogIcon: 'layouts',
  
  	layoutCache: {},
  	currentTab: false,
  	directoryPage: 1,
  
  	events: {
  		'click .so-close': 'closeDialog',
  		'click .so-sidebar-tabs li a': 'tabClickHandler',
  		'click .so-content .layout': 'layoutClickHandler',
  		'keyup .so-sidebar-search': 'searchHandler',
  
  		// The directory items
  		'click .so-screenshot, .so-title': 'directoryItemClickHandler'
  	},
  
  	/**
  	 * Initialize the prebuilt dialog.
  	 */
  	initializeDialog: function () {
  		var thisView = this;
  
  		this.on( 'open_dialog', function () {
  			thisView.$( '.so-sidebar-tabs li a' ).first().click();
  			thisView.$( '.so-status' ).removeClass( 'so-panels-loading' );
  		} );
  
  		this.on( 'button_click', this.toolbarButtonClick, this );
  	},
  
  	/**
  	 * Render the prebuilt layouts dialog
  	 */
  	render: function () {
  		this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-prebuilt' ).html(), {} ) );
  
  		this.initToolbar();
  	},
  
  	/**
  	 *
  	 * @param e
  	 * @return {boolean}
  	 */
  	tabClickHandler: function ( e ) {
  		e.preventDefault();
  		// Reset selected item state when changing tabs
  		this.selectedLayoutItem = null;
  		this.uploadedLayout = null;
  		this.updateButtonState( false );
  
  		this.$( '.so-sidebar-tabs li' ).removeClass( 'tab-active' );
  
  		var $$ = $( e.target );
  		var tab = $$.attr( 'href' ).split( '#' )[1];
  		$$.parent().addClass( 'tab-active' );
  
  		var thisView = this;
  
  		// Empty everything
  		this.$( '.so-content' ).empty();
  
  		thisView.currentTab = tab;
  		if ( tab == 'import' ) {
  			this.displayImportExport();
  		} else {
  			this.displayLayoutDirectory( '', 1, tab );
  		}
  
  		thisView.$( '.so-sidebar-search' ).val( '' );
  	},
  
  	/**
  	 * Display and setup the import/export form
  	 */
  	displayImportExport: function () {
  		var c = this.$( '.so-content' ).empty().removeClass( 'so-panels-loading' );
  		c.html( $( '#siteorigin-panels-dialog-prebuilt-importexport' ).html() );
  
  		var thisView = this;
  		var uploadUi = thisView.$( '.import-upload-ui' ).hide();
  
  		// Create the uploader
  		var uploader = new plupload.Uploader( {
  			runtimes: 'html5,silverlight,flash,html4',
  
  			browse_button: uploadUi.find( '.file-browse-button' ).get( 0 ),
  			container: uploadUi.get( 0 ),
  			drop_element: uploadUi.find( '.drag-upload-area' ).get( 0 ),
  
  			file_data_name: 'panels_import_data',
  			multiple_queues: false,
  			max_file_size: panelsOptions.plupload.max_file_size,
  			url: panelsOptions.plupload.url,
  			flash_swf_url: panelsOptions.plupload.flash_swf_url,
  			silverlight_xap_url: panelsOptions.plupload.silverlight_xap_url,
  			filters: [
  				{title: panelsOptions.plupload.filter_title, extensions: 'json'}
  			],
  
  			multipart_params: {
  				action: 'so_panels_import_layout'
  			},
  
  			init: {
  				PostInit: function ( uploader ) {
  					if ( uploader.features.dragdrop ) {
  						uploadUi.addClass( 'has-drag-drop' );
  					}
  					uploadUi.show().find( '.progress-precent' ).css( 'width', '0%' );
  				},
  				FilesAdded: function ( uploader ) {
  					uploadUi.find( '.file-browse-button' ).blur();
  					uploadUi.find( '.drag-upload-area' ).removeClass( 'file-dragover' );
  					uploadUi.find( '.progress-bar' ).fadeIn( 'fast' );
  					thisView.$( '.js-so-selected-file' ).text( panelsOptions.loc.prebuilt_loading );
  					uploader.start();
  				},
  				UploadProgress: function ( uploader, file ) {
  					uploadUi.find( '.progress-precent' ).css( 'width', file.percent + '%' );
  				},
  				FileUploaded: function ( uploader, file, response ) {
  					var layout = JSON.parse( response.response );
  					if ( ! _.isUndefined( layout.widgets ) ) {
  
  						thisView.uploadedLayout = layout;
  						uploadUi.find( '.progress-bar' ).hide();
  						thisView.$( '.js-so-selected-file' ).text(
  							panelsOptions.loc.ready_to_insert.replace( '%s', file.name )
  						);
  						thisView.updateButtonState( true );
  					} else {
  						alert( panelsOptions.plupload.error_message );
  					}
  				},
  				Error: function () {
  					alert( panelsOptions.plupload.error_message );
  				}
  			}
  		} );
  		uploader.init();
  
  		// This is
  		uploadUi.find( '.drag-upload-area' )
  			.on( 'dragover', function () {
  				$( this ).addClass( 'file-dragover' );
  			} )
  			.on( 'dragleave', function () {
  				$( this ).removeClass( 'file-dragover' );
  			} );
  
  		// Handle exporting the file
  		c.find( '.so-export' ).submit( function ( e ) {
  			var $$ = $( this );
  			$$.find( 'input[name="panels_export_data"]' ).val( JSON.stringify( thisView.builder.model.getPanelsData() ) );
  		} );
  
  	},
  
  	/**
  	 * Display the layout directory tab.
  	 *
  	 * @param query
  	 */
  	displayLayoutDirectory: function ( search, page, type ) {
  		var thisView = this;
  		var c = this.$( '.so-content' ).empty().addClass( 'so-panels-loading' );
  
  		if ( search === undefined ) {
  			search = '';
  		}
  		if ( page === undefined ) {
  			page = 1;
  		}
  		if ( type === undefined ) {
  			type = 'directory-siteorigin';
  		}
  
  		if ( type.match('^directory-') && ! panelsOptions.directory_enabled ) {
  			// Display the button to enable the prebuilt layout
  			c.removeClass( 'so-panels-loading' ).html( $( '#siteorigin-panels-directory-enable' ).html() );
  			c.find( '.so-panels-enable-directory' ).click( function ( e ) {
  				e.preventDefault();
  				// Sent the query to enable the directory, then enable the directory
  				$.get(
  					panelsOptions.ajaxurl,
  					{action: 'so_panels_directory_enable'},
  					function () {
  
  					}
  				);
  
  				// Enable the layout directory
  				panelsOptions.directory_enabled = true;
  				c.addClass( 'so-panels-loading' );
  				thisView.displayLayoutDirectory( search, page, type );
  			} );
  			return;
  		}
  
  		// Get all the items for the current query
  		$.get(
  			panelsOptions.ajaxurl,
  			{
  				action: 'so_panels_layouts_query',
  				search: search,
  				page: page,
  				type: type,
  			},
  			function ( data ) {
  				// Skip this if we're no longer viewing the layout directory
  				if ( thisView.currentTab !== type ) {
  					return;
  				}
  
  				// Add the directory items
  				c.removeClass( 'so-panels-loading' ).html( thisView.directoryTemplate( data ) );
  
  				// Lets setup the next and previous buttons
  				var prev = c.find( '.so-previous' ), next = c.find( '.so-next' );
  
  				if ( page <= 1 ) {
  					prev.addClass( 'button-disabled' );
  				} else {
  					prev.click( function ( e ) {
  						e.preventDefault();
  						thisView.displayLayoutDirectory( search, page - 1, thisView.currentTab );
  					} );
  				}
  
  				if ( page === data.max_num_pages || data.max_num_pages === 0 ) {
  					next.addClass( 'button-disabled' );
  				} else {
  					next.click( function ( e ) {
  						e.preventDefault();
  						thisView.displayLayoutDirectory( search, page + 1, thisView.currentTab );
  					} );
  				}
  
  				// Handle nice preloading of the screenshots
  				c.find( '.so-screenshot' ).each( function () {
  					var $$ = $( this ), $a = $$.find( '.so-screenshot-wrapper' );
  					$a.css( 'height', ( $a.width() / 4 * 3 ) + 'px' ).addClass( 'so-loading' );
  
  					if ( $$.data( 'src' ) !== '' ) {
  						// Set the initial height
  						var $img = $( '<img/>' ).attr( 'src', $$.data( 'src' ) ).load( function () {
  							$a.removeClass( 'so-loading' ).css( 'height', 'auto' );
  							$img.appendTo( $a ).hide().fadeIn( 'fast' );
  						} );
  					} else {
  						$( '<img/>' ).attr( 'src', panelsOptions.prebuiltDefaultScreenshot ).appendTo( $a ).hide().fadeIn( 'fast' );
  					}
  
  				} );
  
  				// Set the title
  				c.find( '.so-directory-browse' ).html( data.title );
  			},
  			'json'
  		);
  	},
  
  	/**
  	 * Set the selected state for the clicked layout directory item and remove previously selected item.
  	 * Enable the toolbar buttons.
  	 */
  	directoryItemClickHandler: function ( e ) {
  		var $directoryItem = this.$( e.target ).closest( '.so-directory-item' );
  		this.$( '.so-directory-items' ).find( '.selected' ).removeClass( 'selected' );
  		$directoryItem.addClass( 'selected' );
  		this.selectedLayoutItem = {lid: $directoryItem.data( 'layout-id' ), type: $directoryItem.data( 'layout-type' )};
  		this.updateButtonState( true );
  
  	},
  
  	/**
  	 * Load a particular layout into the builder.
  	 *
  	 * @param id
  	 */
  	toolbarButtonClick: function ( $button ) {
  		if ( ! this.canAddLayout() ) {
  			return false;
  		}
  		var position = $button.data( 'value' );
  		if ( _.isUndefined( position ) ) {
  			return false;
  		}
  		this.updateButtonState( false );
  
  		if ( $button.hasClass( 'so-needs-confirm' ) && ! $button.hasClass( 'so-confirmed' ) ) {
  			this.updateButtonState( true );
  			if ( $button.hasClass( 'so-confirming' ) ) {
  				return;
  			}
  			$button.addClass( 'so-confirming' );
  			var originalText = $button.html();
  			$button.html( '<span class="dashicons dashicons-yes"></span>' + $button.data( 'confirm' ) );
  			setTimeout( function () {
  				$button.removeClass( 'so-confirmed' ).html( originalText );
  			}, 2500 );
  			setTimeout( function () {
  				$button.removeClass( 'so-confirming' );
  				$button.addClass( 'so-confirmed' );
  			}, 200 );
  			return false;
  		}
  		this.addingLayout = true;
  		if ( this.currentTab === 'import' ) {
  			this.addLayoutToBuilder( this.uploadedLayout, position );
  		} else {
  			this.loadSelectedLayout().then( function ( layout ) {
  				this.addLayoutToBuilder( layout, position );
  			}.bind( this ) );
  		}
  	},
  
  	canAddLayout: function () {
  		return (
  			   this.selectedLayoutItem || this.uploadedLayout
  			   ) && ! this.addingLayout;
  	},
  
  	/**
  	 * Load the layout according to selectedLayoutItem.
  	 */
  	loadSelectedLayout: function () {
  		this.setStatusMessage( panelsOptions.loc.prebuilt_loading, true );
  
  		var args = _.extend( this.selectedLayoutItem, {action: 'so_panels_get_layout'} );
  		var deferredLayout = new $.Deferred();
  
  		$.get(
  			panelsOptions.ajaxurl,
  			args,
  			function ( layout ) {
  				if ( layout.error !== undefined ) {
  					// There was an error
  					alert( layout.error );
  					deferredLayout.reject( layout );
  				} else {
  					this.setStatusMessage( '', false );
  					deferredLayout.resolve( layout );
  				}
  			}.bind( this )
  		);
  		return deferredLayout.promise();
  	},
  
  	/**
  	 * Handle an update to the search
  	 */
  	searchHandler: function ( e ) {
  		if ( e.keyCode === 13 ) {
  			this.displayLayoutDirectory( $( e.currentTarget ).val(), 1, this.currentTab );
  		}
  	},
  
  	/**
  	 * Attempt to set the 'Insert' button's state according to the `enabled` argument, also checking whether the
  	 * requirements for inserting a layout have valid values.
  	 */
  	updateButtonState: function ( enabled ) {
  		enabled = enabled && (
  			this.selectedLayoutItem || this.uploadedLayout
  			);
  		var $button = this.$( '.so-import-layout' );
  		$button.prop( "disabled", ! enabled );
  		if ( enabled ) {
  			$button.removeClass( 'disabled' );
  		} else {
  			$button.addClass( 'disabled' );
  		}
  	},
  
  	addLayoutToBuilder: function ( layout, position ) {
  		this.builder.addHistoryEntry( 'prebuilt_loaded' );
  		this.builder.model.loadPanelsData( layout, position );
  		this.addingLayout = false;
  		this.closeDialog();
  	}
  } );
  
  },{}],8:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = panels.view.dialog.extend({
  
  	cellPreviewTemplate: _.template( panels.helpers.utils.processTemplate( $('#siteorigin-panels-dialog-row-cell-preview').html() ) ),
  
  	editableLabel: true,
  
  	events: {
  		'click .so-close': 'closeDialog',
  
  		// Toolbar buttons
  		'click .so-toolbar .so-save': 'saveHandler',
  		'click .so-toolbar .so-insert': 'insertHandler',
  		'click .so-toolbar .so-delete': 'deleteHandler',
  		'click .so-toolbar .so-duplicate': 'duplicateHandler',
  
  		// Changing the row
  		'change .row-set-form > *': 'setCellsFromForm',
  		'click .row-set-form button.set-row': 'setCellsFromForm',
  	},
  
  	dialogIcon: 'add-row',
  	dialogClass: 'so-panels-dialog-row-edit',
  	styleType: 'row',
  
  	dialogType: 'edit',
  
  	/**
  	 * The current settings, not yet saved to the model
  	 */
  	row: {
  		// This will be a clone of cells collection.
  		cells: null,
  		// The style settings of the row
  		style: {}
  	},
  
  	cellStylesCache: [],
  
  	initializeDialog: function () {
  		this.on('open_dialog', function () {
  			if (!_.isUndefined(this.model) && !_.isEmpty(this.model.get('cells'))) {
  				this.setRowModel(this.model);
  			} else {
  				this.setRowModel(null);
  			}
  
  			this.regenerateRowPreview();
  		}, this);
  
  		// This is the default row layout
  		this.row = {
  			cells: new panels.collection.cells([{weight: 0.5}, {weight: 0.5}]),
  			style: {}
  		};
  
  		// Refresh panels data after both dialog form components are loaded
  		this.dialogFormsLoaded = 0;
  		var thisView = this;
  		this.on('form_loaded styles_loaded', function () {
  			this.dialogFormsLoaded++;
  			if (this.dialogFormsLoaded === 2) {
  				thisView.updateModel({
  					refreshArgs: {
  						silent: true
  					}
  				});
  			}
  		});
  
  		this.on('close_dialog', this.closeHandler);
  
  		this.on( 'edit_label', function ( text ) {
  			// If text is set to default values, just clear it.
  			if ( text === panelsOptions.loc.row.add || text === panelsOptions.loc.row.edit ) {
  				text = '';
  			}
  			this.model.set( 'label', text );
  			if ( _.isEmpty( text ) ) {
  				var title = this.dialogType === 'create' ? panelsOptions.loc.row.add : panelsOptions.loc.row.edit;
  				this.$( '.so-title').text( title );
  			}
  		}.bind( this ) );
  	},
  
  	/**
  	 *
  	 * @param dialogType Either "edit" or "create"
  	 */
  	setRowDialogType: function (dialogType) {
  		this.dialogType = dialogType;
  	},
  
  	/**
  	 * Render the new row dialog
  	 */
  	render: function () {
  		var title = this.dialogType === 'create' ? panelsOptions.loc.row.add : panelsOptions.loc.row.edit;
  		this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-row' ).html(), {
  			title: title,
  			dialogType: this.dialogType
  		} ) );
  
  		var titleElt = this.$( '.so-title' );
  
  		if ( this.model.has( 'label' ) && ! _.isEmpty( this.model.get( 'label' ) ) ) {
  			titleElt.text( this.model.get( 'label' ) );
  		}
  		this.$( '.so-edit-title' ).val( titleElt.text() );
  
  		// Now we need to attach the style window
  		this.styles = new panels.view.styles();
  		this.styles.model = this.model;
  		this.styles.render('row', this.builder.config.postId, {
  			builderType: this.builder.config.builderType,
  			dialog: this
  		});
  
  		if (!this.builder.supports('addRow')) {
  			this.$('.so-buttons .so-duplicate').remove();
  		}
  		if (!this.builder.supports('deleteRow')) {
  			this.$('.so-buttons .so-delete').remove();
  		}
  
  		var $rightSidebar = this.$('.so-sidebar.so-right-sidebar');
  		this.styles.attach($rightSidebar);
  
  		// Handle the loading class
  		this.styles.on('styles_loaded', function (hasStyles) {
  			// If we have styles remove the loading spinner, else remove the whole empty sidebar.
  			if (hasStyles) {
  				$rightSidebar.removeClass('so-panels-loading');
  			} else {
  				$rightSidebar.closest('.so-panels-dialog').removeClass('so-panels-dialog-has-right-sidebar');
  				$rightSidebar.remove();
  			}
  		}, this);
  		$rightSidebar.addClass('so-panels-loading');
  
  		if (!_.isUndefined(this.model)) {
  			// Set the initial value of the
  			this.$('input.so-row-field').val(this.model.get('cells').length);
  		}
  
  		this.$('input.so-row-field').keyup(function () {
  			$(this).trigger('change');
  		});
  
  		return this;
  	},
  
  	/**
  	 * Set the row model we'll be using for this dialog.
  	 *
  	 * @param model
  	 */
  	setRowModel: function (model) {
  		this.model = model;
  
  		if (_.isEmpty(this.model)) {
  			return this;
  		}
  
  		// Set the rows to be a copy of the model
  		this.row = {
  			cells: this.model.get('cells').clone(),
  			style: {}
  		};
  
  		// Set the initial value of the cell field.
  		this.$('input.so-row-field').val(this.model.get('cells').length);
  
  		this.clearCellStylesCache();
  
  		return this;
  	},
  
  	/**
  	 * Regenerate the row preview and resizing interface.
  	 */
  	regenerateRowPreview: function () {
  		var thisDialog = this;
  		var rowPreview = this.$('.row-preview');
  
  		// If no selected cell, select the first cell.
  		var selectedIndex = this.getSelectedCellIndex();
  
  		rowPreview.empty();
  
  		var timeout;
  
  		// Represent the cells
  		this.row.cells.each(function (cellModel, i) {
  			var newCell = $(this.cellPreviewTemplate({weight: cellModel.get('weight')}));
  			rowPreview.append(newCell);
  
  			if(i == selectedIndex) {
  				newCell.find('.preview-cell-in').addClass('cell-selected');
  			}
  
  			var prevCell = newCell.prev();
  			var handle;
  
  			if (prevCell.length) {
  				handle = $('<div class="resize-handle"></div>');
  				handle
  					.appendTo(newCell)
  					.dblclick(function () {
  						var prevCellModel = thisDialog.row.cells.at(i - 1);
  						var t = cellModel.get('weight') + prevCellModel.get('weight');
  						cellModel.set('weight', t / 2);
  						prevCellModel.set('weight', t / 2);
  						thisDialog.scaleRowWidths();
  					});
  
  				handle.draggable({
  					axis: 'x',
  					containment: rowPreview,
  					start: function (e, ui) {
  
  						// Create the clone for the current cell
  						var newCellClone = newCell.clone().appendTo(ui.helper).css({
  							position: 'absolute',
  							top: '0',
  							width: newCell.outerWidth(),
  							left: 6,
  							height: newCell.outerHeight()
  						});
  						newCellClone.find('.resize-handle').remove();
  
  						// Create the clone for the previous cell
  						var prevCellClone = prevCell.clone().appendTo(ui.helper).css({
  							position: 'absolute',
  							top: '0',
  							width: prevCell.outerWidth(),
  							right: 6,
  							height: prevCell.outerHeight()
  						});
  						prevCellClone.find('.resize-handle').remove();
  
  						$(this).data({
  							'newCellClone': newCellClone,
  							'prevCellClone': prevCellClone
  						});
  
  						// Hide the
  						newCell.find('> .preview-cell-in').css('visibility', 'hidden');
  						prevCell.find('> .preview-cell-in').css('visibility', 'hidden');
  					},
  					drag: function (e, ui) {
  						// Calculate the new cell and previous cell widths as a percent
  						var cellWeight = thisDialog.row.cells.at(i).get('weight');
  						var prevCellWeight = thisDialog.row.cells.at(i - 1).get('weight');
  						var ncw = cellWeight - (
  								(
  									ui.position.left + 6
  								) / rowPreview.width()
  							);
  						var pcw = prevCellWeight + (
  								(
  									ui.position.left + 6
  								) / rowPreview.width()
  							);
  
  						var helperLeft = ui.helper.offset().left - rowPreview.offset().left - 6;
  
  						$(this).data('newCellClone').css('width', rowPreview.width() * ncw)
  							.find('.preview-cell-weight').html(Math.round(ncw * 1000) / 10);
  
  						$(this).data('prevCellClone').css('width', rowPreview.width() * pcw)
  							.find('.preview-cell-weight').html(Math.round(pcw * 1000) / 10);
  					},
  					stop: function (e, ui) {
  						// Remove the clones
  						$(this).data('newCellClone').remove();
  						$(this).data('prevCellClone').remove();
  
  						// Reshow the main cells
  						newCell.find('.preview-cell-in').css('visibility', 'visible');
  						prevCell.find('.preview-cell-in').css('visibility', 'visible');
  
  						// Calculate the new cell weights
  						var offset = ui.position.left + 6;
  						var percent = offset / rowPreview.width();
  
  						// Ignore this if any of the cells are below 2% in width.
  						var cellModel = thisDialog.row.cells.at(i);
  						var prevCellModel = thisDialog.row.cells.at(i - 1);
  						if (cellModel.get('weight') - percent > 0.02 && prevCellModel.get('weight') + percent > 0.02) {
  							cellModel.set('weight', cellModel.get('weight') - percent);
  							prevCellModel.set('weight', prevCellModel.get('weight') + percent);
  						}
  
  						thisDialog.scaleRowWidths();
  						ui.helper.css('left', -6);
  					}
  				});
  			}
  
  			newCell.click(function (event) {
  
  				if ( ! ( $(event.target).is('.preview-cell') || $(event.target).is('.preview-cell-in') ) ) {
  					return;
  				}
  
  				var cell = $(event.target);
  				cell.closest('.row-preview').find('.preview-cell .preview-cell-in').removeClass('cell-selected');
  				cell.addClass('cell-selected');
  
  				this.openSelectedCellStyles();
  
  			}.bind(this));
  
  			// Make this row weight click editable
  			newCell.find('.preview-cell-weight').click(function (ci) {
  
  				// Disable the draggable while entering values
  				thisDialog.$('.resize-handle').css('pointer-event', 'none').draggable('disable');
  
  				rowPreview.find('.preview-cell-weight').each(function () {
  					var $$ = jQuery(this).hide();
  					$('<input type="text" class="preview-cell-weight-input no-user-interacted" />')
  						.val(parseFloat($$.html())).insertAfter($$)
  						.focus(function () {
  							clearTimeout(timeout);
  						})
  						.keyup(function (e) {
  							if (e.keyCode !== 9) {
  								// Only register the interaction if the user didn't press tab
  								$(this).removeClass('no-user-interacted');
  							}
  
  							// Enter is clicked
  							if (e.keyCode === 13) {
  								e.preventDefault();
  								$(this).blur();
  							}
  						})
  						.keydown(function (e) {
  							if (e.keyCode === 9) {
  								e.preventDefault();
  
  								// Tab will always cycle around the row inputs
  								var inputs = rowPreview.find('.preview-cell-weight-input');
  								var i = inputs.index($(this));
  								if (i === inputs.length - 1) {
  									inputs.eq(0).focus().select();
  								} else {
  									inputs.eq(i + 1).focus().select();
  								}
  							}
  						})
  						.blur(function () {
  							rowPreview.find('.preview-cell-weight-input').each(function (i, el) {
  								if (isNaN(parseFloat($(el).val()))) {
  									$(el).val(Math.floor(thisDialog.row.cells.at(i).get('weight') * 1000) / 10);
  								}
  							});
  
  							timeout = setTimeout(function () {
  								// If there are no weight inputs, then skip this
  								if (rowPreview.find('.preview-cell-weight-input').length === 0) {
  									return false;
  								}
  
  								// Go through all the inputs
  								var rowWeights = [],
  									rowChanged = [],
  									changedSum = 0,
  									unchangedSum = 0;
  
  								rowPreview.find('.preview-cell-weight-input').each(function (i, el) {
  									var val = parseFloat($(el).val());
  									if (isNaN(val)) {
  										val = 1 / thisDialog.row.cells.length;
  									} else {
  										val = Math.round(val * 10) / 1000;
  									}
  
  									// Check within 3 decimal points
  									var changed = !$(el).hasClass('no-user-interacted');
  
  									rowWeights.push(val);
  									rowChanged.push(changed);
  
  									if (changed) {
  										changedSum += val;
  									} else {
  										unchangedSum += val;
  									}
  								});
  
  								if (changedSum > 0 && unchangedSum > 0 && (
  										1 - changedSum
  									) > 0) {
  									// Balance out the unchanged rows to occupy the weight left over by the changed sum
  									for (var i = 0; i < rowWeights.length; i++) {
  										if (!rowChanged[i]) {
  											rowWeights[i] = (
  													rowWeights[i] / unchangedSum
  												) * (
  													1 - changedSum
  												);
  										}
  									}
  								}
  
  								// Last check to ensure total weight is 1
  								var sum = _.reduce(rowWeights, function (memo, num) {
  									return memo + num;
  								});
  								rowWeights = rowWeights.map(function (w) {
  									return w / sum;
  								});
  
  								// Set the new cell weights and regenerate the preview.
  								if (Math.min.apply(Math, rowWeights) > 0.01) {
  									thisDialog.row.cells.each(function (cell, i) {
  										cell.set('weight', rowWeights[i]);
  									});
  								}
  
  								// Now lets animate the cells into their new widths
  								rowPreview.find('.preview-cell').each(function (i, el) {
  									var cellWeight = thisDialog.row.cells.at(i).get('weight');
  									$(el).animate({'width': Math.round(cellWeight * 1000) / 10 + "%"}, 250);
  									$(el).find('.preview-cell-weight-input').val(Math.round(cellWeight * 1000) / 10);
  								});
  
  								// So the draggable handle is not hidden.
  								rowPreview.find('.preview-cell').css('overflow', 'visible');
  
  								setTimeout(function () {
  									thisDialog.regenerateRowPreview();
  								}, 260);
  
  							}, 100);
  						})
  						.click(function () {
  							$(this).select();
  						});
  				});
  
  				$(this).siblings('.preview-cell-weight-input').select();
  
  			});
  
  		}, this);
  
  		this.openSelectedCellStyles();
  
  		this.trigger('form_loaded', this);
  	},
  
  	getSelectedCellIndex: function() {
  		var selectedIndex = -1;
  		this.$('.preview-cell .preview-cell-in').each(function(index, el) {
  			if($(el).is('.cell-selected')) {
  				selectedIndex = index;
  			}
  		});
  		return selectedIndex;
  	},
  
  	openSelectedCellStyles: function() {
  		if (!_.isUndefined(this.cellStyles)) {
  			if (this.cellStyles.stylesLoaded) {
  				var style = {};
  				try {
  					style = this.getFormValues('.so-sidebar .so-visual-styles.so-cell-styles').style;
  				}
  				catch (err) {
  					console.log('Error retrieving cell styles - ' + err.message);
  				}
  
  				this.cellStyles.model.set('style', style);
  			}
  			this.cellStyles.detach();
  		}
  
  		this.cellStyles = this.getSelectedCellStyles();
  
  		if ( this.cellStyles ) {
  			var $rightSidebar = this.$( '.so-sidebar.so-right-sidebar' );
  			this.cellStyles.attach( $rightSidebar );
  
  			if ( !this.cellStyles.stylesLoaded ) {
  				this.cellStyles.on( 'styles_loaded', function () {
  					$rightSidebar.removeClass( 'so-panels-loading' );
  				}, this );
  				$rightSidebar.addClass( 'so-panels-loading' );
  			}
  		}
  	},
  
  	getSelectedCellStyles: function () {
  		var cellIndex = this.getSelectedCellIndex();
  		if ( cellIndex > -1 ) {
  			var cellStyles = this.cellStylesCache[cellIndex];
  			if ( !cellStyles ) {
  				cellStyles = new panels.view.styles();
  				cellStyles.model = this.row.cells.at( cellIndex );
  				cellStyles.render( 'cell', this.builder.config.postId, {
  					builderType: this.builder.config.builderType,
  					dialog: this,
  					index: cellIndex,
  				} );
  				this.cellStylesCache[cellIndex] = cellStyles;
  			}
  		}
  
  		return cellStyles;
  	},
  
  	clearCellStylesCache: function () {
  		// Call remove() on all cell styles to remove data, event listeners etc.
  		this.cellStylesCache.forEach(function (cellStyles) {
  			cellStyles.remove();
  		});
  		this.cellStylesCache = [];
  	},
  
  	/**
  	 * Visually scale the row widths based on the cell weights
  	 */
  	scaleRowWidths: function () {
  		var thisDialog = this;
  		this.$('.row-preview .preview-cell').each(function (i, el) {
  			var cell = thisDialog.row.cells.at(i);
  			$(el)
  				.css('width', cell.get('weight') * 100 + "%")
  				.find('.preview-cell-weight').html(Math.round(cell.get('weight') * 1000) / 10);
  		});
  	},
  
  	/**
  	 * Get the weights from the
  	 */
  	setCellsFromForm: function () {
  
  		try {
  			var f = {
  				'cells': parseInt(this.$('.row-set-form input[name="cells"]').val()),
  				'ratio': parseFloat(this.$('.row-set-form select[name="ratio"]').val()),
  				'direction': this.$('.row-set-form select[name="ratio_direction"]').val()
  			};
  
  			if (_.isNaN(f.cells)) {
  				f.cells = 1;
  			}
  			if (isNaN(f.ratio)) {
  				f.ratio = 1;
  			}
  			if (f.cells < 1) {
  				f.cells = 1;
  				this.$('.row-set-form input[name="cells"]').val(f.cells);
  			}
  			else if (f.cells > 12) {
  				f.cells = 12;
  				this.$('.row-set-form input[name="cells"]').val(f.cells);
  			}
  
  			this.$('.row-set-form input[name="ratio"]').val(f.ratio);
  
  			var cells = [];
  			var cellCountChanged = (
  				this.row.cells.length !== f.cells
  			);
  
  			// Now, lets create some cells
  			var currentWeight = 1;
  			for (var i = 0; i < f.cells; i++) {
  				cells.push(currentWeight);
  				currentWeight *= f.ratio;
  			}
  
  			// Now lets make sure that the row weights add up to 1
  
  			var totalRowWeight = _.reduce(cells, function (memo, weight) {
  				return memo + weight;
  			});
  			cells = _.map(cells, function (cell) {
  				return cell / totalRowWeight;
  			});
  
  			// Don't return cells that are too small
  			cells = _.filter(cells, function (cell) {
  				return cell > 0.01;
  			});
  
  			if (f.direction === 'left') {
  				cells = cells.reverse();
  			}
  
  			// Discard deleted cells.
  			this.row.cells = new panels.collection.cells(this.row.cells.first(cells.length));
  
  			_.each(cells, function (cellWeight, index) {
  				var cell = this.row.cells.at(index);
  				if (!cell) {
  					cell = new panels.model.cell({weight: cellWeight, row: this.model});
  					this.row.cells.add(cell);
  				} else {
  					cell.set('weight', cellWeight);
  				}
  			}.bind(this));
  
  			if (cellCountChanged) {
  				this.regenerateRowPreview();
  			} else {
  				var thisDialog = this;
  
  				// Now lets animate the cells into their new widths
  				this.$('.preview-cell').each(function (i, el) {
  					var cellWeight = thisDialog.row.cells.at(i).get('weight');
  					$(el).animate({'width': Math.round(cellWeight * 1000) / 10 + "%"}, 250);
  					$(el).find('.preview-cell-weight').html(Math.round(cellWeight * 1000) / 10);
  				});
  
  				// So the draggable handle is not hidden.
  				this.$('.preview-cell').css('overflow', 'visible');
  
  				setTimeout(function () {
  					thisDialog.regenerateRowPreview();
  				}, 260);
  			}
  		}
  		catch (err) {
  			console.log('Error setting cells - ' + err.message);
  		}
  
  
  		// Remove the button primary class
  		this.$('.row-set-form .so-button-row-set').removeClass('button-primary');
  	},
  
  	/**
  	 * Handle a click on the dialog left bar tab
  	 */
  	tabClickHandler: function ($t) {
  		if ($t.attr('href') === '#row-layout') {
  			this.$('.so-panels-dialog').addClass('so-panels-dialog-has-right-sidebar');
  		} else {
  			this.$('.so-panels-dialog').removeClass('so-panels-dialog-has-right-sidebar');
  		}
  	},
  
  	/**
  	 * Update the current model with what we have in the dialog
  	 */
  	updateModel: function (args) {
  		args = _.extend({
  			refresh: true,
  			refreshArgs: null
  		}, args);
  
  		// Set the cells
  		if (!_.isEmpty(this.model)) {
  			this.model.setCells(this.row.cells);
  		}
  
  		// Update the row styles if they've loaded
  		if (!_.isUndefined(this.styles) && this.styles.stylesLoaded) {
  			// This is an edit dialog, so there are styles
  			var style = {};
  			try {
  				style = this.getFormValues('.so-sidebar .so-visual-styles.so-row-styles').style;
  			}
  			catch (err) {
  				console.log('Error retrieving row styles - ' + err.message);
  			}
  
  			this.model.set('style', style);
  		}
  
  		// Update the cell styles if any are showing.
  		if (!_.isUndefined(this.cellStyles) && this.cellStyles.stylesLoaded) {
  
  			var style = {};
  			try {
  				style = this.getFormValues('.so-sidebar .so-visual-styles.so-cell-styles').style;
  			}
  			catch (err) {
  				console.log('Error retrieving cell styles - ' + err.message);
  			}
  
  			this.cellStyles.model.set('style', style);
  		}
  
  		if (args.refresh) {
  			this.builder.model.refreshPanelsData(args.refreshArgs);
  		}
  	},
  
  	/**
  	 * Insert the new row
  	 */
  	insertHandler: function () {
  		this.builder.addHistoryEntry('row_added');
  
  		this.updateModel();
  
  		var activeCell = this.builder.getActiveCell({
  			createCell: false,
  		});
  
  		var options = {};
  		if (activeCell !== null) {
  			options.at = this.builder.model.get('rows').indexOf(activeCell.row) + 1;
  		}
  
  		// Set up the model and add it to the builder
  		this.model.collection = this.builder.model.get('rows');
  		this.builder.model.get('rows').add(this.model, options);
  
  		this.closeDialog();
  
  		this.builder.model.refreshPanelsData();
  
  		return false;
  	},
  
  	/**
  	 * We'll just save this model and close the dialog
  	 */
  	saveHandler: function () {
  		this.builder.addHistoryEntry('row_edited');
  		this.updateModel();
  		this.closeDialog();
  
  		this.builder.model.refreshPanelsData();
  
  		return false;
  	},
  
  	/**
  	 * The user clicks delete, so trigger deletion on the row model
  	 */
  	deleteHandler: function () {
  		// Trigger a destroy on the model that will happen with a visual indication to the user
  		this.model.trigger('visual_destroy');
  		this.closeDialog({silent: true});
  
  		return false;
  	},
  
  	/**
  	 * Duplicate this row
  	 */
  	duplicateHandler: function () {
  		this.builder.addHistoryEntry('row_duplicated');
  
  		var duplicateRow = this.model.clone(this.builder.model);
  
  		this.builder.model.get('rows').add( duplicateRow, {
  			at: this.builder.model.get('rows').indexOf(this.model) + 1
  		} );
  
  		this.closeDialog({silent: true});
  
  		return false;
  	},
  
  	closeHandler: function() {
  		this.clearCellStylesCache();
  		if( ! _.isUndefined(this.cellStyles) ) {
  			this.cellStyles = undefined;
  		}
  	},
  
  });
  
  },{}],9:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  var jsWidget = require( '../view/widgets/js-widget' );
  
  module.exports = panels.view.dialog.extend( {
  
  	builder: null,
  	sidebarWidgetTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-dialog-widget-sidebar-widget' ).html() ) ),
  
  	dialogClass: 'so-panels-dialog-edit-widget',
      dialogIcon: 'add-widget',
  
  	widgetView: false,
  	savingWidget: false,
  	editableLabel: true,
  
  	events: {
  		'click .so-close': 'saveHandler',
  		'click .so-nav.so-previous': 'navToPrevious',
  		'click .so-nav.so-next': 'navToNext',
  
  		// Action handlers
  		'click .so-toolbar .so-delete': 'deleteHandler',
  		'click .so-toolbar .so-duplicate': 'duplicateHandler'
  	},
  
  	initializeDialog: function () {
  		var thisView = this;
  		this.model.on( 'change:values', this.handleChangeValues, this );
  		this.model.on( 'destroy', this.remove, this );
  
  		// Refresh panels data after both dialog form components are loaded
  		this.dialogFormsLoaded = 0;
  		this.on( 'form_loaded styles_loaded', function () {
  			this.dialogFormsLoaded ++;
  			if ( this.dialogFormsLoaded === 2 ) {
  				thisView.updateModel( {
  					refreshArgs: {
  						silent: true
  					}
  				} );
  			}
  		} );
  
  		this.on( 'edit_label', function ( text ) {
  			// If text is set to default value, just clear it.
  			if ( text === panelsOptions.widgets[ this.model.get( 'class' ) ][ 'title' ] ) {
  				text = '';
  			}
  			this.model.set( 'label', text );
  			if ( _.isEmpty( text ) ) {
  				this.$( '.so-title' ).text( this.model.getWidgetField( 'title' ) );
  			}
  		}.bind( this ) );
  	},
  
  	/**
  	 * Render the widget dialog.
  	 */
  	render: function () {
  		// Render the dialog and attach it to the builder interface
  		this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-widget' ).html(), {} ) );
  		this.loadForm();
  
  		var title = this.model.getWidgetField( 'title' );
  		this.$( '.so-title .widget-name' ).html( title );
  		this.$( '.so-edit-title' ).val( title );
  
  		if( ! this.builder.supports( 'addWidget' ) ) {
  			this.$( '.so-buttons .so-duplicate' ).remove();
  		}
  		if( ! this.builder.supports( 'deleteWidget' ) ) {
  			this.$( '.so-buttons .so-delete' ).remove();
  		}
  
  		// Now we need to attach the style window
  		this.styles = new panels.view.styles();
  		this.styles.model = this.model;
  		this.styles.render( 'widget', this.builder.config.postId, {
  			builderType: this.builder.config.builderType,
  			dialog: this
  		} );
  
  		var $rightSidebar = this.$( '.so-sidebar.so-right-sidebar' );
  		this.styles.attach( $rightSidebar );
  
  		// Handle the loading class
  		this.styles.on( 'styles_loaded', function ( hasStyles ) {
  			// If we have styles remove the loading spinner, else remove the whole empty sidebar.
  			if ( hasStyles ) {
  				$rightSidebar.removeClass( 'so-panels-loading' );
  			} else {
  				$rightSidebar.closest( '.so-panels-dialog' ).removeClass( 'so-panels-dialog-has-right-sidebar' );
  				$rightSidebar.remove();
  			}
  		}, this );
  		$rightSidebar.addClass( 'so-panels-loading' );
  	},
  
  	/**
  	 * Get the previous widget editing dialog by looking at the dom.
  	 * @returns {*}
  	 */
  	getPrevDialog: function () {
  		var widgets = this.builder.$( '.so-cells .cell .so-widget' );
  		if ( widgets.length <= 1 ) {
  			return false;
  		}
  		var currentIndex = widgets.index( this.widgetView.$el );
  
  		if ( currentIndex === 0 ) {
  			return false;
  		} else {
  			do {
  				widgetView = widgets.eq( --currentIndex ).data( 'view' );
  				if ( ! _.isUndefined( widgetView ) && ! widgetView.model.get( 'read_only' ) ) {
  					return widgetView.getEditDialog();
  				}
  			} while( ! _.isUndefined( widgetView ) && currentIndex > 0 );
  		}
  
  		return false;
  	},
  
  	/**
  	 * Get the next widget editing dialog by looking at the dom.
  	 * @returns {*}
  	 */
  	getNextDialog: function () {
  		var widgets = this.builder.$( '.so-cells .cell .so-widget' );
  		if ( widgets.length <= 1 ) {
  			return false;
  		}
  
  		var currentIndex = widgets.index( this.widgetView.$el ), widgetView;
  
  		if ( currentIndex === widgets.length - 1 ) {
  			return false;
  		} else {
  			do {
  				widgetView = widgets.eq( ++currentIndex ).data( 'view' );
  				if ( ! _.isUndefined( widgetView ) && ! widgetView.model.get( 'read_only' ) ) {
  					return widgetView.getEditDialog();
  				}
  			} while( ! _.isUndefined( widgetView ) );
  		}
  
  		return false;
  	},
  
  	/**
  	 * Load the widget form from the server.
  	 * This is called when rendering the dialog for the first time.
  	 */
  	loadForm: function () {
  		// don't load the form if this dialog hasn't been rendered yet
  		if ( ! this.$( '> *' ).length ) {
  			return;
  		}
  
  		this.$( '.so-content' ).addClass( 'so-panels-loading' );
  
  		var data = {
  			'action': 'so_panels_widget_form',
  			'widget': this.model.get( 'class' ),
  			'instance': JSON.stringify( this.model.get( 'values' ) ),
  			'raw': this.model.get( 'raw' )
  		};
  
  		$.post(
  			panelsOptions.ajaxurl,
  			data,
  			function ( result ) {
  				// Add in the CID of the widget model
  				var html = result.replace( /{\$id}/g, this.model.cid );
  
  				// Load this content into the form
  				var $soContent = this.$( '.so-content' );
  				$soContent
  					.removeClass( 'so-panels-loading' )
  					.html( html );
  
  				// Trigger all the necessary events
  				this.trigger( 'form_loaded', this );
  
  				// For legacy compatibility, trigger a panelsopen event
  				this.$( '.panel-dialog' ).trigger( 'panelsopen' );
  
  				// If the main dialog is closed from this point on, save the widget content
  				this.on( 'close_dialog', this.updateModel, this );
  
  				var widgetContent = $soContent.find( '> .widget-content' );
  				// If there's a widget content wrapper, this is one of the new widgets in WP 4.8 which need some special
  				// handling in JS.
  				if ( widgetContent.length > 0 ) {
  					jsWidget.addWidget( $soContent, this.model.widget_id );
  				}
  
  			}.bind( this ),
  			'html'
  		);
  	},
  
  	/**
  	 * Save the widget from the form to the model
  	 */
  	updateModel: function ( args ) {
  		args = _.extend( {
  			refresh: true,
  			refreshArgs: null
  		}, args );
  
  		// Get the values from the form and assign the new values to the model
  		this.savingWidget = true;
  
  		if ( ! this.model.get( 'missing' ) ) {
  			// Only get the values for non missing widgets.
  			var values = this.getFormValues();
  			if ( _.isUndefined( values.widgets ) ) {
  				values = {};
  			} else {
  				values = values.widgets;
  				values = values[Object.keys( values )[0]];
  			}
  
  			this.model.setValues( values );
  			this.model.set( 'raw', true ); // We've saved from the widget form, so this is now raw
  		}
  
  		if ( this.styles.stylesLoaded ) {
  			// If the styles view has loaded
  			var style = {};
  			try {
  				style = this.getFormValues( '.so-sidebar .so-visual-styles' ).style;
  			}
  			catch ( e ) {
  			}
  			this.model.set( 'style', style );
  		}
  
  		this.savingWidget = false;
  
  		if ( args.refresh ) {
  			this.builder.model.refreshPanelsData( args.refreshArgs );
  		}
  	},
  
  	/**
  	 *
  	 */
  	handleChangeValues: function () {
  		if ( ! this.savingWidget ) {
  			// Reload the form when we've changed the model and we're not currently saving from the form
  			this.loadForm();
  		}
  	},
  
  	/**
  	 * Save a history entry for this widget. Called when the dialog is closed.
  	 */
  	saveHandler: function () {
  		this.builder.addHistoryEntry( 'widget_edited' );
  		this.closeDialog();
  	},
  
  	/**
  	 * When the user clicks delete.
  	 *
  	 * @returns {boolean}
  	 */
  	deleteHandler: function () {
  
  		this.model.trigger( 'visual_destroy' );
  		this.closeDialog( {silent: true} );
  		this.builder.model.refreshPanelsData();
  
  		return false;
  	},
  
  	duplicateHandler: function () {
  		this.model.trigger( 'user_duplicate' );
  
  		this.closeDialog( {silent: true} );
  		this.builder.model.refreshPanelsData();
  
  		return false;
  	}
  
  } );
  
  },{"../view/widgets/js-widget":30}],10:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = panels.view.dialog.extend( {
  
  	builder: null,
  	widgetTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-dialog-widgets-widget' ).html() ) ),
  	filter: {},
  
  	dialogClass: 'so-panels-dialog-add-widget',
  	dialogIcon: 'add-widget',
  
  	events: {
  		'click .so-close': 'closeDialog',
  		'click .widget-type': 'widgetClickHandler',
  		'keyup .so-sidebar-search': 'searchHandler'
  	},
  
  	/**
  	 * Initialize the widget adding dialog
  	 */
  	initializeDialog: function () {
  
  		this.on( 'open_dialog', function () {
  			this.filter.search = '';
  			this.filterWidgets( this.filter );
  		}, this );
  
  		this.on( 'open_dialog_complete', function () {
  			// Clear the search and re-filter the widgets when we open the dialog
  			this.$( '.so-sidebar-search' ).val( '' ).focus();
  			this.balanceWidgetHeights();
  		} );
  
  		// We'll implement a custom tab click handler
  		this.on( 'tab_click', this.tabClickHandler, this );
  	},
  
  	render: function () {
  		// Render the dialog and attach it to the builder interface
  		this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-widgets' ).html(), {} ) );
  
  		// Add all the widgets
  		_.each( panelsOptions.widgets, function ( widget ) {
  			var $w = $( this.widgetTemplate( {
  				title: widget.title,
  				description: widget.description
  			} ) );
  
  			if ( _.isUndefined( widget.icon ) ) {
  				widget.icon = 'dashicons dashicons-admin-generic';
  			}
  
  			$( '<span class="widget-icon" />' ).addClass( widget.icon ).prependTo( $w.find( '.widget-type-wrapper' ) );
  
  			$w.data( 'class', widget.class ).appendTo( this.$( '.widget-type-list' ) );
  		}, this );
  
  		// Add the sidebar tabs
  		var tabs = this.$( '.so-sidebar-tabs' );
  		_.each( panelsOptions.widget_dialog_tabs, function ( tab ) {
  			$( this.dialogTabTemplate( {'title': tab.title} ) ).data( {
  				'message': tab.message,
  				'filter': tab.filter
  			} ).appendTo( tabs );
  		}, this );
  
  		// We'll be using tabs, so initialize them
  		this.initTabs();
  
  		var thisDialog = this;
  		$( window ).resize( function () {
  			thisDialog.balanceWidgetHeights();
  		} );
  	},
  
  	/**
  	 * Handle a tab being clicked
  	 */
  	tabClickHandler: function ( $t ) {
  		// Get the filter from the tab, and filter the widgets
  		this.filter = $t.parent().data( 'filter' );
  		this.filter.search = this.$( '.so-sidebar-search' ).val();
  
  		var message = $t.parent().data( 'message' );
  		if ( _.isEmpty( message ) ) {
  			message = '';
  		}
  
  		this.$( '.so-toolbar .so-status' ).html( message );
  
  		this.filterWidgets( this.filter );
  
  		return false;
  	},
  
  	/**
  	 * Handle changes to the search value
  	 */
  	searchHandler: function ( e ) {
  		if( e.which === 13 ) {
  			var visibleWidgets = this.$( '.widget-type-list .widget-type:visible' );
  			if( visibleWidgets.length === 1 ) {
  				visibleWidgets.click();
  			}
  		}
  		else {
  			this.filter.search = $( e.target ).val().trim();
  			this.filterWidgets( this.filter );
  		}
  	},
  
  	/**
  	 * Filter the widgets that we're displaying
  	 * @param filter
  	 */
  	filterWidgets: function ( filter ) {
  		if ( _.isUndefined( filter ) ) {
  			filter = {};
  		}
  
  		if ( _.isUndefined( filter.groups ) ) {
  			filter.groups = '';
  		}
  
  		this.$( '.widget-type-list .widget-type' ).each( function () {
  			var $$ = $( this ), showWidget;
  			var widgetClass = $$.data( 'class' );
  
  			var widgetData = (
  				! _.isUndefined( panelsOptions.widgets[widgetClass] )
  			) ? panelsOptions.widgets[widgetClass] : null;
  
  			if ( _.isEmpty( filter.groups ) ) {
  				// This filter doesn't specify groups, so show all
  				showWidget = true;
  			} else if ( widgetData !== null && ! _.isEmpty( _.intersection( filter.groups, panelsOptions.widgets[widgetClass].groups ) ) ) {
  				// This widget is in the filter group
  				showWidget = true;
  			} else {
  				// This widget is not in the filter group
  				showWidget = false;
  			}
  
  			// This can probably be done with a more intelligent operator
  			if ( showWidget ) {
  
  				if ( ! _.isUndefined( filter.search ) && filter.search !== '' ) {
  					// Check if the widget title contains the search term
  					if ( widgetData.title.toLowerCase().indexOf( filter.search.toLowerCase() ) === - 1 ) {
  						showWidget = false;
  					}
  				}
  
  			}
  
  			if ( showWidget ) {
  				$$.show();
  			} else {
  				$$.hide();
  			}
  		} );
  
  		// Balance the tags after filtering
  		this.balanceWidgetHeights();
  	},
  
  	/**
  	 * Add the widget to the current builder
  	 *
  	 * @param e
  	 */
  	widgetClickHandler: function ( e ) {
  		// Add the history entry
  		this.builder.addHistoryEntry( 'widget_added' );
  
  		var $w = $( e.currentTarget );
  
  		var widget = new panels.model.widget( {
  			class: $w.data( 'class' )
  		} );
  
  		// Add the widget to the cell model
  		widget.cell = this.builder.getActiveCell();
  		widget.cell.get('widgets').add( widget );
  
  		this.closeDialog();
  		this.builder.model.refreshPanelsData();
  	},
  
  	/**
  	 * Balance widgets in a given row so they have enqual height.
  	 * @param e
  	 */
  	balanceWidgetHeights: function ( e ) {
  		var widgetRows = [[]];
  		var previousWidget = null;
  
  		// Work out how many widgets there are per row
  		var perRow = Math.round( this.$( '.widget-type' ).parent().width() / this.$( '.widget-type' ).width() );
  
  		// Add clears to create balanced rows
  		this.$( '.widget-type' )
  			.css( 'clear', 'none' )
  			.filter( ':visible' )
  			.each( function ( i, el ) {
  				if ( i % perRow === 0 && i !== 0 ) {
  					$( el ).css( 'clear', 'both' );
  				}
  			} );
  
  		// Group the widgets into rows
  		this.$( '.widget-type-wrapper' )
  			.css( 'height', 'auto' )
  			.filter( ':visible' )
  			.each( function ( i, el ) {
  				var $el = $( el );
  				if ( previousWidget !== null && previousWidget.position().top !== $el.position().top ) {
  					widgetRows[widgetRows.length] = [];
  				}
  				previousWidget = $el;
  				widgetRows[widgetRows.length - 1].push( $el );
  			} );
  
  		// Balance the height of the widgets within the row.
  		_.each( widgetRows, function ( row, i ) {
  			var maxHeight = _.max( row.map( function ( el ) {
  				return el.height();
  			} ) );
  			// Set the height of each widget in the row
  			_.each( row, function ( el ) {
  				el.height( maxHeight );
  			} );
  
  		} );
  	}
  } );
  
  },{}],11:[function(require,module,exports){
  module.exports = {
  	/**
  	 * Check if we have copy paste available.
  	 * @returns {boolean|*}
  	 */
  	canCopyPaste: function(){
  		return typeof(Storage) !== "undefined" && panelsOptions.user;
  	},
  
  	/**
  	 * Set the model that we're going to store in the clipboard
  	 */
  	setModel: function( model ){
  		if( ! this.canCopyPaste() ) {
  			return false;
  		}
  
  		var serial = panels.helpers.serialize.serialize( model );
  		if( model instanceof  panels.model.row ) {
  			serial.thingType = 'row-model';
  		} else if( model instanceof  panels.model.widget ) {
  			serial.thingType = 'widget-model';
  		}
  
  		// Store this in local storage
  		localStorage[ 'panels_clipboard_' + panelsOptions.user ] = JSON.stringify( serial );
  		return true;
  	},
  
  	/**
  	 * Check if the current model stored in the clipboard is the expected type
  	 */
  	isModel: function( expected ){
  		if( ! this.canCopyPaste() ) {
  			return false;
  		}
  
  		var clipboardObject = localStorage[ 'panels_clipboard_' + panelsOptions.user ];
  		if( clipboardObject !== undefined ) {
  			clipboardObject = JSON.parse(clipboardObject);
  			return clipboardObject.thingType && clipboardObject.thingType === expected;
  		}
  
  		return false;
  	},
  
  	/**
  	 * Get the model currently stored in the clipboard
  	 */
  	getModel: function( expected ){
  		if( ! this.canCopyPaste() ) {
  			return null;
  		}
  
  		var clipboardObject = localStorage[ 'panels_clipboard_' + panelsOptions.user ];
  		if( clipboardObject !== undefined ) {
  			clipboardObject = JSON.parse( clipboardObject );
  			if( clipboardObject.thingType && clipboardObject.thingType === expected ) {
  				return panels.helpers.serialize.unserialize( clipboardObject, clipboardObject.thingType, null );
  			}
  		}
  
  		return null;
  	},
  };
  
  },{}],12:[function(require,module,exports){
  module.exports = {
  	/**
  	 * Lock window scrolling for the main overlay
  	 */
  	lock: function () {
  		if ( jQuery( 'body' ).css( 'overflow' ) === 'hidden' ) {
  			return;
  		}
  
  		// lock scroll position, but retain settings for later
  		var scrollPosition = [
  			self.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
  			self.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
  		];
  
  		jQuery( 'body' )
  			.data( {
  				'scroll-position': scrollPosition
  			} )
  			.css( 'overflow', 'hidden' );
  
  		if( ! _.isUndefined( scrollPosition ) ) {
  			window.scrollTo( scrollPosition[0], scrollPosition[1] );
  		}
  	},
  
  	/**
  	 * Unlock window scrolling
  	 */
  	unlock: function () {
  		if ( jQuery( 'body' ).css( 'overflow' ) !== 'hidden' ) {
  			return;
  		}
  
  		// Check that there are no more dialogs or a live editor
  		if ( ! jQuery( '.so-panels-dialog-wrapper' ).is( ':visible' ) && ! jQuery( '.so-panels-live-editor' ).is( ':visible' ) ) {
  			jQuery( 'body' ).css( 'overflow', 'visible' );
  			var scrollPosition = jQuery( 'body' ).data( 'scroll-position' );
  
  			if( ! _.isUndefined( scrollPosition ) ) {
  				window.scrollTo( scrollPosition[0], scrollPosition[1] );
  			}
  		}
  	},
  };
  
  },{}],13:[function(require,module,exports){
  /*
  This is a modified version of https://github.com/underdogio/backbone-serialize/
  */
  
  /* global Backbone, module, panels */
  
  module.exports = {
  	serialize: function( thing ){
  		var val;
  
  		if( thing instanceof Backbone.Model ) {
  			var retObj = {};
  			for ( var key in thing.attributes ) {
  				if (thing.attributes.hasOwnProperty( key ) ) {
  					// Skip these to avoid recursion
  					if( key === 'builder' || key === 'collection' ) { continue; }
  
  					// If the value is a Model or a Collection, then serialize them as well
  					val = thing.attributes[key];
  					if ( val instanceof Backbone.Model || val instanceof Backbone.Collection ) {
  						retObj[key] = this.serialize( val );
  					} else {
  						// Otherwise, save the original value
  						retObj[key] = val;
  					}
  				}
  			}
  			return retObj;
  		}
  		else if( thing instanceof Backbone.Collection ) {
  			// Walk over all of our models
  			var retArr = [];
  
  			for ( var i = 0; i < thing.models.length; i++ ) {
  				// If the model is serializable, then serialize it
  				val = thing.models[i];
  
  				if ( val instanceof Backbone.Model || val instanceof Backbone.Collection ) {
  					retArr.push( this.serialize( val ) );
  				} else {
  					// Otherwise (it is an object), return it in its current form
  					retArr.push( val );
  				}
  			}
  
  			// Return the serialized models
  			return retArr;
  		}
  	},
  
  	unserialize: function( thing, thingType, parent ) {
  		var retObj;
  
  		switch( thingType ) {
  			case 'row-model' :
  				retObj = new panels.model.row();
  				retObj.builder = parent;
  				retObj.set( 'style', thing.style );
  				retObj.setCells( this.unserialize( thing.cells, 'cell-collection', retObj ) );
  				break;
  
  			case 'cell-model' :
  				retObj = new panels.model.cell();
  				retObj.row = parent;
  				retObj.set( 'weight', thing.weight );
  				retObj.set( 'style', thing.style );
  				retObj.set( 'widgets', this.unserialize( thing.widgets, 'widget-collection', retObj ) );
  				break;
  
  			case 'widget-model' :
  				retObj = new panels.model.widget();
  				retObj.cell = parent;
  				for ( var key in thing ) {
  					if ( thing.hasOwnProperty( key ) ) {
  						retObj.set( key, thing[key] );
  					}
  				}
  				retObj.set( 'widget_id', panels.helpers.utils.generateUUID() );
  				break;
  
  			case 'cell-collection':
  				retObj = new panels.collection.cells();
  				for( var i = 0; i < thing.length; i++ ) {
  					retObj.push( this.unserialize( thing[i], 'cell-model', parent ) );
  				}
  				break;
  
  			case 'widget-collection':
  				retObj = new panels.collection.widgets();
  				for( var i = 0; i < thing.length; i++ ) {
  					retObj.push( this.unserialize( thing[i], 'widget-model', parent ) );
  				}
  				break;
  
  			default:
  				console.log( 'Unknown Thing - ' + thingType );
  				break;
  		}
  
  		return retObj;
  	}
  };
  
  },{}],14:[function(require,module,exports){
  module.exports = {
  
  	generateUUID: function(){
  		var d = new Date().getTime();
  		if( window.performance && typeof window.performance.now === "function" ){
  			d += performance.now(); //use high-precision timer if available
  		}
  		var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( /[xy]/g, function(c) {
  			var r = (d + Math.random()*16)%16 | 0;
  			d = Math.floor(d/16);
  			return ( c == 'x' ? r : (r&0x3|0x8) ).toString(16);
  		} );
  		return uuid;
  	},
  
  	processTemplate: function ( s ) {
  		s = s.replace( /{{%/g, '<%' );
  		s = s.replace( /%}}/g, '%>' );
  		s = s.trim();
  		return s;
  	},
  
  	// From this SO post: http://stackoverflow.com/questions/6139107/programmatically-select-text-in-a-contenteditable-html-element
  	selectElementContents: function( element ) {
  		var range = document.createRange();
  		range.selectNodeContents( element );
  		var sel = window.getSelection();
  		sel.removeAllRanges();
  		sel.addRange( range );
  	},
  
  }
  
  },{}],15:[function(require,module,exports){
  /* global _, jQuery, panels */
  
  var panels = window.panels, $ = jQuery;
  
  module.exports = function ( config ) {
  
  	return this.each( function () {
  		var $$ = jQuery( this );
  		var widgetId = $$.closest( 'form' ).find( '.widget-id' ).val();
  
  		// Create a config for this specific widget
  		var thisConfig = $.extend(true, {}, config);
  
  		// Exit if this isn't a real widget
  		if ( ! _.isUndefined( widgetId ) && widgetId.indexOf( '__i__' ) > - 1 ) {
  			return;
  		}
  
  		// Create the main builder model
  		var builderModel = new panels.model.builder();
  
  		// Now for the view to display the builder
  		var builderView = new panels.view.builder( {
  			model: builderModel,
  			config: thisConfig
  		} );
  
  		// Save panels data when we close the dialog, if we're in a dialog
  		var dialog = $$.closest( '.so-panels-dialog-wrapper' ).data( 'view' );
  		if ( ! _.isUndefined( dialog ) ) {
  			dialog.on( 'close_dialog', function () {
  				builderModel.refreshPanelsData();
  			} );
  
  			dialog.on( 'open_dialog_complete', function () {
  				// Make sure the new layout widget is always properly setup
  				builderView.trigger( 'builder_resize' );
  			} );
  
  			dialog.model.on( 'destroy', function () {
  				// Destroy the builder
  				builderModel.emptyRows().destroy();
  			} );
  
  			// Set the parent for all the sub dialogs
  			builderView.setDialogParents( panelsOptions.loc.layout_widget, dialog );
  		}
  
  		// Basic setup for the builder
  		var isWidget = Boolean( $$.closest( '.widget-content' ).length );
  		builderView
  			.render()
  			.attach( {
  				container: $$,
  				dialog: isWidget || $$.data('mode') === 'dialog',
  				type: $$.data( 'type' )
  			} )
  			.setDataField( $$.find( 'input.panels-data' ) );
  
  		if ( isWidget || $$.data('mode') === 'dialog' ) {
  			// Set up the dialog opening
  			builderView.setDialogParents( panelsOptions.loc.layout_widget, builderView.dialog );
  			$$.find( '.siteorigin-panels-display-builder' ).click( function ( e ) {
  				e.preventDefault();
  				builderView.dialog.openDialog();
  			} );
  		} else {
  			// Remove the dialog opener button, this is already being displayed in a page builder dialog.
  			$$.find( '.siteorigin-panels-display-builder' ).parent().remove();
  		}
  
  		// Trigger a global jQuery event after we've setup the builder view
  		$( document ).trigger( 'panels_setup', builderView );
  	} );
  };
  
  },{}],16:[function(require,module,exports){
  /**
   * Everything we need for SiteOrigin Page Builder.
   *
   * @copyright Greg Priday 2013 - 2016 - <https://siteorigin.com/>
   * @license GPL 3.0 http://www.gnu.org/licenses/gpl.html
   */
  
  /* global Backbone, _, jQuery, tinyMCE, panelsOptions, plupload, confirm, console, require */
  
  var panels = {};
  
  // Store everything globally
  window.panels = panels;
  window.siteoriginPanels = panels;
  
  // Helpers
  panels.helpers = {};
  panels.helpers.clipboard = require( './helpers/clipboard' );
  panels.helpers.utils = require( './helpers/utils' );
  panels.helpers.serialize = require( './helpers/serialize' );
  panels.helpers.pageScroll = require( './helpers/page-scroll' );
  
  // The models
  panels.model = {};
  panels.model.widget = require( './model/widget' );
  panels.model.cell = require( './model/cell' );
  panels.model.row = require( './model/row' );
  panels.model.builder = require( './model/builder' );
  panels.model.historyEntry = require( './model/history-entry' );
  
  // The collections
  panels.collection = {};
  panels.collection.widgets = require( './collection/widgets' );
  panels.collection.cells = require( './collection/cells' );
  panels.collection.rows = require( './collection/rows' );
  panels.collection.historyEntries = require( './collection/history-entries' );
  
  // The views
  panels.view = {};
  panels.view.widget = require( './view/widget' );
  panels.view.cell = require( './view/cell' );
  panels.view.row = require( './view/row' );
  panels.view.builder = require( './view/builder' );
  panels.view.dialog = require( './view/dialog' );
  panels.view.styles = require( './view/styles' );
  panels.view.liveEditor = require( './view/live-editor' );
  
  // The dialogs
  panels.dialog = {};
  panels.dialog.builder = require( './dialog/builder' );
  panels.dialog.widgets = require( './dialog/widgets' );
  panels.dialog.widget = require( './dialog/widget' );
  panels.dialog.prebuilt = require( './dialog/prebuilt' );
  panels.dialog.row = require( './dialog/row' );
  panels.dialog.history = require( './dialog/history' );
  
  // The utils
  panels.utils = {};
  panels.utils.menu = require( './utils/menu' );
  
  // jQuery Plugins
  jQuery.fn.soPanelsSetupBuilderWidget = require( './jquery/setup-builder-widget' );
  
  
  // Set up Page Builder if we're on the main interface
  jQuery( function ( $ ) {
  
  	var container,
  		field,
  		form,
  		builderConfig;
  
  	if ( $( '#siteorigin-panels-metabox' ).length && $( 'form#post' ).length ) {
  		// This is usually the case when we're in the post edit interface
  		container = $( '#siteorigin-panels-metabox' );
  		field = $( '#siteorigin-panels-metabox .siteorigin-panels-data-field' );
  		form = $( 'form#post' );
  
  		builderConfig = {
  			editorType: 'tinyMCE',
  			postId: $( '#post_ID' ).val(),
  			editorId: '#content',
  			builderType: $( '#siteorigin-panels-metabox' ).data( 'builder-type' ),
  			builderSupports: $( '#siteorigin-panels-metabox' ).data( 'builder-supports' ),
  			loadOnAttach: panelsOptions.loadOnAttach && $( '#auto_draft' ).val() == 1,
  			loadLiveEditor: $( '#siteorigin-panels-metabox' ).data('live-editor') == 1,
  			liveEditorPreview: container.data('preview-url')
  		};
  	}
  	else if ( $( '.siteorigin-panels-builder-form' ).length ) {
  		// We're dealing with another interface like the custom home page interface
  		var $$ = $( '.siteorigin-panels-builder-form' );
  
  		container = $$.find( '.siteorigin-panels-builder-container' );
  		field = $$.find( 'input[name="panels_data"]' );
  		form = $$;
  
  		builderConfig = {
  			editorType: 'standalone',
  			postId: $$.data( 'post-id' ),
  			editorId: '#post_content',
  			builderType: $$.data( 'type' ),
  			builderSupports: $$.data( 'builder-supports' ),
  			loadLiveEditor: false,
  			liveEditorPreview: $$.data( 'preview-url' )
  		};
  	}
  
  	if ( ! _.isUndefined( container ) ) {
  		// If we have a container, then set up the main builder
  		var panels = window.siteoriginPanels;
  
  		// Create the main builder model
  		var builderModel = new panels.model.builder();
  
  		// Now for the view to display the builder
  		var builderView = new panels.view.builder( {
  			model: builderModel,
  			config: builderConfig
  		} );
  
  		// Set up the builder view
  		builderView
  			.render()
  			.attach( {
  				container: container
  			} )
  			.setDataField( field )
  			.attachToEditor();
  
  		// When the form is submitted, update the panels data
  		form.submit( function () {
  			// Refresh the data
  			builderModel.refreshPanelsData();
  		} );
  
  		container.removeClass( 'so-panels-loading' );
  
  		// Trigger a global jQuery event after we've setup the builder view. Everything is accessible form there
  		$( document ).trigger( 'panels_setup', builderView, window.panels );
  	}
  
  	// Setup new widgets when they're added in the standard widget interface
  	$( document ).on( 'widget-added', function ( e, widget ) {
  		$( widget ).find( '.siteorigin-page-builder-widget' ).soPanelsSetupBuilderWidget();
  	} );
  
  	// Setup existing widgets on the page (for the widgets interface)
  	if ( ! $( 'body' ).hasClass( 'wp-customizer' ) ) {
  		$( function () {
  			$( '.siteorigin-page-builder-widget' ).soPanelsSetupBuilderWidget();
  		} );
  	}
  } );
  
  },{"./collection/cells":1,"./collection/history-entries":2,"./collection/rows":3,"./collection/widgets":4,"./dialog/builder":5,"./dialog/history":6,"./dialog/prebuilt":7,"./dialog/row":8,"./dialog/widget":9,"./dialog/widgets":10,"./helpers/clipboard":11,"./helpers/page-scroll":12,"./helpers/serialize":13,"./helpers/utils":14,"./jquery/setup-builder-widget":15,"./model/builder":17,"./model/cell":18,"./model/history-entry":19,"./model/row":20,"./model/widget":21,"./utils/menu":22,"./view/builder":23,"./view/cell":24,"./view/dialog":25,"./view/live-editor":26,"./view/row":27,"./view/styles":28,"./view/widget":29}],17:[function(require,module,exports){
  module.exports = Backbone.Model.extend({
  	layoutPosition: {
  		BEFORE: 'before',
  		AFTER: 'after',
  		REPLACE: 'replace',
  	},
  
  	rows: {},
  
  	defaults: {
  		'data': {
  			'widgets': [],
  			'grids': [],
  			'grid_cells': []
  		}
  	},
  
  	initialize: function () {
  		// These are the main rows in the interface
  		this.set( 'rows', new panels.collection.rows() );
  	},
  
  	/**
  	 * Add a new row to this builder.
  	 *
  	 * @param attrs
  	 * @param cells
  	 * @param options
  	 */
  	addRow: function (attrs, cells, options) {
  		options = _.extend({
  			noAnimate: false
  		}, options);
  
  		var cellCollection = new panels.collection.cells(cells);
  
  		attrs = _.extend({
  			collection: this.get('rows'),
  			cells: cellCollection,
  		}, attrs);
  
  		// Create the actual row
  		var row = new panels.model.row(attrs);
  		row.builder = this;
  
  		this.get('rows').add( row, options );
  
  		return row;
  	},
  
  	/**
  	 * Load the panels data into the builder
  	 *
  	 * @param data Object the layout and widgets data to load.
  	 * @param position string Where to place the new layout. Allowed options are 'before', 'after'. Anything else will
  	 *						  cause the new layout to replace the old one.
  	 */
  	loadPanelsData: function ( data, position ) {
  		try {
  			if ( position === this.layoutPosition.BEFORE ) {
  				data = this.concatPanelsData( data, this.getPanelsData() );
  			} else if ( position === this.layoutPosition.AFTER ) {
  				data = this.concatPanelsData( this.getPanelsData(), data );
  			}
  
  			// Start by destroying any rows that currently exist. This will in turn destroy cells, widgets and all the associated views
  			this.emptyRows();
  
  			// This will empty out the current rows and reload the builder data.
  			this.set( 'data', JSON.parse( JSON.stringify( data ) ), {silent: true} );
  
  			var cit = 0;
  			var rows = [];
  
  			if ( _.isUndefined( data.grid_cells ) ) {
  				this.trigger( 'load_panels_data' );
  				return;
  			}
  
  			var gi;
  			for ( var ci = 0; ci < data.grid_cells.length; ci ++ ) {
  				gi = parseInt( data.grid_cells[ci].grid );
  				if ( _.isUndefined( rows[gi] ) ) {
  					rows[gi] = [];
  				}
  
  				rows[gi].push( data.grid_cells[ci] );
  			}
  
  			var builderModel = this;
  			_.each( rows, function ( row, i ) {
  				var rowAttrs = {};
  
  				if ( ! _.isUndefined( data.grids[i].style ) ) {
  					rowAttrs.style = data.grids[i].style;
  				}
  
  				if ( ! _.isUndefined( data.grids[i].color_label) ) {
  					rowAttrs.color_label = data.grids[i].color_label;
  				}
  
  				if ( ! _.isUndefined( data.grids[i].label) ) {
  					rowAttrs.label = data.grids[i].label;
  				}
  				// This will create and add the row model and its cells
  				builderModel.addRow(rowAttrs, row, {noAnimate: true} );
  			} );
  
  
  			if ( _.isUndefined( data.widgets ) ) {
  				return;
  			}
  
  			// Add the widgets
  			_.each( data.widgets, function ( widgetData ) {
  				var panels_info = null;
  				if ( ! _.isUndefined( widgetData.panels_info ) ) {
  					panels_info = widgetData.panels_info;
  					delete widgetData.panels_info;
  				} else {
  					panels_info = widgetData.info;
  					delete widgetData.info;
  				}
  
  				var row = builderModel.get('rows').at( parseInt( panels_info.grid ) );
  				var cell = row.get('cells').at( parseInt( panels_info.cell ) );
  
  				var newWidget = new panels.model.widget( {
  					class: panels_info.class,
  					values: widgetData
  				} );
  
  				if ( ! _.isUndefined( panels_info.style ) ) {
  					newWidget.set( 'style', panels_info.style );
  				}
  
  				if ( ! _.isUndefined( panels_info.read_only ) ) {
  					newWidget.set( 'read_only', panels_info.read_only );
  				}
  				if ( ! _.isUndefined( panels_info.widget_id ) ) {
  					newWidget.set( 'widget_id', panels_info.widget_id );
  				}
  				else {
  					newWidget.set( 'widget_id', panels.helpers.utils.generateUUID() );
  				}
  
  				if ( ! _.isUndefined( panels_info.label ) ) {
  					newWidget.set( 'label', panels_info.label );
  				}
  
  				newWidget.cell = cell;
  				cell.get('widgets').add( newWidget, { noAnimate: true } );
  			} );
  
  			this.trigger( 'load_panels_data' );
  		}
  		catch ( err ) {
  			console.log( 'Error loading data: ' + err.message );
  
  		}
  	},
  
  	/**
  	 * Concatenate the second set of Page Builder data to the first. There is some validation of input, but for the most
  	 * part it's up to the caller to ensure the Page Builder data is well formed.
  	 */
  	concatPanelsData: function ( panelsDataA, panelsDataB ) {
  
  		if ( _.isUndefined( panelsDataB ) || _.isUndefined( panelsDataB.grids ) || _.isEmpty( panelsDataB.grids ) ||
  			 _.isUndefined( panelsDataB.grid_cells ) || _.isEmpty( panelsDataB.grid_cells ) ) {
  			return panelsDataA;
  		}
  
  		if ( _.isUndefined( panelsDataA ) || _.isUndefined( panelsDataA.grids ) || _.isEmpty( panelsDataA.grids ) ) {
  			return panelsDataB;
  		}
  
  		var gridsBOffset = panelsDataA.grids.length;
  		var widgetsBOffset = ! _.isUndefined( panelsDataA.widgets ) ? panelsDataA.widgets.length : 0;
  		var newPanelsData = {grids: [], 'grid_cells': [], 'widgets': []};
  
  		// Concatenate grids (rows)
  		newPanelsData.grids = panelsDataA.grids.concat( panelsDataB.grids );
  
  		// Create a copy of panelsDataA grid_cells and widgets
  		if ( ! _.isUndefined( panelsDataA.grid_cells ) ) {
  			newPanelsData.grid_cells = panelsDataA.grid_cells.slice();
  		}
  		if ( ! _.isUndefined( panelsDataA.widgets ) ) {
  			newPanelsData.widgets = panelsDataA.widgets.slice();
  		}
  
  		var i;
  		// Concatenate grid cells (row columns)
  		for ( i = 0; i < panelsDataB.grid_cells.length; i ++ ) {
  			var gridCellB = panelsDataB.grid_cells[i];
  			gridCellB.grid = parseInt( gridCellB.grid ) + gridsBOffset;
  			newPanelsData.grid_cells.push( gridCellB );
  		}
  
  		// Concatenate widgets
  		if ( ! _.isUndefined( panelsDataB.widgets ) ) {
  			for ( i = 0; i < panelsDataB.widgets.length; i ++ ) {
  				var widgetB = panelsDataB.widgets[i];
  				widgetB.panels_info.grid = parseInt( widgetB.panels_info.grid ) + gridsBOffset;
  				widgetB.panels_info.id = parseInt( widgetB.panels_info.id ) + widgetsBOffset;
  				newPanelsData.widgets.push( widgetB );
  			}
  		}
  
  		return newPanelsData;
  	},
  
  	/**
  	 * Convert the content of the builder into a object that represents the page builder data
  	 */
  	getPanelsData: function () {
  
  		var builder = this;
  
  		var data = {
  			'widgets': [],
  			'grids': [],
  			'grid_cells': []
  		};
  		var widgetId = 0;
  
  		this.get('rows').each( function ( row, ri ) {
  
  			row.get('cells').each( function ( cell, ci ) {
  
  				cell.get('widgets').each( function ( widget, wi ) {
  					// Add the data for the widget, including the panels_info field.
  					var panels_info = {
  						class: widget.get( 'class' ),
  						raw: widget.get( 'raw' ),
  						grid: ri,
  						cell: ci,
  						// Strictly this should be an index
  						id: widgetId ++,
  						widget_id: widget.get( 'widget_id' ),
  						style: widget.get( 'style' ),
  						label: widget.get( 'label' ),
  					};
  
  					if( _.isEmpty( panels_info.widget_id ) ) {
  						panels_info.widget_id = panels.helpers.utils.generateUUID();
  					}
  
  					var values = _.extend( _.clone( widget.get( 'values' ) ), {
  						panels_info: panels_info
  					} );
  					data.widgets.push( values );
  				} );
  
  				// Add the cell info
  				data.grid_cells.push( {
  					grid: ri,
  					index: ci,
  					weight: cell.get( 'weight' ),
  					style: cell.get( 'style' ),
  				} );
  
  			} );
  
  			data.grids.push( {
  				cells: row.get('cells').length,
  				style: row.get( 'style' ),
  				color_label: row.get( 'color_label' ),
  				label: row.get( 'label' ),
  			} );
  
  		} );
  
  		return data;
  
  	},
  
  	/**
  	 * This will check all the current entries and refresh the panels data
  	 */
  	refreshPanelsData: function ( args ) {
  		args = _.extend( {
  			silent: false
  		}, args );
  
  		var oldData = this.get( 'data' );
  		var newData = this.getPanelsData();
  		this.set( 'data', newData, {silent: true} );
  
  		if ( ! args.silent && JSON.stringify( newData ) !== JSON.stringify( oldData ) ) {
  			// The default change event doesn't trigger on deep changes, so we'll trigger our own
  			this.trigger( 'change' );
  			this.trigger( 'change:data' );
  			this.trigger( 'refresh_panels_data', newData, args );
  		}
  	},
  
  	/**
  	 * Empty all the rows and the cells/widgets they contain.
  	 */
  	emptyRows: function () {
  		_.invoke( this.get('rows').toArray(), 'destroy' );
  		this.get('rows').reset();
  
  		return this;
  	},
  
  	isValidLayoutPosition: function ( position ) {
  		return position === this.layoutPosition.BEFORE ||
  			   position === this.layoutPosition.AFTER ||
  			   position === this.layoutPosition.REPLACE;
  	},
  
  	/**
  	 * Convert HTML into Panels Data
  	 * @param html
  	 */
  	getPanelsDataFromHtml: function( html, editorClass ){
  		var thisModel = this;
  		var $html = jQuery( '<div id="wrapper">' + html + '</div>' );
  
  		if( $html.find('.panel-layout .panel-grid').length ) {
  			// This looks like Page Builder html, lets try parse it
  			var panels_data = {
  				grids: [],
  				grid_cells: [],
  				widgets: [],
  			};
  
  			// The Regex object that'll match SiteOrigin widgets
  			var re = new RegExp( panelsOptions.siteoriginWidgetRegex , "i" );
  			var decodeEntities = (function() {
  				// this prevents any overhead from creating the object each time
  				var element = document.createElement('div');
  
  				function decodeHTMLEntities (str) {
  					if(str && typeof str === 'string') {
  						// strip script/html tags
  						str = str.replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '');
  						str = str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, '');
  						element.innerHTML = str;
  						str = element.textContent;
  						element.textContent = '';
  					}
  
  					return str;
  				}
  
  				return decodeHTMLEntities;
  			})();
  
  			// Remove all wrapping divs from a widget to get its html
  			var getTextWidgetContents = function( $el ){
  				var $divs = $el.find( 'div' );
  				if( ! $divs.length ) {
  					return $el.html();
  				}
  
  				var i;
  				for( i = 0; i < $divs.length - 1; i++ ) {
  					if( jQuery.trim( $divs.eq(i).text() ) != jQuery.trim( $divs.eq(i+1).text() ) ) {
  						break;
  					}
  				}
  
  				var title = $divs.eq( i ).find( '.widget-title:header' ),
  					titleText = '';
  
  				if( title.length ) {
  					titleText = title.html();
  					title.remove();
  				}
  
  				return {
  					title: titleText,
  					text: $divs.eq(i).html(),
  				};
  			};
  
  			var $layout = $html.find( '.panel-layout' ).eq(0);
  			var filterNestedLayout = function( i, el ){
  				return jQuery( el ).closest( '.panel-layout' ).is( $layout );
  			};
  
  			$html.find('> .panel-layout > .panel-grid').filter( filterNestedLayout ).each( function( ri, el ){
  				var $row = jQuery( el ),
  					$cells = $row.find( '.panel-grid-cell' ).filter( filterNestedLayout );
  
  				panels_data.grids.push( {
  					cells: $cells.length,
  					style: $row.data( 'style' ),
  					color_label: $row.data( 'color-label' ),
  					label: $row.data( 'label' ),
  				} );
  
  				$cells.each( function( ci, el ){
  					var $cell = jQuery( el ),
  						$widgets = $cell.find( '.so-panel' ).filter( filterNestedLayout );
  
  					panels_data.grid_cells.push( {
  						grid: ri,
  						weight: ! _.isUndefined( $cell.data( 'weight' ) ) ? parseFloat( $cell.data( 'weight' ) ) : 1,
  						style: $cell.data( 'style' ),
  					} );
  
  					$widgets.each( function( wi, el ){
  						var $widget = jQuery(el),
  							widgetContent = $widget.find('.panel-widget-style').length ? $widget.find('.panel-widget-style').html() : $widget.html(),
  							panels_info = {
  								grid: ri,
  								cell: ci,
  								raw: false,
  								label: $widget.data( 'label' )
  							};
  
  						widgetContent = widgetContent.trim();
  
  						// Check if this is a SiteOrigin Widget
  						var match = re.exec( widgetContent );
  						if( ! _.isNull( match ) && widgetContent.replace( re, '' ).trim() === '' ) {
  							try {
  								var classMatch = /class="(.*?)"/.exec( match[3] ),
  									dataInput = jQuery( match[5] ),
  									data = JSON.parse( decodeEntities( dataInput.val( ) ) ),
  									newWidget = data.instance;
  
  								panels_info.class = classMatch[1].replace( /\\\\+/g, '\\' );
  								panels_info.raw = false;
  
  								newWidget.panels_info = panels_info;
  								panels_data.widgets.push( newWidget );
  							}
  							catch ( err ) {
  								// There was a problem, so treat this as a standard editor widget
  								panels_info.class = editorClass;
  								panels_data.widgets.push( _.extend( getTextWidgetContents( $widget ), {
  									filter: "1",
  									type: "visual",
  									panels_info: panels_info
  								} ) );
  							}
  
  							// Continue
  							return true;
  						}
  						else if( widgetContent.indexOf( 'panel-layout' ) !== -1 ) {
  							// Check if this is a layout widget
  							var $widgetContent = jQuery( '<div>' + widgetContent + '</div>' );
  							if( $widgetContent.find('.panel-layout .panel-grid').length ) {
  								// This is a standard editor class widget
  								panels_info.class = 'SiteOrigin_Panels_Widgets_Layout';
  								panels_data.widgets.push( {
  									panels_data: thisModel.getPanelsDataFromHtml( widgetContent, editorClass ),
  									panels_info: panels_info
  								} );
  
  								// continue
  								return true;
  							}
  						}
  
  						// This is a standard editor class widget
  						panels_info.class = editorClass;
  						panels_data.widgets.push( _.extend( getTextWidgetContents( $widget ), {
  							filter: "1",
  							type: "visual",
  							panels_info: panels_info
  						} ) );
  						return true;
  					} );
  				} );
  			} );
  
  			// Remove all the Page Builder content
  			$html.find('.panel-layout').remove();
  			$html.find('style[data-panels-style-for-post]').remove();
  
  			// If there's anything left, add it to an editor widget at the end of panels_data
  			if( $html.html().replace(/^\s+|\s+$/gm,'').length ) {
  				panels_data.grids.push( {
  					cells: 1,
  					style: {},
  				} );
  				panels_data.grid_cells.push( {
  					grid: panels_data.grids.length - 1,
  					weight: 1,
  				} );
  				panels_data.widgets.push( {
  					filter: "1",
  					text: $html.html().replace(/^\s+|\s+$/gm,''),
  					title: "",
  					type: "visual",
  					panels_info: {
  						class: editorClass,
  						raw: false,
  						grid: panels_data.grids.length - 1,
  						cell: 0
  					}
  				} );
  			}
  
  			return panels_data;
  		}
  		else {
  			// This is probably just old school post content
  			return {
  				grid_cells: [ { grid: 0, weight: 1 } ],
  				grids: [ { cells: 1 } ],
  				widgets: [
  					{
  						filter: "1",
  						text: html,
  						title: "",
  						type: "visual",
  						panels_info: {
  							class: editorClass,
  							raw: false,
  							grid: 0,
  							cell: 0
  						}
  					}
  				]
  			};
  		}
  	}
  } );
  
  },{}],18:[function(require,module,exports){
  module.exports = Backbone.Model.extend( {
  	/* A collection of widgets */
  	widgets: {},
  
  	/* The row this model belongs to */
  	row: null,
  
  	defaults: {
  		weight: 0,
  		style: {}
  	},
  
  	indexes: null,
  
  	/**
  	 * Set up the cell model
  	 */
  	initialize: function () {
  		this.set( 'widgets', new panels.collection.widgets() );
  		this.on( 'destroy', this.onDestroy, this );
  	},
  
  	/**
  	 * Triggered when we destroy a cell
  	 */
  	onDestroy: function () {
  		// Destroy all the widgets
  		_.invoke( this.get('widgets').toArray(), 'destroy' );
  		this.get('widgets').reset();
  	},
  
  	/**
  	 * Create a clone of the cell, along with all its widgets
  	 */
  	clone: function ( row, cloneOptions ) {
  		if ( _.isUndefined( row ) ) {
  			row = this.row;
  		}
  		cloneOptions = _.extend( {cloneWidgets: true}, cloneOptions );
  
  		var clone = new this.constructor( this.attributes );
  		clone.set( 'collection', row.get('cells'), {silent: true} );
  		clone.row = row;
  
  		if ( cloneOptions.cloneWidgets ) {
  			// Now we're going add all the widgets that belong to this, to the clone
  			this.get('widgets').each( function ( widget ) {
  				clone.get('widgets').add( widget.clone( clone, cloneOptions ), {silent: true} );
  			} );
  		}
  
  		return clone;
  	}
  
  } );
  
  },{}],19:[function(require,module,exports){
  module.exports = Backbone.Model.extend( {
  	defaults: {
  		text: '',
  		data: '',
  		time: null,
  		count: 1
  	}
  } );
  
  },{}],20:[function(require,module,exports){
  module.exports = Backbone.Model.extend( {
  	/* The builder model */
  	builder: null,
  
  	defaults: {
  		style: {}
  	},
  
  	indexes: null,
  
  	/**
  	 * Initialize the row model
  	 */
  	initialize: function () {
  		if ( _.isEmpty(this.get('cells') ) ) {
  			this.set('cells', new panels.collection.cells());
  		}
  		else {
  			// Make sure that the cells have this row set as their parent
  			this.get('cells').each( function( cell ){
  				cell.row = this;
  			}.bind( this ) );
  		}
  		this.on( 'destroy', this.onDestroy, this );
  	},
  
  	/**
  	 * Add cells to the model row
  	 *
  	 * @param newCells the updated collection of cell models
  	 */
  	setCells: function ( newCells ) {
  		var currentCells = this.get('cells') || new panels.collection.cells();
  		var cellsToRemove = [];
  
  		currentCells.each(function (cell, i) {
  			var newCell = newCells.at(i);
  			if(newCell) {
  				cell.set('weight', newCell.get('weight'));
  			} else {
  				var newParentCell = currentCells.at( newCells.length - 1 );
  
  				// First move all the widgets to the new cell
  				var widgetsToMove = cell.get('widgets').models.slice();
  				for ( var j = 0; j < widgetsToMove.length; j++ ) {
  					widgetsToMove[j].moveToCell( newParentCell, { silent: false } );
  				}
  
  				cellsToRemove.push(cell);
  			}
  		});
  
  		_.each(cellsToRemove, function(cell) {
  			currentCells.remove(cell);
  		});
  
  		if( newCells.length > currentCells.length) {
  			_.each(newCells.slice(currentCells.length, newCells.length), function (newCell) {
  				// TODO: make sure row and collection is set correctly when cell is created then we can just add new cells
  				newCell.set({collection: currentCells});
  				newCell.row = this;
  				currentCells.add(newCell);
  			}.bind(this));
  		}
  
  		// Rescale the cells when we add or remove
  		this.reweightCells();
  	},
  
  	/**
  	 * Make sure that all the cell weights add up to 1
  	 */
  	reweightCells: function () {
  		var totalWeight = 0;
  		var cells = this.get('cells');
  		cells.each( function ( cell ) {
  			totalWeight += cell.get( 'weight' );
  		} );
  
  		cells.each( function ( cell ) {
  			cell.set( 'weight', cell.get( 'weight' ) / totalWeight );
  		} );
  
  		// This is for the row view to hook into and resize
  		this.trigger( 'reweight_cells' );
  	},
  
  	/**
  	 * Triggered when the model is destroyed
  	 */
  	onDestroy: function () {
  		// Also destroy all the cells
  		_.invoke( this.get('cells').toArray(), 'destroy' );
  		this.get('cells').reset();
  	},
  
  	/**
  	 * Create a clone of the row, along with all its cells
  	 *
  	 * @param {panels.model.builder} builder The builder model to attach this to.
  	 *
  	 * @return {panels.model.row} The cloned row.
  	 */
  	clone: function ( builder ) {
  		if ( _.isUndefined( builder ) ) {
  			builder = this.builder;
  		}
  
  		var clone = new this.constructor( this.attributes );
  		clone.set( 'collection', builder.get('rows'), {silent: true} );
  		clone.builder = builder;
  
  		var cellClones = new panels.collection.cells();
  		this.get('cells').each( function ( cell ) {
  			cellClones.add( cell.clone( clone ), {silent: true} );
  		} );
  
  		clone.set( 'cells', cellClones );
  
  		return clone;
  	}
  } );
  
  },{}],21:[function(require,module,exports){
  /**
   * Model for an instance of a widget
   */
  module.exports = Backbone.Model.extend( {
  
  	cell: null,
  
  	defaults: {
  		// The PHP Class of the widget
  		class: null,
  
  		// Is this class missing? Missing widgets are a special case.
  		missing: false,
  
  		// The values of the widget
  		values: {},
  
  		// Have the current values been passed through the widgets update function
  		raw: false,
  
  		// Visual style fields
  		style: {},
  
  		read_only: false,
  		widget_id: '',
  	},
  
  	indexes: null,
  
  	initialize: function () {
  		var widgetClass = this.get( 'class' );
  		if ( _.isUndefined( panelsOptions.widgets[widgetClass] ) || ! panelsOptions.widgets[widgetClass].installed ) {
  			this.set( 'missing', true );
  		}
  	},
  
  	/**
  	 * @param field
  	 * @returns {*}
  	 */
  	getWidgetField: function ( field ) {
  		if ( _.isUndefined( panelsOptions.widgets[this.get( 'class' )] ) ) {
  			if ( field === 'title' || field === 'description' ) {
  				return panelsOptions.loc.missing_widget[field];
  			} else {
  				return '';
  			}
  		} else if ( this.has( 'label' ) && ! _.isEmpty( this.get( 'label' ) ) ) {
  			// Use the label instead of the actual widget title
  			return this.get( 'label' );
  		} else {
  			return panelsOptions.widgets[ this.get( 'class' ) ][ field ];
  		}
  	},
  
  	/**
  	 * Move this widget model to a new cell. Called by the views.
  	 *
  	 * @param panels.model.cell newCell
  	 * @param object options The options passed to the
  	 *
  	 * @return boolean Indicating if the widget was moved into a different cell
  	 */
  	moveToCell: function ( newCell, options, at ) {
  		options = _.extend( {
  			silent: true,
  		}, options );
  
  		this.cell = newCell;
  		this.collection.remove( this, options );
  		newCell.get('widgets').add( this, _.extend( {
  			at: at
  		}, options ) );
  
  		// This should be used by views to reposition everything.
  		this.trigger( 'move_to_cell', newCell, at );
  
  		return this;
  	},
  
  	/**
  	 * Trigger an event on the model that indicates a user wants to edit it
  	 */
  	triggerEdit: function () {
  		this.trigger( 'user_edit', this );
  	},
  
  	/**
  	 * Trigger an event on the widget that indicates a user wants to duplicate it
  	 */
  	triggerDuplicate: function () {
  		this.trigger( 'user_duplicate', this );
  	},
  
  	/**
  	 * This is basically a wrapper for set that checks if we need to trigger a change
  	 */
  	setValues: function ( values ) {
  		var hasChanged = false;
  		if ( JSON.stringify( values ) !== JSON.stringify( this.get( 'values' ) ) ) {
  			hasChanged = true;
  		}
  
  		this.set( 'values', values, {silent: true} );
  
  		if ( hasChanged ) {
  			// We'll trigger our own change events.
  			// NB: Must include the model being changed (i.e. `this`) as a workaround for a bug in Backbone 1.2.3
  			this.trigger( 'change', this );
  			this.trigger( 'change:values' );
  		}
  	},
  
  	/**
  	 * Create a clone of this widget attached to the given cell.
  	 *
  	 * @param {panels.model.cell} cell The cell model we're attaching this widget clone to.
  	 * @returns {panels.model.widget}
  	 */
  	clone: function ( cell, options ) {
  		if ( _.isUndefined( cell ) ) {
  			cell = this.cell;
  		}
  
  		var clone = new this.constructor( this.attributes );
  
  		// Create a deep clone of the original values
  		var cloneValues = JSON.parse( JSON.stringify( this.get( 'values' ) ) );
  
  		// We want to exclude any fields that start with _ from the clone. Assuming these are internal.
  		var cleanClone = function ( vals ) {
  			_.each( vals, function ( el, i ) {
  				if ( _.isString( i ) && i[0] === '_' ) {
  					delete vals[i];
  				}
  				else if ( _.isObject( vals[i] ) ) {
  					cleanClone( vals[i] );
  				}
  			} );
  
  			return vals;
  		};
  		cloneValues = cleanClone( cloneValues );
  
  		if ( this.get( 'class' ) === "SiteOrigin_Panels_Widgets_Layout" ) {
  			// Special case of this being a layout widget, it needs a new ID
  			cloneValues.builder_id = Math.random().toString( 36 ).substr( 2 );
  		}
  
  		clone.set( 'values', cloneValues, {silent: true} );
  		clone.set( 'collection', cell.get('widgets'), {silent: true} );
  		clone.cell = cell;
  
  		// This is used to force a form reload later on
  		clone.isDuplicate = true;
  
  		return clone;
  	},
  
  	/**
  	 * Gets the value that makes most sense as the title.
  	 */
  	getTitle: function () {
  		var widgetData = panelsOptions.widgets[this.get( 'class' )];
  
  		if ( _.isUndefined( widgetData ) ) {
  			return this.get( 'class' ).replace( /_/g, ' ' );
  		}
  		else if ( ! _.isUndefined( widgetData.panels_title ) ) {
  			// This means that the widget has told us which field it wants us to use as a title
  			if ( widgetData.panels_title === false ) {
  				return panelsOptions.widgets[this.get( 'class' )].description;
  			}
  		}
  
  		var values = this.get( 'values' );
  
  		// Create a list of fields to check for a title
  		var titleFields = ['title', 'text'];
  
  		for ( var k in values ) {
  			if ( values.hasOwnProperty( k ) ) {
  				titleFields.push( k );
  			}
  		}
  
  		titleFields = _.uniq( titleFields );
  
  		for ( var i in titleFields ) {
  			if (
  				! _.isUndefined( values[titleFields[i]] ) &&
  				_.isString( values[titleFields[i]] ) &&
  				values[titleFields[i]] !== '' &&
  				values[titleFields[i]] !== 'on' &&
  				titleFields[i][0] !== '_' && ! jQuery.isNumeric( values[titleFields[i]] )
  			) {
  				var title = values[titleFields[i]];
  				title = title.replace( /<\/?[^>]+(>|$)/g, "" );
  				var parts = title.split( " " );
  				parts = parts.slice( 0, 20 );
  				return parts.join( ' ' );
  			}
  		}
  
  		// If we still have nothing, then just return the widget description
  		return this.getWidgetField( 'description' );
  	}
  
  } );
  
  },{}],22:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = Backbone.View.extend( {
  	wrapperTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-context-menu' ).html() ) ),
  	sectionTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-context-menu-section' ).html() ) ),
  
  	contexts: [],
  	active: false,
  
  	events: {
  		'keyup .so-search-wrapper input': 'searchKeyUp'
  	},
  
  	/**
  	 * Intialize the context menu
  	 */
  	initialize: function () {
  		this.listenContextMenu();
  		this.render();
  		this.attach();
  	},
  
  	/**
  	 * Listen for the right click context menu
  	 */
  	listenContextMenu: function () {
  		var thisView = this;
  
  		$( window ).on( 'contextmenu', function ( e ) {
  			if ( thisView.active && ! thisView.isOverEl( thisView.$el, e ) ) {
  				thisView.closeMenu();
  				thisView.active = false;
  				e.preventDefault();
  				return false;
  			}
  
  			if ( thisView.active ) {
  				// Lets not double up on the context menu
  				return true;
  			}
  
  			// Other components should listen to activate_context
  			thisView.active = false;
  			thisView.trigger( 'activate_context', e, thisView );
  
  			if ( thisView.active ) {
  				// We don't want the default event to happen.
  				e.preventDefault();
  
  				thisView.openMenu( {
  					left: e.pageX,
  					top: e.pageY
  				} );
  			}
  		} );
  	},
  
  	render: function () {
  		this.setElement( this.wrapperTemplate() );
  	},
  
  	attach: function () {
  		this.$el.appendTo( 'body' );
  	},
  
  	/**
  	 * Display the actual context menu.
  	 *
  	 * @param position
  	 */
  	openMenu: function ( position ) {
  		this.trigger( 'open_menu' );
  
  		// Start listening for situations when we should close the menu
  		$( window ).on( 'keyup', {menu: this}, this.keyboardListen );
  		$( window ).on( 'click', {menu: this}, this.clickOutsideListen );
  
  		// Set the maximum height of the menu
  		this.$el.css( 'max-height', $( window ).height() - 20 );
  
  		// Correct the left position
  		if ( position.left + this.$el.outerWidth() + 10 >= $( window ).width() ) {
  			position.left = $( window ).width() - this.$el.outerWidth() - 10;
  		}
  		if ( position.left <= 0 ) {
  			position.left = 10;
  		}
  
  		// Check top position
  		if ( position.top + this.$el.outerHeight() - $( window ).scrollTop() + 10 >= $( window ).height() ) {
  			position.top = $( window ).height() + $( window ).scrollTop() - this.$el.outerHeight() - 10;
  		}
  		if ( position.left <= 0 ) {
  			position.left = 10;
  		}
  
  		// position the contextual menu
  		this.$el.css( {
  			left: position.left + 1,
  			top: position.top + 1
  		} ).show();
  		this.$( '.so-search-wrapper input' ).focus();
  	},
  
  	closeMenu: function () {
  		this.trigger( 'close_menu' );
  
  		// Stop listening for situations when we should close the menu
  		$( window ).off( 'keyup', this.keyboardListen );
  		$( window ).off( 'click', this.clickOutsideListen );
  
  		this.active = false;
  		this.$el.empty().hide();
  	},
  
  	/**
  	 * Keyboard events handler
  	 */
  	keyboardListen: function ( e ) {
  		var menu = e.data.menu;
  
  		switch ( e.which ) {
  			case 27:
  				menu.closeMenu();
  				break;
  		}
  	},
  
  	/**
  	 * Listen for a click outside the menu to close it.
  	 * @param e
  	 */
  	clickOutsideListen: function ( e ) {
  		var menu = e.data.menu;
  		if ( e.which !== 3 && menu.$el.is( ':visible' ) && ! menu.isOverEl( menu.$el, e ) ) {
  			menu.closeMenu();
  		}
  	},
  
  	/**
  	 * Add a new section to the contextual menu.
  	 *
  	 * @param settings
  	 * @param items
  	 * @param callback
  	 */
  	addSection: function ( id, settings, items, callback ) {
  		var thisView = this;
  		settings = _.extend( {
  			display: 5,
  			defaultDisplay: false,
  			search: true,
  
  			// All the labels
  			sectionTitle: '',
  			searchPlaceholder: '',
  
  			// This is the key to be used in items for the title. Makes it easier to list objects
  			titleKey: 'title'
  		}, settings );
  
  		// Create the new section
  		var section = $( this.sectionTemplate( {
  			settings: settings,
  			items: items
  		} ) ).attr( 'id', 'panels-menu-section-' + id );
  		this.$el.append( section );
  
  		section.find( '.so-item:not(.so-confirm)' ).click( function () {
  			var $$ = $( this );
  			callback( $$.data( 'key' ) );
  			thisView.closeMenu();
  		} );
  
  		section.find( '.so-item.so-confirm' ).click( function () {
  			var $$ = $( this );
  
  			if ( $$.hasClass( 'so-confirming' ) ) {
  				callback( $$.data( 'key' ) );
  				thisView.closeMenu();
  				return;
  			}
  
  			$$
  				.data( 'original-text', $$.html() )
  				.addClass( 'so-confirming' )
  				.html( '<span class="dashicons dashicons-yes"></span> ' + panelsOptions.loc.dropdown_confirm );
  
  			setTimeout( function () {
  				$$.removeClass( 'so-confirming' );
  				$$.html( $$.data( 'original-text' ) );
  			}, 2500 );
  		} );
  
  		section.data( 'settings', settings ).find( '.so-search-wrapper input' ).trigger( 'keyup' );
  
  		this.active = true;
  	},
  
  	/**
  	 * Check if a section exists in the current menu.
  	 *
  	 * @param id
  	 * @returns {boolean}
  	 */
  	hasSection: function( id ){
  		return this.$el.find( '#panels-menu-section-' + id  ).length > 0;
  	},
  
  	/**
  	 * Handle searching inside a section.
  	 *
  	 * @param e
  	 * @returns {boolean}
  	 */
  	searchKeyUp: function ( e ) {
  		var
  			$$ = $( e.currentTarget ),
  			section = $$.closest( '.so-section' ),
  			settings = section.data( 'settings' );
  
  		if ( e.which === 38 || e.which === 40 ) {
  			// First, lets check if this is an up, down or enter press
  			var
  				items = section.find( 'ul li:visible' ),
  				activeItem = items.filter( '.so-active' ).eq( 0 );
  
  			if ( activeItem.length ) {
  				items.removeClass( 'so-active' );
  
  				var activeIndex = items.index( activeItem );
  
  				if ( e.which === 38 ) {
  					if ( activeIndex - 1 < 0 ) {
  						activeItem = items.last();
  					} else {
  						activeItem = items.eq( activeIndex - 1 );
  					}
  				}
  				else if ( e.which === 40 ) {
  					if ( activeIndex + 1 >= items.length ) {
  						activeItem = items.first();
  					} else {
  						activeItem = items.eq( activeIndex + 1 );
  					}
  				}
  			}
  			else if ( e.which === 38 ) {
  				activeItem = items.last();
  			}
  			else if ( e.which === 40 ) {
  				activeItem = items.first();
  			}
  
  			activeItem.addClass( 'so-active' );
  			return false;
  		}
  		if ( e.which === 13 ) {
  			if ( section.find( 'ul li:visible' ).length === 1 ) {
  				// We'll treat a single visible item as active when enter is clicked
  				section.find( 'ul li:visible' ).trigger( 'click' );
  				return false;
  			}
  			section.find( 'ul li.so-active:visible' ).trigger( 'click' );
  			return false;
  		}
  
  		if ( $$.val() === '' ) {
  			// We'll display the defaultDisplay items
  			if ( settings.defaultDisplay ) {
  				section.find( '.so-item' ).hide();
  				for ( var i = 0; i < settings.defaultDisplay.length; i ++ ) {
  					section.find( '.so-item[data-key="' + settings.defaultDisplay[i] + '"]' ).show();
  				}
  			} else {
  				// We'll just display all the items
  				section.find( '.so-item' ).show();
  			}
  		} else {
  			section.find( '.so-item' ).hide().each( function () {
  				var item = $( this );
  				if ( item.html().toLowerCase().indexOf( $$.val().toLowerCase() ) !== - 1 ) {
  					item.show();
  				}
  			} );
  		}
  
  		// Now, we'll only show the first settings.display visible items
  		section.find( '.so-item:visible:gt(' + (
  			settings.display - 1
  			) + ')' ).hide();
  
  
  		if ( section.find( '.so-item:visible' ).length === 0 && $$.val() !== '' ) {
  			section.find( '.so-no-results' ).show();
  		} else {
  			section.find( '.so-no-results' ).hide();
  		}
  	},
  
  	/**
  	 * Check if the given mouse event is over the element
  	 * @param el
  	 * @param event
  	 */
  	isOverEl: function ( el, event ) {
  		var elPos = [
  			[el.offset().left, el.offset().top],
  			[el.offset().left + el.outerWidth(), el.offset().top + el.outerHeight()]
  		];
  
  		// Return if this event is over the given element
  		return (
  			event.pageX >= elPos[0][0] && event.pageX <= elPos[1][0] &&
  			event.pageY >= elPos[0][1] && event.pageY <= elPos[1][1]
  		);
  	}
  
  } );
  
  },{}],23:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = Backbone.View.extend( {
  
  	// Config options
  	config: {},
  
  	template: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-builder' ).html() ) ),
  	dialogs: {},
  	rowsSortable: null,
  	dataField: false,
  	currentData: '',
  
  	attachedToEditor: false,
  	attachedVisible: false,
  	liveEditor: undefined,
  	menu: false,
  
  	activeCell: null,
  
  	events: {
  		'click .so-tool-button.so-widget-add': 'displayAddWidgetDialog',
  		'click .so-tool-button.so-row-add': 'displayAddRowDialog',
  		'click .so-tool-button.so-prebuilt-add': 'displayAddPrebuiltDialog',
  		'click .so-tool-button.so-history': 'displayHistoryDialog',
  		'click .so-tool-button.so-live-editor': 'displayLiveEditor',
  		'click .so-learn-wrapper .show-tutorials': 'loadTutorials'
  	},
  
  	/* A row collection */
  	rows: null,
  
  	/**
  	 * Initialize the builder
  	 */
  	initialize: function ( options ) {
  		var builder = this;
  
  		this.config = _.extend( {
  			loadLiveEditor: false,
  			builderSupports : {}
  		}, options.config);
  
  		// These are the actions that a user can perform in the builder
  		this.config.builderSupports = _.extend( {
  			addRow: true,
  			editRow: true,
  			deleteRow: true,
  			moveRow: true,
  			addWidget: true,
  			editWidget: true,
  			deleteWidget: true,
  			moveWidget: true,
  			prebuilt: true,
  			history: true,
  			liveEditor: true,
  			revertToEditor: true
  		}, this.config.builderSupports );
  
  		// Automatically load the live editor as soon as it's ready
  		if( options.config.loadLiveEditor ) {
  			this.on( 'builder_live_editor_added', function(){
  				this.displayLiveEditor();
  			} );
  			}
  
  		// Now lets create all the dialog boxes that the main builder interface uses
  		this.dialogs = {
  			widgets: new panels.dialog.widgets(),
  			row: new panels.dialog.row(),
  			prebuilt: new panels.dialog.prebuilt()
  		};
  
  		// Set the builder for each dialog and render it.
  		_.each( this.dialogs, function ( p, i, d ) {
  			d[i].setBuilder( builder );
  		} );
  
  		this.dialogs.row.setRowDialogType( 'create' );
  
  		// This handles a new row being added to the collection - we'll display it in the interface
  		this.model.get('rows').on( 'add', this.onAddRow, this );
  
  		// Reflow the entire builder when ever the
  		$( window ).resize( function ( e ) {
  			if ( e.target === window ) {
  				builder.trigger( 'builder_resize' );
  			}
  		} );
  
  		// When the data changes in the model, store it in the field
  		this.model.on( 'change:data load_panels_data', this.storeModelData, this );
  
  		// Handle a content change
  		this.on( 'content_change', this.handleContentChange, this );
  		this.on( 'display_builder', this.handleDisplayBuilder, this );
  		this.on( 'hide_builder', this.handleHideBuilder, this );
  		this.on( 'builder_rendered builder_resize', this.handleBuilderSizing, this );
  		this.model.on( 'change:data load_panels_data', this.toggleWelcomeDisplay, this );
  
  		this.on( 'display_builder', this.wrapEditorExpandAdjust, this );
  
  		// Create the context menu for this builder
  		this.menu = new panels.utils.menu( {} );
  		this.menu.on( 'activate_context', this.activateContextMenu, this );
  
  		if( this.config.loadOnAttach ) {
  			this.on( 'builder_attached_to_editor', function(){
  				this.displayAttachedBuilder( { confirm: false } );
  			}, this );
  		}
  
  
  		return this;
  	},
  
  	/**
  	 * Render the builder interface.
  	 *
  	 * @return {panels.view.builder}
  	 */
  	render: function () {
  		// this.$el.html( this.template() );
  		this.setElement( this.template() );
  		this.$el
  			.attr( 'id', 'siteorigin-panels-builder-' + this.cid )
  			.addClass( 'so-builder-container' );
  
  		if( panelsOptions.tutorials_enabled ) {
  			this.loadTutorials();
  		}
  
  		this.trigger( 'builder_rendered' );
  
  		return this;
  	},
  
  	/**
  	 * Attach the builder to the given container
  	 *
  	 * @param container
  	 * @returns {panels.view.builder}
  	 */
  	attach: function ( options ) {
  
  		options = _.extend( {
  			container: false,
  			dialog: false
  		}, options );
  
  		if ( options.dialog ) {
  			// We're going to add this to a dialog
  			this.dialog = new panels.dialog.builder();
  			this.dialog.builder = this;
  		} else {
  			// Attach this in the standard way
  			this.$el.appendTo( options.container );
  			this.metabox = options.container.closest( '.postbox' );
  			this.initSortable();
  			this.trigger( 'attached_to_container', options.container );
  		}
  
  		this.trigger( 'builder_attached' );
  
  		// Add support for components we have
  
  		if( this.supports( 'liveEditor' ) ) {
  			this.addLiveEditor();
  		}
  		if( this.supports( 'history' ) ) {
  			this.addHistoryBrowser();
  		}
  
  		// Hide toolbar buttons we don't support
  		var toolbar = this.$('.so-builder-toolbar');
  		if( ! this.supports( 'addWidget' ) ) {
  			toolbar.find('.so-widget-add' ).hide();
  		}
  		if( ! this.supports( 'addRow' ) ) {
  			toolbar.find('.so-row-add' ).hide();
  		}
  		if( ! this.supports( 'prebuilt' ) ) {
  			toolbar.find('.so-prebuilt-add' ).hide();
  		}
  
  		return this;
  	},
  
  	/**
  	 * This will move the Page Builder meta box into the editor if we're in the post/page edit interface.
  	 *
  	 * @returns {panels.view.builder}
  	 */
  	attachToEditor: function () {
  		if ( this.config.editorType !== 'tinyMCE' ) {
  			return this;
  		}
  
  		this.attachedToEditor = true;
  		var metabox = this.metabox;
  		var thisView = this;
  
  		// Handle switching between the page builder and other tabs
  		$( '#wp-content-wrap .wp-editor-tabs' )
  			.find( '.wp-switch-editor' )
  			.click( function ( e ) {
  				e.preventDefault();
  				$( '#wp-content-editor-container' ).show();
  
  				// metabox.hide();
  				$( '#wp-content-wrap' ).removeClass( 'panels-active' );
  				$( '#content-resize-handle' ).show();
  
  				// Make sure the word count is visible
  				thisView.trigger( 'hide_builder' );
  			} ).end()
  			.append(
  				$( '<a id="content-panels" class="hide-if-no-js wp-switch-editor switch-panels">' + metabox.find( '.hndle span' ).html() + '</a>' )
  					.click( function ( e ) {
  						if ( thisView.displayAttachedBuilder( { confirm: true } ) ) {
  							e.preventDefault();
  						}
  					} )
  			);
  
  		// Switch back to the standard editor
  		if( this.supports( 'revertToEditor' ) ) {
  			metabox.find( '.so-switch-to-standard' ).click( function ( e ) {
  				e.preventDefault();
  
  				if ( ! confirm( panelsOptions.loc.confirm_stop_builder ) ) {
  					return;
  				}
  
  				// User is switching to the standard visual editor
  				thisView.addHistoryEntry( 'back_to_editor' );
  				thisView.model.loadPanelsData( false );
  
  				// Switch back to the standard editor
  				$( '#wp-content-wrap' ).show();
  				metabox.hide();
  
  				// Resize to trigger reflow of WordPress editor stuff
  				$( window ).resize();
  
                  thisView.attachedVisible = false;
  				thisView.trigger( 'hide_builder' );
  			} ).show();
  		}
  
  		// Move the panels box into a tab of the content editor
  		metabox.insertAfter( '#wp-content-wrap' ).hide().addClass( 'attached-to-editor' );
  
  		// Switch to the Page Builder interface as soon as we load the page if there are widgets
  		var data = this.model.get( 'data' );
  		if ( ! _.isEmpty( data.widgets ) || ! _.isEmpty( data.grids ) ) {
  			this.displayAttachedBuilder( { confirm: false } );
  		}
  
  		// We will also make this sticky if its attached to an editor.
  		var stickToolbar = function () {
  			var toolbar = thisView.$( '.so-builder-toolbar' );
  
  			if ( thisView.$el.hasClass( 'so-display-narrow' ) ) {
  				// In this case, we don't want to stick the toolbar.
  				toolbar.css( {
  					top: 0,
  					left: 0,
  					width: '100%',
  					position: 'absolute'
  				} );
  				thisView.$el.css( 'padding-top', toolbar.outerHeight() );
  				return;
  			}
  
  			var newTop = $( window ).scrollTop() - thisView.$el.offset().top;
  
  			if ( $( '#wpadminbar' ).css( 'position' ) === 'fixed' ) {
  				newTop += $( '#wpadminbar' ).outerHeight();
  			}
  
  			var limits = {
  				top: 0,
  				bottom: thisView.$el.outerHeight() - toolbar.outerHeight() + 20
  			};
  
  			if ( newTop > limits.top && newTop < limits.bottom ) {
  				if ( toolbar.css( 'position' ) !== 'fixed' ) {
  					// The toolbar needs to stick to the top, over the interface
  					toolbar.css( {
  						top: $( '#wpadminbar' ).outerHeight(),
  						left: thisView.$el.offset().left,
  						width: thisView.$el.outerWidth(),
  						position: 'fixed'
  					} );
  				}
  			} else {
  				// The toolbar needs to be at the top or bottom of the interface
  				toolbar.css( {
  					top: Math.min( Math.max( newTop, 0 ), thisView.$el.outerHeight() - toolbar.outerHeight() + 20 ),
  					left: 0,
  					width: '100%',
  					position: 'absolute'
  				} );
  			}
  
  			thisView.$el.css( 'padding-top', toolbar.outerHeight() );
  		};
  
  		this.on( 'builder_resize', stickToolbar, this );
  		$( document ).scroll( stickToolbar );
  		stickToolbar();
  
  		this.trigger('builder_attached_to_editor');
  
  		return this;
  	},
  
  	/**
  	 * Display the builder interface when attached to a WordPress editor
  	 */
  	displayAttachedBuilder: function( options ){
  		options = _.extend( {
  			confirm: true
  		}, options );
  
  		// Switch to the Page Builder interface
  
  		if( options.confirm ) {
  			var editor = typeof tinyMCE !== 'undefined' ? tinyMCE.get( 'content' ) : false;
  			var editorContent = ( editor && _.isFunction( editor.getContent ) ) ? editor.getContent() : $( 'textarea#content' ).val();
  
  			if ( editorContent !== '' && ! confirm( panelsOptions.loc.confirm_use_builder ) ) {
  				return false;
  			}
  		}
  
  		// Hide the standard content editor
  		$( '#wp-content-wrap' ).hide();
  
  		// Show page builder and the inside div
  		this.metabox.show().find( '> .inside' ).show();
  
  		// Triggers full refresh
  		$( window ).resize();
  		$( document ).scroll();
  
  		// Make sure the word count is visible
  		this.attachedVisible = true;
  		this.trigger( 'display_builder' );
  
  		return true;
  	},
  
  	/**
  	 * Initialize the row sortables
  	 */
  	initSortable: function () {
  		if( ! this.supports( 'moveRow' ) ) {
  			return this;
  		}
  
  		// Create the sortable for the rows
  		var builderView = this;
  
  		this.rowsSortable = this.$( '.so-rows-container' ).sortable( {
  			appendTo: '#wpwrap',
  			items: '.so-row-container',
  			handle: '.so-row-move',
  			axis: 'y',
  			tolerance: 'pointer',
  			scroll: false,
  			stop: function ( e, ui ) {
  				builderView.addHistoryEntry( 'row_moved' );
  
  				var $$ =  $( ui.item ),
  					row = $$.data( 'view' );
  
  				builderView.model.get('rows').remove( row.model, {
  					'silent' : true
  				} );
  				builderView.model.get('rows').add( row.model, {
  					'silent' : true,
  					'at' : $$.index()
  				} );
  
  				row.trigger( 'move', $$.index() );
  
  				builderView.model.refreshPanelsData();
  			}
  		} );
  
  		return this;
  	},
  
  	/**
  	 * Refresh the row sortable
  	 */
  	refreshSortable: function () {
  		// Refresh the sortable to account for the new row
  		if ( ! _.isNull( this.rowsSortable ) ) {
  			this.rowsSortable.sortable( 'refresh' );
  		}
  	},
  
  	/**
  	 * Set the field that's used to store the data
  	 * @param field
  	 */
  	setDataField: function ( field, options ) {
  		options = _.extend( {
  			load: true
  		}, options );
  
  		this.dataField = field;
  		this.dataField.data( 'builder', this );
  
  		if ( options.load && field.val() !== '' ) {
  			var data = this.dataField.val();
  			try {
  				data = JSON.parse( data );
  			}
  			catch ( err ) {
  				data = {};
  			}
  
  			this.model.loadPanelsData( data );
  			this.currentData = data;
  			this.toggleWelcomeDisplay();
  		}
  
  		return this;
  	},
  
  	/**
  	 * Store the model data in the data html field set in this.setDataField.
  	 */
  	storeModelData: function () {
  		var data = JSON.stringify( this.model.get( 'data' ) );
  
  		if ( $( this.dataField ).val() !== data ) {
  			// If the data is different, set it and trigger a content_change event
  			$( this.dataField ).val( data );
  			$( this.dataField ).trigger( 'change' );
  			this.trigger( 'content_change' );
  		}
  	},
  
  	/**
  	 * HAndle the visual side of adding a new row to the builder.
  	 *
  	 * @param row
  	 * @param collection
  	 * @param options
  	 */
  	onAddRow: function ( row, collection, options ) {
  		options = _.extend( {noAnimate: false}, options );
  		// Create a view for the row
  		var rowView = new panels.view.row( {model: row} );
  		rowView.builder = this;
  		rowView.render();
  
  		// Attach the row elements to this builder
  		if ( _.isUndefined( options.at ) || collection.length <= 1 ) {
  			// Insert this at the end of the widgets container
  			rowView.$el.appendTo( this.$( '.so-rows-container' ) );
  		} else {
  			// We need to insert this at a specific position
  			rowView.$el.insertAfter(
  				this.$( '.so-rows-container .so-row-container' ).eq( options.at - 1 )
  			);
  		}
  
  		if ( options.noAnimate === false ) {
  			rowView.visualCreate();
  		}
  
  		this.refreshSortable();
  		rowView.resize();
  	},
  
  	/**
  	 * Display the dialog to add a new widget.
  	 *
  	 * @returns {boolean}
  	 */
  	displayAddWidgetDialog: function () {
  		this.dialogs.widgets.openDialog();
  	},
  
  	/**
  	 * Display the dialog to add a new row.
  	 */
  	displayAddRowDialog: function () {
  		var row = new panels.model.row();
  		var cells = new panels.collection.cells([{weight: 0.5}, {weight: 0.5}]);
  		cells.each(function (cell) {
  			cell.row = row;
  		});
  		row.set('cells', cells);
  		row.builder = this.model;
  
  		this.dialogs.row.setRowModel(row);
  		this.dialogs.row.openDialog();
  	},
  
  	/**
  	 * Display the dialog to add prebuilt layouts.
  	 *
  	 * @returns {boolean}
  	 */
  	displayAddPrebuiltDialog: function () {
  		this.dialogs.prebuilt.openDialog();
  	},
  
  	/**
  	 * Display the history dialog.
  	 *
  	 * @returns {boolean}
  	 */
  	displayHistoryDialog: function () {
  		this.dialogs.history.openDialog();
  	},
  
  	/**
  	 * Handle pasting a row into the builder.
  	 */
  	pasteRowHandler: function(){
  		var pastedModel = panels.helpers.clipboard.getModel( 'row-model' );
  
  		if( ! _.isEmpty( pastedModel ) && pastedModel instanceof panels.model.row ) {
  			this.addHistoryEntry( 'row_pasted' );
  			pastedModel.builder = this.model;
  			this.model.get('rows').add( pastedModel, {
  				at: this.model.get('rows').indexOf( this.model ) + 1
  			} );
  			this.model.refreshPanelsData();
  		}
  	},
  
  	/**
  	 * Get the model for the currently selected cell
  	 */
  	getActiveCell: function ( options ) {
  		options = _.extend( {
  			createCell: true,
  		}, options );
  
  		if( ! this.model.get('rows').length ) {
  			// There aren't any rows yet
  			if ( options.createCell ) {
  				// Create a row with a single cell
  				this.model.addRow( {}, [{ weight: 1 }], { noAnimate: true } );
  			} else {
  				return null;
  			}
  		}
  
  		// Make sure the active cell isn't empty, and it's in a row that exists
  		var activeCell = this.activeCell;
  		if( _.isEmpty( activeCell ) || this.model.get('rows').indexOf( activeCell.model.row ) === -1 ) {
  			return this.model.get('rows').last().get('cells').first();
  		} else {
  			return activeCell.model;
  		}
  	},
  
  	/**
  	 * Add a live editor to the builder
  	 *
  	 * @returns {panels.view.builder}
  	 */
  	addLiveEditor: function ( ) {
  		if( _.isEmpty( this.config.liveEditorPreview ) ) {
  			return this;
  		}
  
  		// Create the live editor and set the builder to this.
  		this.liveEditor = new panels.view.liveEditor( {
  			builder: this,
  			previewUrl: this.config.liveEditorPreview
  		} );
  
  		// Display the live editor button in the toolbar
  		if ( this.liveEditor.hasPreviewUrl() ) {
  			this.$( '.so-builder-toolbar .so-live-editor' ).show();
  		}
  
  		this.trigger('builder_live_editor_added');
  
  		return this;
  	},
  
  	/**
  	 * Show the current live editor
  	 */
  	displayLiveEditor: function () {
  		if ( _.isUndefined( this.liveEditor ) ) {
  			return;
  		}
  
  		this.liveEditor.open();
  	},
  
  	/**
  	 * Add the history browser.
  	 *
  	 * @return {panels.view.builder}
  	 */
  	addHistoryBrowser: function () {
  		if( _.isEmpty( this.config.liveEditorPreview ) ) {
  			return this;
  		}
  
  		this.dialogs.history = new panels.dialog.history();
  		this.dialogs.history.builder = this;
  		this.dialogs.history.entries.builder = this.model;
  
  		// Set the revert entry
  		this.dialogs.history.setRevertEntry( this.model );
  
  		// Display the live editor button in the toolbar
  		this.$( '.so-builder-toolbar .so-history' ).show();
  	},
  
  	/**
  	 * Add an entry.
  	 *
  	 * @param text
  	 * @param data
  	 */
  	addHistoryEntry: function ( text, data ) {
  		if ( _.isUndefined( data ) ) {
  			data = null;
  		}
  
  		if ( ! _.isUndefined( this.dialogs.history ) ) {
  			this.dialogs.history.entries.addEntry( text, data );
  		}
  	},
  
  	supports: function( thing ){
  
  		if( thing === 'rowAction' ) {
  			// Check if this supports any row action
  			return this.supports( 'addRow' ) || this.supports( 'editRow' ) || this.supports( 'deleteRow' );
  		} else if ( thing === 'widgetAction' ) {
  			// Check if this supports any widget action
  			return this.supports( 'addWidget' ) || this.supports( 'editWidget' ) || this.supports( 'deleteWidget' );
  		}
  
  		return _.isUndefined( this.config.builderSupports[ thing ] ) ? false : this.config.builderSupports[ thing ];
  	},
  
  	/**
  	 * Handle a change of the content
  	 */
  	handleContentChange: function () {
  
  		// Make sure we actually need to copy content.
  		if ( panelsOptions.copy_content && this.attachedToEditor && this.$el.is( ':visible' ) ) {
  
  			var panelsData = this.model.getPanelsData();
  			if( ! _.isEmpty( panelsData.widgets ) ) {
  				// We're going to create a copy of page builder content into the post content
  				$.post(
  					panelsOptions.ajaxurl,
  					{
  						action: 'so_panels_builder_content',
  						panels_data: JSON.stringify( panelsData ),
  						post_id: this.config.postId
  					},
  					function ( content ) {
  						if( content !== '' ) {
  							this.updateEditorContent( content );
  						}
  					}.bind( this )
  				);
  			}
  		}
  	},
  
  	/**
  	 * Update editor content with the given content.
  	 *
  	 * @param content
  	 */
  	updateEditorContent: function ( content ) {
  		// Switch back to the standard editor
  		if ( this.config.editorType !== 'tinyMCE' || typeof tinyMCE === 'undefined' || _.isNull( tinyMCE.get( "content" ) ) ) {
  			var $editor = $( this.config.editorId );
  			$editor.val( content ).trigger( 'change' ).trigger( 'keyup' );
  		} else {
  			var contentEd = tinyMCE.get( "content" );
  
  			contentEd.setContent( content );
  
  			contentEd.fire( 'change' );
  			contentEd.fire( 'keyup' );
  		}
  
  		this.triggerYoastSeoChange();
  	},
  
  	/**
  	 * Trigger a change on Yoast SEO
  	 */
  	triggerYoastSeoChange: function () {
  		if ( $( '#yoast_wpseo_focuskw_text_input' ).length ) {
  			var element = document.getElementById( 'yoast_wpseo_focuskw_text_input' ), event;
  
  			if ( document.createEvent ) {
  				event = document.createEvent( "HTMLEvents" );
  				event.initEvent( "keyup", true, true );
  			} else {
  				event = document.createEventObject();
  				event.eventType = "keyup";
  			}
  
  			event.eventName = "keyup";
  
  			if ( document.createEvent ) {
  				element.dispatchEvent( event );
  			} else {
  				element.fireEvent( "on" + event.eventType, event );
  			}
  		}
  	},
  
  	/**
  	 * Handle displaying the builder
  	 */
  	handleDisplayBuilder: function () {
  		var editor = typeof tinyMCE !== 'undefined' ? tinyMCE.get( 'content' ) : false;
  		var editorContent = ( editor && _.isFunction( editor.getContent ) ) ? editor.getContent() : $( 'textarea#content' ).val();
  
  		if (
  			(
  				_.isEmpty( this.model.get( 'data' ) ) ||
  				( _.isEmpty( this.model.get( 'data' ).widgets ) && _.isEmpty( this.model.get( 'data' ).grids ) )
  			) &&
  			editorContent !== ''
  		) {
  			var editorClass = panelsOptions.text_widget;
  			// There is a small chance a theme will have removed this, so check
  			if ( _.isEmpty( editorClass ) ) {
  				return;
  			}
  
  			// Create the existing page content in a single widget
  			this.model.loadPanelsData( this.model.getPanelsDataFromHtml( editorContent, editorClass ) );
  			this.model.trigger( 'change' );
  			this.model.trigger( 'change:data' );
  		}
  
  		$('#post-status-info').addClass( 'for-siteorigin-panels' );
  	},
  
  	handleHideBuilder: function(){
  		$('#post-status-info').show().removeClass( 'for-siteorigin-panels' );
  	},
  
      wrapEditorExpandAdjust: function( ){
  		try {
  			var events = ( $.hasData( window ) && $._data( window ) ).events.scroll,
  				event;
  
  			for( var i = 0; i < events.length; i++ ) {
  				if( events[i].namespace === 'editor-expand' ) {
                      event = events[i];
  
                      // Wrap the call
  					$( window ).unbind( 'scroll', event.handler );
  					$( window ).bind( 'scroll', function( e ){
  						if( ! this.attachedVisible ) {
                              event.handler( e );
  						}
  					}.bind( this ) );
  
  					break;
  				}
  			}
  		}
  		catch( e ){
  			// We tried, we failed
  			return;
  		}
  	},
  
  	/**
  	 * Either add or remove the narrow class
  	 * @returns {exports}
  	 */
  	handleBuilderSizing: function () {
  		var width = this.$el.width();
  
  		if ( ! width ) {
  			return this;
  		}
  
  		if ( width < 480 ) {
  			this.$el.addClass( 'so-display-narrow' );
  		} else {
  			this.$el.removeClass( 'so-display-narrow' );
  		}
  
  		return this;
  	},
  
  	/**
  	 * Set the parent dialog for all the dialogs in this builder.
  	 *
  	 * @param text
  	 * @param dialog
  	 */
  	setDialogParents: function ( text, dialog ) {
  		_.each( this.dialogs, function ( p, i, d ) {
  			d[i].setParent( text, dialog );
  		} );
  
  		// For any future dialogs
  		this.on( 'add_dialog', function ( newDialog ) {
  			newDialog.setParent( text, dialog );
  		}, this );
  	},
  
  	/**
  	 * This shows or hides the welcome display depending on whether there are any rows in the collection.
  	 */
  	toggleWelcomeDisplay: function () {
  		if ( ! this.model.get('rows').isEmpty() ) {
  			this.$( '.so-panels-welcome-message' ).hide();
  		} else {
  			this.$( '.so-panels-welcome-message' ).show();
  		}
  	},
  
  	/**
  	 * Activate the contextual menu
  	 * @param e
  	 * @param menu
  	 */
  	activateContextMenu: function ( e, menu ) {
  		var builder = this;
  
  		// Of all the visible builders, find the topmost
  		var topmostBuilder = $( '.siteorigin-panels-builder:visible' )
  			.sort( function ( a, b ) {
  				return $( a ).zIndex() > $( b ).zIndex() ? 1 : - 1;
  			} )
  			.last();
  
  		var topmostDialog = $( '.so-panels-dialog-wrapper:visible' )
  			.sort( function ( a, b ) {
  				return $( a ).zIndex() > $( b ).zIndex() ? 1 : - 1;
  			} )
  			.last();
  
  		var closestDialog = builder.$el.closest('.so-panels-dialog-wrapper');
  
  		// Only run this if its element is the topmost builder, in the topmost dialog
  		if (
  			builder.$el.is( topmostBuilder ) &&
  			(
  				topmostDialog.length === 0 ||
  				topmostDialog.is( closestDialog )
  			)
  		) {
  			// Get the element we're currently hovering over
  			var over = $( [] )
  				.add( builder.$( '.so-panels-welcome-message:visible' ) )
  				.add( builder.$( '.so-rows-container > .so-row-container' ) )
  				.add( builder.$( '.so-cells > .cell' ) )
  				.add( builder.$( '.cell-wrapper > .so-widget' ) )
  				.filter( function ( i ) {
  					return menu.isOverEl( $( this ), e );
  				} );
  
  			var activeView = over.last().data( 'view' );
  			if ( activeView !== undefined && activeView.buildContextualMenu !== undefined ) {
  				// We'll pass this to the current active view so it can popular the contextual menu
  				activeView.buildContextualMenu( e, menu );
  			}
  			else if( over.last().hasClass( 'so-panels-welcome-message' ) ) {
  				// The user opened the contextual menu on the welcome message
  				this.buildContextualMenu( e, menu );
  			}
  		}
  	},
  
  	/**
  	 * Build the contextual menu for the main builder - before any content has been added.
  	 */
  	buildContextualMenu: function( e, menu ){
  		var actions = {};
  
  		if( this.supports( 'addRow' ) ) {
  			actions.add_row = { title: panelsOptions.loc.contextual.add_row };
  		}
  
  		if ( panels.helpers.clipboard.canCopyPaste() ) {
  			if( panels.helpers.clipboard.isModel( 'row-model' ) && this.supports( 'addRow' ) ) {
  				actions.paste_row = { title: panelsOptions.loc.contextual.row_paste };
  			}
  		}
  
  		if( ! _.isEmpty( actions ) ) {
  			menu.addSection(
  				'builder-actions',
  				{
  					sectionTitle: panelsOptions.loc.contextual.row_actions,
  					search: false,
  				},
  				actions,
  				function ( c ) {
  					switch ( c ) {
  						case 'add_row':
  							this.displayAddRowDialog();
  							break;
  
  						case 'paste_row':
  							this.pasteRowHandler();
  							break;
  					}
  				}.bind( this )
  			);
  		}
  	},
  
  	loadTutorials: function( event ){
  		if( ! _.isUndefined( event ) ) {
  			event.preventDefault();
  		}
  
  		var $dd = this.$('.so-learn-wrapper .so-tool-button-dropdown');
  		$dd.addClass( 'so-loading' ).find( '.view-message' ).hide();
  
  		var loadResponse = function( response ){
  			if( ! _.isUndefined( response.error ) ) {
  				$dd.find( '.view-message' ).show().find('p').html(response.error);
  			}
  			else if( response.length ) {
  				panelsOptions.cache.tutorials = response;
  
  				for( var i in response.slice( 0,4 ) ) {
  					$dd.find( '.view-tutorials ul' ).append(
  						$('<li></li>')
  							.append(
  								$('<a target="_blank"></a>')
  									.text( response[i].title )
  									.attr( 'href', response[i].url )
  							)
  							.append(
  								$('<small></small>').text( response[i].excerpt )
  							)
  					);
  				}
  
  				$dd.find('.view-tutorials').show();
  			}
  			$dd.removeClass( 'so-loading' );
  		};
  
  		if( typeof panelsOptions.cache.tutorials === 'undefined' ) {
  			$.get(
  				panelsOptions.ajaxurl,
  				{ action: 'so_panels_get_tutorials' },
  				loadResponse
  			);
  		}
  		else {
  			loadResponse( panelsOptions.cache.tutorials );
  		}
  	},
  } );
  
  },{}],24:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = Backbone.View.extend( {
  	template: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-builder-cell' ).html() ) ),
  	events: {
  		'click .cell-wrapper': 'handleCellClick'
  	},
  
  	/* The row view that this cell is a part of */
  	row: null,
  	widgetSortable: null,
  
  	initialize: function () {
  		this.model.get('widgets').on( 'add', this.onAddWidget, this );
  	},
  
  	/**
  	 * Render the actual cell
  	 */
  	render: function () {
  		var templateArgs = {
  			weight: this.model.get( 'weight' ),
  			totalWeight: this.row.model.get('cells').totalWeight()
  		};
  
  		this.setElement( this.template( templateArgs ) );
  		this.$el.data( 'view', this );
  
  		// Now lets render any widgets that are currently in the row
  		var thisView = this;
  		this.model.get('widgets').each( function ( widget ) {
  			var widgetView = new panels.view.widget( {model: widget} );
  			widgetView.cell = thisView;
  			widgetView.render();
  
  			widgetView.$el.appendTo( thisView.$( '.widgets-container' ) );
  		} );
  
  		this.initSortable();
  		this.initResizable();
  
  		return this;
  	},
  
  	/**
  	 * Initialize the widget sortable
  	 */
  	initSortable: function () {
  		if( ! this.row.builder.supports( 'moveWidget' ) ) {
  			return this;
  		}
  
  		var cellView = this;
  
  		// Go up the view hierarchy until we find the ID attribute
  		var builderID = cellView.row.builder.$el.attr( 'id' );
  
  		// Create a widget sortable that's connected with all other cells
  		this.widgetSortable = this.$( '.widgets-container' ).sortable( {
  			placeholder: "so-widget-sortable-highlight",
  			connectWith: '#' + builderID + ' .so-cells .cell .widgets-container',
  			tolerance: 'pointer',
  			scroll: false,
  			over: function ( e, ui ) {
  				// This will make all the rows in the current builder resize
  				cellView.row.builder.trigger( 'widget_sortable_move' );
  			},
  			stop: function ( e, ui ) {
  				cellView.row.builder.addHistoryEntry( 'widget_moved' );
  
  				var $$ =  $( ui.item ),
  					widget = $$.data( 'view' ),
  					targetCell = $$.closest( '.cell' ).data( 'view' );
  
  				// Move the model and the view to the new cell
  				widget.model.moveToCell( targetCell.model, {}, $$.index() );
  				widget.cell = targetCell;
  
  				widget.cell.row.builder.model.refreshPanelsData();
  			},
  			helper: function ( e, el ) {
  				var helper = el.clone()
  					.css( {
  						'width': el.outerWidth(),
  						'z-index': 10000,
  						'position': 'fixed'
  					} )
  					.addClass( 'widget-being-dragged' ).appendTo( 'body' );
  
  				// Center the helper to the mouse cursor.
  				if ( el.outerWidth() > 720 ) {
  					helper.animate( {
  						'margin-left': e.pageX - el.offset().left - (
  						480 / 2
  						),
  						'width': 480
  					}, 'fast' );
  				}
  
  				return helper;
  			}
  		} );
  
  		return this;
  	},
  
  	/**
  	 * Refresh the widget sortable when a new widget is added
  	 */
  	refreshSortable: function () {
  		if ( ! _.isNull( this.widgetSortable ) ) {
  			this.widgetSortable.sortable( 'refresh' );
  		}
  	},
  
  	/**
  	 * This will make the cell resizble
  	 */
  	initResizable: function () {
  		if( ! this.row.builder.supports( 'editRow' ) ) {
  			return this;
  		}
  
  		// var neighbor = this.$el.previous().data('view');
  		var handle = this.$( '.resize-handle' ).css( 'position', 'absolute' );
  		var container = this.row.$el;
  		var cellView = this;
  
  		// The view of the cell to the left is stored when dragging starts.
  		var previousCell;
  
  		handle.draggable( {
  			axis: 'x',
  			containment: container,
  			start: function ( e, ui ) {
  				// Set the containment to the cell parent
  				previousCell = cellView.$el.prev().data( 'view' );
  				if ( _.isUndefined( previousCell ) ) {
  					return;
  				}
  
  				// Create the clone for the current cell
  				var newCellClone = cellView.$el.clone().appendTo( ui.helper ).css( {
  					position: 'absolute',
  					top: '0',
  					width: cellView.$el.outerWidth(),
  					left: 5,
  					height: cellView.$el.outerHeight()
  				} );
  				newCellClone.find( '.resize-handle' ).remove();
  
  				// Create the clone for the previous cell
  				var prevCellClone = previousCell.$el.clone().appendTo( ui.helper ).css( {
  					position: 'absolute',
  					top: '0',
  					width: previousCell.$el.outerWidth(),
  					right: 5,
  					height: previousCell.$el.outerHeight()
  				} );
  				prevCellClone.find( '.resize-handle' ).remove();
  
  				$( this ).data( {
  					'newCellClone': newCellClone,
  					'prevCellClone': prevCellClone
  				} );
  			},
  			drag: function ( e, ui ) {
  				// Calculate the new cell and previous cell widths as a percent
  				var containerWidth = cellView.row.$el.width() + 10;
  				var ncw = cellView.model.get( 'weight' ) - (
  					(
  					ui.position.left + handle.outerWidth() / 2
  					) / containerWidth
  					);
  				var pcw = previousCell.model.get( 'weight' ) + (
  					(
  					ui.position.left + handle.outerWidth() / 2
  					) / containerWidth
  					);
  
  				$( this ).data( 'newCellClone' ).css( 'width', containerWidth * ncw )
  					.find( '.preview-cell-weight' ).html( Math.round( ncw * 1000 ) / 10 );
  
  				$( this ).data( 'prevCellClone' ).css( 'width', containerWidth * pcw )
  					.find( '.preview-cell-weight' ).html( Math.round( pcw * 1000 ) / 10 );
  			},
  			stop: function ( e, ui ) {
  				// Remove the clones
  				$( this ).data( 'newCellClone' ).remove();
  				$( this ).data( 'prevCellClone' ).remove();
  
  				var containerWidth = cellView.row.$el.width() + 10;
  				var ncw = cellView.model.get( 'weight' ) - (
  					(
  					ui.position.left + handle.outerWidth() / 2
  					) / containerWidth
  					);
  				var pcw = previousCell.model.get( 'weight' ) + (
  					(
  					ui.position.left + handle.outerWidth() / 2
  					) / containerWidth
  					);
  
  				if ( ncw > 0.02 && pcw > 0.02 ) {
  					cellView.row.builder.addHistoryEntry( 'cell_resized' );
  					cellView.model.set( 'weight', ncw );
  					previousCell.model.set( 'weight', pcw );
  					cellView.row.resize();
  				}
  
  				ui.helper.css( 'left', - handle.outerWidth() / 2 );
  
  				// Refresh the panels data
  				cellView.row.builder.model.refreshPanelsData();
  			}
  		} );
  
  		return this;
  	},
  
  	/**
  	 * This is triggered when ever a widget is added to the row collection.
  	 *
  	 * @param widget
  	 */
  	onAddWidget: function ( widget, collection, options ) {
  		options = _.extend( {noAnimate: false}, options );
  
  		// Create the view for the widget
  		var view = new panels.view.widget( {
  			model: widget
  		} );
  		view.cell = this;
  
  		if ( _.isUndefined( widget.isDuplicate ) ) {
  			widget.isDuplicate = false;
  		}
  
  		// Render and load the form if this is a duplicate
  		view.render( {
  			'loadForm': widget.isDuplicate
  		} );
  
  		if ( _.isUndefined( options.at ) || collection.length <= 1 ) {
  			// Insert this at the end of the widgets container
  			view.$el.appendTo( this.$( '.widgets-container' ) );
  		} else {
  			// We need to insert this at a specific position
  			view.$el.insertAfter(
  				this.$( '.widgets-container .so-widget' ).eq( options.at - 1 )
  			);
  		}
  
  		if ( options.noAnimate === false ) {
  			// We need an animation
  			view.visualCreate();
  		}
  
  		this.refreshSortable();
  		this.row.resize();
  	},
  
  	/**
  	 * Handle this cell being clicked on
  	 *
  	 * @param e
  	 * @returns {boolean}
  	 */
  	handleCellClick: function ( e ) {
  		// Remove all existing selected cell indication for this builder
  		this.row.builder.$el.find( '.so-cells .cell' ).removeClass( 'cell-selected' );
  
  		if( this.row.builder.activeCell === this && ! this.model.get('widgets').length ) {
  			// This is a click on an empty cell
  			this.row.builder.activeCell = null;
  		}
  		else {
  			this.$el.addClass( 'cell-selected' );
  			this.row.builder.activeCell = this;
  		}
  	},
  
  	/**
  	 * Insert a widget from the clipboard
  	 */
  	pasteHandler: function(){
  		var pastedModel = panels.helpers.clipboard.getModel( 'widget-model' );
  		if( ! _.isEmpty( pastedModel ) && pastedModel instanceof panels.model.widget ) {
  			this.row.builder.addHistoryEntry( 'widget_pasted' );
  			pastedModel.cell = this.model;
  			this.model.get('widgets').add( pastedModel );
  			this.row.builder.model.refreshPanelsData();
  		}
  	},
  
  	/**
  	 * Build up the contextual menu for a cell
  	 *
  	 * @param e
  	 * @param menu
  	 */
  	buildContextualMenu: function ( e, menu ) {
  		var thisView = this;
  
  		if( ! menu.hasSection( 'add-widget-below' ) ) {
  			menu.addSection(
  				'add-widget-cell',
  				{
  					sectionTitle: panelsOptions.loc.contextual.add_widget_cell,
  					searchPlaceholder: panelsOptions.loc.contextual.search_widgets,
  					defaultDisplay: panelsOptions.contextual.default_widgets
  				},
  				panelsOptions.widgets,
  				function ( c ) {
  					thisView.row.builder.addHistoryEntry( 'widget_added' );
  
  					var widget = new panels.model.widget( {
  						class: c
  					} );
  
  					// Add the widget to the cell model
  					widget.cell = thisView.model;
  					widget.cell.get('widgets').add( widget );
  
  					thisView.row.builder.model.refreshPanelsData();
  				}
  			);
  		}
  
  		var actions = {};
  		if ( this.row.builder.supports('addWidget') && panels.helpers.clipboard.isModel( 'widget-model' ) ) {
  			actions.paste = {title: panelsOptions.loc.contextual.cell_paste_widget};
  		}
  
  		if( ! _.isEmpty( actions ) ) {
  			menu.addSection(
  				'cell-actions',
  				{
  					sectionTitle: panelsOptions.loc.contextual.cell_actions,
  					search: false,
  				},
  				actions,
  				function ( c ) {
  					switch ( c ) {
  						case 'paste':
  							this.pasteHandler();
  							break;
  					}
  
  					this.row.builder.model.refreshPanelsData();
  				}.bind( this )
  			);
  		}
  
  		// Add the contextual menu for the parent row
  		this.row.buildContextualMenu( e, menu );
  	}
  } );
  
  },{}],25:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = Backbone.View.extend( {
  	dialogTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-dialog' ).html() ) ),
  	dialogTabTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-dialog-tab' ).html() ) ),
  
  	tabbed: false,
  	rendered: false,
  	builder: false,
  	className: 'so-panels-dialog-wrapper',
  	dialogClass: '',
  	dialogIcon: '',
  	parentDialog: false,
  	dialogOpen: false,
  	editableLabel: false,
  
  	events: {
  		'click .so-close': 'closeDialog',
  		'click .so-nav.so-previous': 'navToPrevious',
  		'click .so-nav.so-next': 'navToNext',
  	},
  
  	initialize: function () {
  		// The first time this dialog is opened, render it
  		this.once( 'open_dialog', this.render );
  		this.once( 'open_dialog', this.attach );
  		this.once( 'open_dialog', this.setDialogClass );
  
  		this.trigger( 'initialize_dialog', this );
  
  		if ( ! _.isUndefined( this.initializeDialog ) ) {
  			this.initializeDialog();
  		}
  	},
  
  	/**
  	 * Returns the next dialog in the sequence. Should be overwritten by a child dialog.
  	 * @returns {null}
  	 */
  	getNextDialog: function () {
  		return null;
  	},
  
  	/**
  	 * Returns the previous dialog in this sequence. Should be overwritten by child dialog.
  	 * @returns {null}
  	 */
  	getPrevDialog: function () {
  		return null;
  	},
  
  	/**
  	 * Adds a dialog class to uniquely identify this dialog type
  	 */
  	setDialogClass: function () {
  		if ( this.dialogClass !== '' ) {
  			this.$( '.so-panels-dialog' ).addClass( this.dialogClass );
  		}
  	},
  
  	/**
  	 * Set the builder that controls this dialog.
  	 * @param {panels.view.builder} builder
  	 */
  	setBuilder: function ( builder ) {
  		this.builder = builder;
  
  		// Trigger an add dialog event on the builder so it can modify the dialog in any way
  		builder.trigger( 'add_dialog', this, this.builder );
  
  		return this;
  	},
  
  	/**
  	 * Attach the dialog to the window
  	 */
  	attach: function () {
  		this.$el.appendTo( 'body' );
  
  		return this;
  	},
  
  	/**
  	 * Converts an HTML representation of the dialog into arguments for a dialog box
  	 * @param html HTML for the dialog
  	 * @param args Arguments passed to the template
  	 * @returns {}
  	 */
  	parseDialogContent: function ( html, args ) {
  		// Add a CID
  		args = _.extend( {cid: this.cid}, args );
  
  
  		var c = $( (
  			_.template( panels.helpers.utils.processTemplate( html ) )
  		)( args ) );
  		var r = {
  			title: c.find( '.title' ).html(),
  			buttons: c.find( '.buttons' ).html(),
  			content: c.find( '.content' ).html()
  		};
  
  		if ( c.has( '.left-sidebar' ) ) {
  			r.left_sidebar = c.find( '.left-sidebar' ).html();
  		}
  
  		if ( c.has( '.right-sidebar' ) ) {
  			r.right_sidebar = c.find( '.right-sidebar' ).html();
  		}
  
  		return r;
  
  	},
  
  	/**
  	 * Render the dialog and initialize the tabs
  	 *
  	 * @param attributes
  	 * @returns {panels.view.dialog}
  	 */
  	renderDialog: function ( attributes ) {
  		attributes = _.extend( {
  			editableLabel: this.editableLabel,
  			dialogIcon: this.dialogIcon,
  		}, attributes );
  
  		this.$el.html( this.dialogTemplate( attributes ) ).hide();
  		this.$el.data( 'view', this );
  		this.$el.addClass( 'so-panels-dialog-wrapper' );
  
  		if ( this.parentDialog !== false ) {
  			// Add a link to the parent dialog as a sort of crumbtrail.
  			var thisDialog = this;
  			var dialogParent = $( '<h3 class="so-parent-link"></h3>' ).html( this.parentDialog.text + '<div class="so-separator"></div>' );
  			dialogParent.click( function ( e ) {
  				e.preventDefault();
  				thisDialog.closeDialog();
  				thisDialog.parentDialog.openDialog();
  			} );
  			this.$( '.so-title-bar' ).prepend( dialogParent );
  		}
  
  		if( this.$( '.so-title-bar .so-title-editable' ).length ) {
  			// Added here because .so-edit-title is only available after the template has been rendered.
  			this.initEditableLabel();
  		}
  
  		return this;
  	},
  
  	/**
  	 * Initialize the sidebar tabs
  	 */
  	initTabs: function () {
  		var tabs = this.$( '.so-sidebar-tabs li a' );
  
  		if ( tabs.length === 0 ) {
  			return this;
  		}
  
  		var thisDialog = this;
  		tabs.click( function ( e ) {
  			e.preventDefault();
  			var $$ = $( this );
  
  			thisDialog.$( '.so-sidebar-tabs li' ).removeClass( 'tab-active' );
  			thisDialog.$( '.so-content .so-content-tabs > *' ).hide();
  
  			$$.parent().addClass( 'tab-active' );
  
  			var url = $$.attr( 'href' );
  			if ( ! _.isUndefined( url ) && url.charAt( 0 ) === '#' ) {
  				// Display the new tab
  				var tabName = url.split( '#' )[1];
  				thisDialog.$( '.so-content .so-content-tabs .tab-' + tabName ).show();
  			}
  
  			// This lets other dialogs implement their own custom handlers
  			thisDialog.trigger( 'tab_click', $$ );
  
  		} );
  
  		// Trigger a click on the first tab
  		this.$( '.so-sidebar-tabs li a' ).first().click();
  		return this;
  	},
  
  	initToolbar: function () {
  		// Trigger simplified click event for elements marked as toolbar buttons.
  		var buttons = this.$( '.so-toolbar .so-buttons .so-toolbar-button' );
  		buttons.click( function ( e ) {
  			e.preventDefault();
  
  			this.trigger( 'button_click', $( e.currentTarget ) );
  		}.bind( this ) );
  
  		// Handle showing and hiding the dropdown list items
  		var $dropdowns = this.$( '.so-toolbar .so-buttons .so-dropdown-button' );
  		$dropdowns.click( function ( e ) {
  			e.preventDefault();
  			var $dropdownButton = $( e.currentTarget );
  			var $dropdownList = $dropdownButton.siblings( '.so-dropdown-links-wrapper' );
  			if ( $dropdownList.is( '.hidden' ) ) {
  				$dropdownList.removeClass( 'hidden' );
  			} else {
  				$dropdownList.addClass( 'hidden' );
  			}
  
  		}.bind( this ) );
  
  		// Hide dropdown list on click anywhere, unless it's a dropdown option which requires confirmation in it's
  		// unconfirmed state.
  		$( 'html' ).click( function ( e ) {
  			this.$( '.so-dropdown-links-wrapper' ).not( '.hidden' ).each( function ( index, el ) {
  				var $dropdownList = $( el );
  				var $trgt = $( e.target );
  				if ( $trgt.length === 0 || !(
  						(
  							$trgt.is('.so-needs-confirm') && !$trgt.is('.so-confirmed')
  						) || $trgt.is('.so-dropdown-button')
  					) ) {
  					$dropdownList.addClass('hidden');
  				}
  			} );
  		}.bind( this ) );
  	},
  
  	/**
  	 * Initialize the editable dialog title
  	 */
  	initEditableLabel: function(){
  		var $editElt = this.$( '.so-title-bar .so-title-editable' );
  
  		$editElt.keypress( function ( event ) {
  			var enterPressed = event.type === 'keypress' && event.keyCode === 13;
  			if ( enterPressed ) {
  				// Need to make sure tab focus is on another element, otherwise pressing enter multiple times refocuses
  				// the element and allows newlines.
  				var tabbables = $( ':tabbable' );
  				var curTabIndex = tabbables.index( $editElt );
  				tabbables.eq( curTabIndex + 1 ).focus();
  				// After the above, we're somehow left with the first letter of text selected,
  				// so this removes the selection.
  				window.getSelection().removeAllRanges();
  			}
  			return !enterPressed;
  		} ).blur( function () {
  			var newValue = $editElt.text().replace( /^\s+|\s+$/gm, '' );
  			var oldValue = $editElt.data( 'original-value' ).replace( /^\s+|\s+$/gm, '' );
  			if ( newValue !== oldValue ) {
  				$editElt.text( newValue );
  				this.trigger( 'edit_label', newValue );
  			}
  
  		}.bind( this ) );
  
  		$editElt.focus( function() {
  			$editElt.data( 'original-value', $editElt.text() );
  			panels.helpers.utils.selectElementContents( this );
  		} );
  	},
  
  	/**
  	 * Quickly setup the dialog by opening and closing it.
  	 */
  	setupDialog: function () {
  		this.openDialog();
  		this.closeDialog();
  	},
  
  	/**
  	 * Refresh the next and previous buttons.
  	 */
  	refreshDialogNav: function () {
  		this.$( '.so-title-bar .so-nav' ).show().removeClass( 'so-disabled' );
  
  		// Lets also hide the next and previous if we don't have a next and previous dialog
  		var nextDialog = this.getNextDialog();
  		var nextButton = this.$( '.so-title-bar .so-next' );
  
  		var prevDialog = this.getPrevDialog();
  		var prevButton = this.$( '.so-title-bar .so-previous' );
  
  		if ( nextDialog === null ) {
  			nextButton.hide();
  		}
  		else if ( nextDialog === false ) {
  			nextButton.addClass( 'so-disabled' );
  		}
  
  		if ( prevDialog === null ) {
  			prevButton.hide();
  		}
  		else if ( prevDialog === false ) {
  			prevButton.addClass( 'so-disabled' );
  		}
  	},
  
  	/**
  	 * Open the dialog
  	 */
  	openDialog: function ( options ) {
  		options = _.extend( {
  			silent: false
  		}, options );
  
  		if ( ! options.silent ) {
  			this.trigger( 'open_dialog' );
  		}
  
  		this.dialogOpen = true;
  
  		this.refreshDialogNav();
  
  		// Stop scrolling for the main body
  		panels.helpers.pageScroll.lock();
  
  		// Start listen for keyboard keypresses.
  		$( window ).on( 'keyup', this.keyboardListen );
  
  		this.$el.show();
  
  		if ( ! options.silent ) {
  			// This triggers once everything is visible
  			this.trigger( 'open_dialog_complete' );
  			this.builder.trigger( 'open_dialog', this );
  		}
  	},
  
  	/**
  	 * Close the dialog
  	 *
  	 * @param e
  	 * @returns {boolean}
  	 */
  	closeDialog: function ( options ) {
  		options = _.extend( {
  			silent: false
  		}, options );
  
  		if ( ! options.silent ) {
  			this.trigger( 'close_dialog' );
  		}
  
  		this.dialogOpen = false;
  
  		this.$el.hide();
  		panels.helpers.pageScroll.unlock();
  
  		// Stop listen for keyboard keypresses.
  		$( window ).off( 'keyup', this.keyboardListen );
  
  		if ( ! options.silent ) {
  			// This triggers once everything is hidden
  			this.trigger( 'close_dialog_complete' );
  			this.builder.trigger( 'close_dialog', this );
  		}
  	},
  
  	/**
  	 * Keyboard events handler
  	 */
  	keyboardListen: function ( e ) {
  		// [Esc] to close
  		if ( e.which === 27 ) {
  			$( '.so-panels-dialog-wrapper .so-close' ).trigger( 'click' );
  		}
  	},
  
  	/**
  	 * Navigate to the previous dialog
  	 */
  	navToPrevious: function () {
  		this.closeDialog();
  
  		var prev = this.getPrevDialog();
  		if ( prev !== null && prev !== false ) {
  			prev.openDialog();
  		}
  	},
  
  	/**
  	 * Navigate to the next dialog
  	 */
  	navToNext: function () {
  		this.closeDialog();
  
  		var next = this.getNextDialog();
  		if ( next !== null && next !== false ) {
  			next.openDialog();
  		}
  	},
  
  	/**
  	 * Get the values from the form and convert them into a data array
  	 */
  	getFormValues: function ( formSelector ) {
  		if ( _.isUndefined( formSelector ) ) {
  			formSelector = '.so-content';
  		}
  
  		var $f = this.$( formSelector );
  
  		var data = {}, parts;
  
  		// Find all the named fields in the form
  		$f.find( '[name]' ).each( function () {
  			var $$ = $( this );
  
  			try {
  
  				var name = /([A-Za-z_]+)\[(.*)\]/.exec( $$.attr( 'name' ) );
  				if ( _.isEmpty( name ) ) {
  					return true;
  				}
  
  				// Create an array with the parts of the name
  				if ( _.isUndefined( name[2] ) ) {
  					parts = $$.attr( 'name' );
  				} else {
  					parts = name[2].split( '][' );
  					parts.unshift( name[1] );
  				}
  
  				parts = parts.map( function ( e ) {
  					if ( ! isNaN( parseFloat( e ) ) && isFinite( e ) ) {
  						return parseInt( e );
  					} else {
  						return e;
  					}
  				} );
  
  				var sub = data;
  				var fieldValue = null;
  
  				var fieldType = (
  					_.isString( $$.attr( 'type' ) ) ? $$.attr( 'type' ).toLowerCase() : false
  				);
  
  				// First we need to get the value from the field
  				if ( fieldType === 'checkbox' ) {
  					if ( $$.is( ':checked' ) ) {
  						fieldValue = $$.val() !== '' ? $$.val() : true;
  					} else {
  						fieldValue = null;
  					}
  				}
  				else if ( fieldType === 'radio' ) {
  					if ( $$.is( ':checked' ) ) {
  						fieldValue = $$.val();
  					} else {
  						//skip over unchecked radios
  						return;
  					}
  				}
  				else if ( $$.prop( 'tagName' ) === 'TEXTAREA' && $$.hasClass( 'wp-editor-area' ) ) {
  					// This is a TinyMCE editor, so we'll use the tinyMCE object to get the content
  					var editor = null;
  					if ( typeof tinyMCE !== 'undefined' ) {
  						editor = tinyMCE.get( $$.attr( 'id' ) );
  					}
  
  					if ( editor !== null && _.isFunction( editor.getContent ) && ! editor.isHidden() ) {
  						fieldValue = editor.getContent();
  					} else {
  						fieldValue = $$.val();
  					}
  				}
  				else if ( $$.prop( 'tagName' ) === 'SELECT' ) {
  					var selected = $$.find( 'option:selected' );
  
  					if ( selected.length === 1 ) {
  						fieldValue = $$.find( 'option:selected' ).val();
  					}
  					else if ( selected.length > 1 ) {
  						// This is a mutli-select field
  						fieldValue = _.map( $$.find( 'option:selected' ), function ( n, i ) {
  							return $( n ).val();
  						} );
  					}
  
  				} else {
  					// This is a fallback that will work for most fields
  					fieldValue = $$.val();
  				}
  
  				// Now, we need to filter this value if necessary
  				if ( ! _.isUndefined( $$.data( 'panels-filter' ) ) ) {
  					switch ( $$.data( 'panels-filter' ) ) {
  						case 'json_parse':
  							// Attempt to parse the JSON value of this field
  							try {
  								fieldValue = JSON.parse( fieldValue );
  							}
  							catch ( err ) {
  								fieldValue = '';
  							}
  							break;
  					}
  				}
  
  				// Now convert this into an array
  				if ( fieldValue !== null ) {
  					for ( var i = 0; i < parts.length; i ++ ) {
  						if ( i === parts.length - 1 ) {
  							if ( parts[i] === '' ) {
  								// This needs to be an array
  								sub.push( fieldValue );
  							} else {
  								sub[parts[i]] = fieldValue;
  							}
  						} else {
  							if ( _.isUndefined( sub[parts[i]] ) ) {
  								if ( parts[i + 1] === '' ) {
  									sub[parts[i]] = [];
  								} else {
  									sub[parts[i]] = {};
  								}
  							}
  							sub = sub[parts[i]];
  						}
  					}
  				}
  			}
  			catch ( error ) {
  				// Ignore this error, just log the message for debugging
  				console.log( 'Field [' + $$.attr('name') + '] could not be processed and was skipped - ' + error.message );
  			}
  
  		} ); // End of each through input fields
  
  		return data;
  	},
  
  	/**
  	 * Set a status message for the dialog
  	 */
  	setStatusMessage: function ( message, loading ) {
  		this.$( '.so-toolbar .so-status' ).html( message );
  		if ( ! _.isUndefined( loading ) && loading ) {
  			this.$( '.so-toolbar .so-status' ).addClass( 'so-panels-loading' );
  		}
  	},
  
  	/**
  	 * Set the parent after.
  	 */
  	setParent: function ( text, dialog ) {
  		this.parentDialog = {
  			text: text,
  			dialog: dialog
  		};
  	}
  } );
  
  },{}],26:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = Backbone.View.extend( {
  	template: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-live-editor' ).html() ) ),
  
  	previewScrollTop: 0,
  	loadTimes: [],
  	previewFrameId: 1,
  
  	previewUrl: null,
  	previewIframe: null,
  
  	events: {
  		'click .live-editor-close': 'close',
  		'click .live-editor-collapse': 'collapse',
  		'click .live-editor-mode': 'mobileToggle'
  	},
  
  	initialize: function ( options ) {
  		options = _.extend( {
  			builder: false,
  			previewUrl: false,
  		}, options );
  
  		if( _.isEmpty( options.previewUrl ) ) {
  			options.previewUrl = panelsOptions.ajaxurl + "&action=so_panels_live_editor_preview";
  		}
  
  		this.builder = options.builder;
  		this.previewUrl = options.previewUrl;
  
  		this.builder.model.on( 'refresh_panels_data', this.handleRefreshData, this );
  		this.builder.model.on( 'load_panels_data', this.handleLoadData, this );
  	},
  
  	/**
  	 * Render the live editor
  	 */
  	render: function () {
  		this.setElement( this.template() );
  		this.$el.hide();
  		var thisView = this;
  
  		var isMouseDown = false;
  
  		$( document )
  			.mousedown( function () {
  				isMouseDown = true;
  			} )
  			.mouseup( function () {
  				isMouseDown = false;
  			} );
  
  		// Handle highlighting the relevant widget in the live editor preview
  		this.$el.on( 'mouseenter', '.so-widget-wrapper', function () {
  			var $$ = $( this ),
  				previewWidget = $$.data( 'live-editor-preview-widget' );
  
  			if ( ! isMouseDown && previewWidget !== undefined && previewWidget.length && ! thisView.$( '.so-preview-overlay' ).is( ':visible' ) ) {
  				thisView.highlightElement( previewWidget );
  				thisView.scrollToElement( previewWidget );
  			}
  		} );
  
  		thisView.$el.on( 'mouseleave', '.so-widget-wrapper', function () {
  			thisView.resetHighlights();
  		} );
  
  		thisView.builder.on( 'open_dialog', function () {
  			thisView.resetHighlights();
  		} );
  
  		return this;
  	},
  
  	/**
  	 * Attach the live editor to the document
  	 */
  	attach: function () {
  		this.$el.appendTo( 'body' );
  	},
  
  	/**
  	 * Display the live editor
  	 */
  	open: function () {
  		if ( this.$el.html() === '' ) {
  			this.render();
  		}
  		if ( this.$el.closest( 'body' ).length === 0 ) {
  			this.attach();
  		}
  
  		// Disable page scrolling
  		panels.helpers.pageScroll.lock();
  
  		if ( this.$el.is( ':visible' ) ) {
  			return this;
  		}
  
  		// Refresh the preview display
  		this.$el.show();
  		this.refreshPreview( this.builder.model.getPanelsData() );
  
  		// Move the builder view into the Live Editor
  		this.originalContainer = this.builder.$el.parent();
  		this.builder.$el.appendTo( this.$( '.so-live-editor-builder' ) );
  		this.builder.$( '.so-tool-button.so-live-editor' ).hide();
  		this.builder.trigger( 'builder_resize' );
  
  
  		if( $('#original_post_status' ).val() === 'auto-draft' && ! this.autoSaved ) {
  			// The live editor requires a saved draft post, so we'll create one for auto-draft posts
  			var thisView = this;
  
  			if ( wp.autosave ) {
  				// Set a temporary post title so the autosave triggers properly
  				if( $('#title[name="post_title"]' ).val() === '' ) {
  					$('#title[name="post_title"]' ).val( panelsOptions.loc.draft ).trigger('keydown');
  				}
  
  				$( document ).one( 'heartbeat-tick.autosave', function(){
  					thisView.autoSaved = true;
  					thisView.refreshPreview( thisView.builder.model.getPanelsData() );
  				} );
  				wp.autosave.server.triggerSave();
  			}
  		}
  	},
  
  	/**
  	 * Close the live editor
  	 */
  	close: function () {
  		if ( ! this.$el.is( ':visible' ) ) {
  			return this;
  		}
  
  		this.$el.hide();
  		panels.helpers.pageScroll.unlock();
  
  		// Move the builder back to its original container
  		this.builder.$el.appendTo( this.originalContainer );
  		this.builder.$( '.so-tool-button.so-live-editor' ).show();
  		this.builder.trigger( 'builder_resize' );
  	},
  
  	/**
  	 * Collapse the live editor
  	 */
  	collapse: function () {
  		this.$el.toggleClass( 'so-collapsed' );
  
  		var text = this.$( '.live-editor-collapse span' );
  		text.html( text.data( this.$el.hasClass( 'so-collapsed' ) ? 'expand' : 'collapse' ) );
  	},
  
  	/**
  	 * Create an overlay in the preview.
  	 *
  	 * @param over
  	 * @return {*|Object} The item we're hovering over.
  	 */
  	highlightElement: function ( over ) {
  		if( ! _.isUndefined( this.resetHighlightTimeout ) ) {
  			clearTimeout( this.resetHighlightTimeout );
  		}
  
  		// Remove any old overlays
  
  		var body = this.previewIframe.contents().find( 'body' );
  		body.find( '.panel-grid .panel-grid-cell .so-panel' )
  			.filter( function () {
  				// Filter to only include non nested
  				return $( this ).parents( '.so-panel' ).length === 0;
  			} )
  			.not( over )
  			.addClass( 'so-panels-faded' );
  
  		over.removeClass( 'so-panels-faded' ).addClass( 'so-panels-highlighted' );
  	},
  
  	/**
  	 * Reset highlights in the live preview
  	 */
  	resetHighlights: function() {
  
  		var body = this.previewIframe.contents().find( 'body' );
  		this.resetHighlightTimeout = setTimeout( function(){
  			body.find( '.panel-grid .panel-grid-cell .so-panel' )
  				.removeClass( 'so-panels-faded so-panels-highlighted' );
  		}, 100 );
  	},
  
  	/**
  	 * Scroll over an element in the live preview
  	 * @param over
  	 */
  	scrollToElement: function( over ) {
  		var contentWindow = this.$( '.so-preview iframe' )[0].contentWindow;
  		contentWindow.liveEditorScrollTo( over );
  	},
  
  	handleRefreshData: function ( newData, args ) {
  		if ( ! this.$el.is( ':visible' ) ) {
  			return this;
  		}
  
  		this.refreshPreview( newData );
  	},
  
  	handleLoadData: function () {
  		if ( ! this.$el.is( ':visible' ) ) {
  			return this;
  		}
  
  		this.refreshPreview( this.builder.model.getPanelsData() );
  	},
  
  	/**
  	 * Refresh the Live Editor preview.
  	 * @returns {exports}
  	 */
  	refreshPreview: function ( data ) {
  		var loadTimePrediction = this.loadTimes.length ?
  		_.reduce( this.loadTimes, function ( memo, num ) {
  			return memo + num;
  		}, 0 ) / this.loadTimes.length : 1000;
  
  		// Store the last preview iframe position
  		if( ! _.isNull( this.previewIframe )  ) {
  			if ( ! this.$( '.so-preview-overlay' ).is( ':visible' ) ) {
  				this.previewScrollTop = this.previewIframe.contents().scrollTop();
  			}
  		}
  
  		// Add a loading bar
  		this.$( '.so-preview-overlay' ).show();
  		this.$( '.so-preview-overlay .so-loading-bar' )
  			.clearQueue()
  			.css( 'width', '0%' )
  			.animate( {width: '100%'}, parseInt( loadTimePrediction ) + 100 );
  
  
  		this.postToIframe(
  			{
  				live_editor_panels_data: JSON.stringify( data ),
  				live_editor_post_ID: this.builder.config.postId
  			},
  			this.previewUrl,
  			this.$('.so-preview')
  		);
  
  		this.previewIframe.data( 'load-start', new Date().getTime() );
  	},
  
  	/**
  	 * Use a temporary form to post data to an iframe.
  	 *
  	 * @param data The data to send
  	 * @param url The preview URL
  	 * @param target The target iframe
  	 */
  	postToIframe: function( data, url, target ){
  		// Store the old preview
  
  		if( ! _.isNull( this.previewIframe )  ) {
  			this.previewIframe.remove();
  		}
  
  		var iframeId = 'siteorigin-panels-live-preview-' + this.previewFrameId;
  
  		// Remove the old preview frame
  		this.previewIframe = $('<iframe src="javascript:false;" />')
  			.attr( {
  				'id' : iframeId,
  				'name' : iframeId,
  			} )
  			.appendTo( target )
  
  		this.setupPreviewFrame( this.previewIframe );
  
  		// We can use a normal POST form submit
  		var tempForm = $('<form id="soPostToPreviewFrame" method="post" />')
  			.attr( {
  				id: iframeId,
  				target: this.previewIframe.attr('id'),
  				action: url
  			} )
  			.appendTo( 'body' );
  
  		$.each( data, function( name, value ){
  			$('<input type="hidden" />')
  				.attr( {
  					name: name,
  					value: value
  				} )
  				.appendTo( tempForm );
  		} );
  
  		tempForm
  			.submit()
  			.remove();
  
  		this.previewFrameId++;
  
  		return this.previewIframe;
  	},
  
  	/**
  	 * Do all the basic setup for the preview Iframe element
  	 * @param iframe
  	 */
  	setupPreviewFrame: function( iframe ){
  		var thisView = this;
  		iframe
  			.data( 'iframeready', false )
  			.on( 'iframeready', function () {
  				var $$ = $( this ),
  					$iframeContents = $$.contents();
  
  				if( $$.data( 'iframeready' ) ) {
  					// Skip this if the iframeready function has already run
  					return;
  				}
  
  				$$.data( 'iframeready', true );
  
  				if ( $$.data( 'load-start' ) !== undefined ) {
  					thisView.loadTimes.unshift( new Date().getTime() - $$.data( 'load-start' ) );
  
  					if ( ! _.isEmpty( thisView.loadTimes ) ) {
  						thisView.loadTimes = thisView.loadTimes.slice( 0, 4 );
  					}
  				}
  
  				setTimeout( function(){
  					// Scroll to the correct position
  					$iframeContents.scrollTop( thisView.previewScrollTop );
  					thisView.$( '.so-preview-overlay' ).hide();
  				}, 100 );
  
  
  				// Lets find all the first level grids. This is to account for the Page Builder layout widget.
  				var layoutWrapper = $iframeContents.find( '#pl-' + thisView.builder.config.postId );
  				layoutWrapper.find( '.panel-grid .panel-grid-cell .so-panel' )
  					.filter( function () {
  						// Filter to only include non nested
  						return $( this ).closest( '.panel-layout' ).is( layoutWrapper );
  					} )
  					.each( function ( i, el ) {
  						var $$ = $( el );
  						var widgetEdit = thisView.$( '.so-live-editor-builder .so-widget-wrapper' ).eq( $$.data( 'index' ) );
  						widgetEdit.data( 'live-editor-preview-widget', $$ );
  
  						$$
  							.css( {
  								'cursor': 'pointer'
  							} )
  							.mouseenter( function () {
  								widgetEdit.parent().addClass( 'so-hovered' );
  								thisView.highlightElement( $$ );
  							} )
  							.mouseleave( function () {
  								widgetEdit.parent().removeClass( 'so-hovered' );
  								thisView.resetHighlights();
  							} )
  							.click( function ( e ) {
  								e.preventDefault();
  								// When we click a widget, send that click to the form
  								widgetEdit.find( '.title h4' ).click();
  							} );
  					} );
  
  				// Prevent default clicks inside the preview iframe
  				$iframeContents.find( "a" ).css( {'pointer-events': 'none'} ).click( function ( e ) {
  					e.preventDefault();
  				} );
  
  			} )
  			.on( 'load', function(){
  				var $$ = $( this );
  				if( ! $$.data( 'iframeready' ) ) {
  					$$.trigger('iframeready');
  				}
  			} );
  	},
  
  	/**
  	 * Return true if the live editor has a valid preview URL.
  	 * @return {boolean}
  	 */
  	hasPreviewUrl: function () {
  		return this.$( 'form.live-editor-form' ).attr( 'action' ) !== '';
  	},
  
  	/**
  	 * Toggle the size of the preview iframe to simulate mobile devices.
  	 * @param e
  	 */
  	mobileToggle: function( e ){
  		var button = $( e.currentTarget );
  		this.$('.live-editor-mode' ).not( button ).removeClass('so-active');
  		button.addClass( 'so-active' );
  
  		this.$el
  			.removeClass( 'live-editor-desktop-mode live-editor-tablet-mode live-editor-mobile-mode' )
  			.addClass( 'live-editor-' + button.data( 'mode' ) + '-mode' );
  
  	}
  } );
  
  },{}],27:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = Backbone.View.extend( {
  	template: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-builder-row' ).html() ) ),
  
  	events: {
  		'click .so-row-settings': 'editSettingsHandler',
  		'click .so-row-duplicate': 'duplicateHandler',
  		'click .so-row-delete': 'confirmedDeleteHandler',
  		'click .so-row-color': 'rowColorChangeHandler',
  	},
  
  	builder: null,
  	dialog: null,
  
  	/**
  	 * Initialize the row view
  	 */
  	initialize: function () {
  
  		var rowCells = this.model.get('cells');
  		rowCells.on( 'add', this.handleCellAdd, this );
  		rowCells.on( 'remove', this.handleCellRemove, this );
  		this.model.on( 'reweight_cells', this.resize, this );
  
  		this.model.on( 'destroy', this.onModelDestroy, this );
  		this.model.on( 'visual_destroy', this.visualDestroyModel, this );
  
  		var thisView = this;
  		rowCells.each( function ( cell ) {
  			thisView.listenTo( cell.get('widgets'), 'add', thisView.resize );
  		} );
  
  		// When ever a new cell is added, listen to it for new widgets
  		rowCells.on( 'add', function ( cell ) {
  			thisView.listenTo( cell.get('widgets'), 'add', thisView.resize );
  		}, this );
  
  		this.model.on( 'change:label', this.onLabelChange, this );
  	},
  
  	/**
  	 * Render the row.
  	 *
  	 * @returns {panels.view.row}
  	 */
  	render: function () {
  		var rowColorLabel = this.model.has( 'color_label' ) ? this.model.get( 'color_label' ) : 1;
  		var rowLabel = this.model.has( 'label' ) ? this.model.get( 'label' ) : '';
  		this.setElement( this.template( { rowColorLabel: rowColorLabel, rowLabel: rowLabel } ) );
  		this.$el.data( 'view', this );
  
  		// Create views for the cells in this row
  		var thisView = this;
  		this.model.get('cells').each( function ( cell ) {
  			var cellView = new panels.view.cell( {
  				model: cell
  			} );
  			cellView.row = thisView;
  			cellView.render();
  			cellView.$el.appendTo( thisView.$( '.so-cells' ) );
  		} );
  
  		// Remove any unsupported actions
  		if( ! this.builder.supports( 'rowAction' ) ) {
  			this.$('.so-row-toolbar .so-dropdown-wrapper' ).remove();
  			this.$el.addClass('so-row-no-actions');
  		}
  		else {
  			if( ! this.builder.supports( 'editWidget' ) ) {
  				this.$('.so-row-toolbar .so-row-settings' ).parent().remove();
  				this.$el.addClass('so-row-no-edit');
  			}
  			if( ! this.builder.supports( 'addWidget' ) ) {
  				this.$('.so-row-toolbar .so-row-duplicate' ).parent().remove();
  				this.$el.addClass('so-row-no-duplicate');
  			}
  			if( ! this.builder.supports( 'deleteWidget' ) ) {
  				this.$('.so-row-toolbar .so-row-delete' ).parent().remove();
  				this.$el.addClass('so-row-no-delete');
  			}
  		}
  		if( ! this.builder.supports( 'moveRow' ) ) {
  			this.$('.so-row-toolbar .so-row-move' ).remove();
  			this.$el.addClass('so-row-no-move');
  		}
  		if( !$.trim( this.$('.so-row-toolbar').html() ).length ) {
  			this.$('.so-row-toolbar' ).remove();
  		}
  
  		// Resize the rows when ever the widget sortable moves
  		this.builder.on( 'widget_sortable_move', this.resize, this );
  		this.builder.on( 'builder_resize', this.resize, this );
  
  		this.resize();
  
  		return this;
  	},
  
  	/**
  	 * Give a visual indication of the creation of this row
  	 */
  	visualCreate: function () {
  		this.$el.hide().fadeIn( 'fast' );
  	},
  
  	/**
  	 * Visually resize the row so that all cell heights are the same and the widths so that they balance to 100%
  	 *
  	 * @param e
  	 */
  	resize: function ( e ) {
  		// Don't resize this
  		if ( ! this.$el.is( ':visible' ) ) {
  			return;
  		}
  
  		// Reset everything to have an automatic height
  		this.$( '.so-cells .cell-wrapper' ).css( 'min-height', 0 );
  		this.$( '.so-cells .resize-handle' ).css( 'height', 0 );
  
  		// We'll tie the values to the row view, to prevent issue with values going to different rows
  		var height = 0;
  		this.$( '.so-cells .cell' ).each( function () {
  			height = Math.max(
  				height,
  				$( this ).height()
  			);
  
  			$( this ).css(
  				'width',
  				( $( this ).data( 'view' ).model.get( 'weight' ) * 100) + "%"
  			);
  		} );
  
  		// Resize all the grids and cell wrappers
  		this.$( '.so-cells .cell-wrapper' ).css( 'min-height', Math.max( height, 63 ) );
  		this.$( '.so-cells .resize-handle' ).css( 'height', this.$( '.so-cells .cell-wrapper' ).outerHeight() );
  	},
  
  	/**
  	 * Remove the view from the dom.
  	 */
  	onModelDestroy: function () {
  		this.remove();
  	},
  
  	/**
  	 * Fade out the view and destroy the model
  	 */
  	visualDestroyModel: function () {
  		this.builder.addHistoryEntry( 'row_deleted' );
  		var thisView = this;
  		this.$el.fadeOut( 'normal', function () {
  			thisView.model.destroy();
  			thisView.builder.model.refreshPanelsData();
  		} );
  	},
  
  	onLabelChange: function( model, text ) {
  		if ( this.$('.so-row-label').length == 0 ) {
  			this.$( '.so-row-toolbar' ).prepend( '<h3 class="so-row-label">' + text + '</h3>' );
  		} else {
  			this.$('.so-row-label').text( text );
  		}
  	},
  
  	/**
  	 * Duplicate this row.
  	 *
  	 * @return {boolean}
  	 */
  	duplicateHandler: function () {
  		this.builder.addHistoryEntry( 'row_duplicated' );
  
  		var duplicateRow = this.model.clone( this.builder.model );
  
  		this.builder.model.get('rows').add( duplicateRow, {
  			at: this.builder.model.get('rows').indexOf( this.model ) + 1
  		} );
  
  		this.builder.model.refreshPanelsData();
  	},
  
  	/**
  	 * Copy the row to a localStorage
  	 */
  	copyHandler: function(){
  		panels.helpers.clipboard.setModel( this.model );
  	},
  
  	/**
  	 * Create a new row and insert it
  	 */
  	pasteHandler: function(){
  		var pastedModel = panels.helpers.clipboard.getModel( 'row-model' );
  
  		if( ! _.isEmpty( pastedModel ) && pastedModel instanceof panels.model.row ) {
  			this.builder.addHistoryEntry( 'row_pasted' );
  			pastedModel.builder = this.builder.model;
  			this.builder.model.get('rows').add( pastedModel, {
  				at: this.builder.model.get('rows').indexOf( this.model ) + 1
  			} );
  			this.builder.model.refreshPanelsData();
  		}
  	},
  
  	/**
  	 * Handles deleting the row with a confirmation.
  	 */
  	confirmedDeleteHandler: function ( e ) {
  		var $$ = $( e.target );
  
  		// The user clicked on the dashicon
  		if ( $$.hasClass( 'dashicons' ) ) {
  			$$ = $.parent();
  		}
  
  		if ( $$.hasClass( 'so-confirmed' ) ) {
  			this.visualDestroyModel();
  		} else {
  			var originalText = $$.html();
  
  			$$.addClass( 'so-confirmed' ).html(
  				'<span class="dashicons dashicons-yes"></span>' + panelsOptions.loc.dropdown_confirm
  			);
  
  			setTimeout( function () {
  				$$.removeClass( 'so-confirmed' ).html( originalText );
  			}, 2500 );
  		}
  	},
  
  	/**
  	 * Handle displaying the settings dialog
  	 */
  	editSettingsHandler: function () {
  		// Lets open up an instance of the settings dialog
  		if ( this.dialog === null ) {
  			// Create the dialog
  			this.dialog = new panels.dialog.row();
  			this.dialog.setBuilder( this.builder ).setRowModel( this.model );
  		}
  
  		this.dialog.openDialog();
  
  		return this;
  	},
  
  	/**
  	 * Handle deleting this entire row.
  	 */
  	deleteHandler: function () {
  		this.model.destroy();
  		return this;
  	},
  
  	/**
  	 * Change the row background color.
  	 */
  	rowColorChangeHandler: function ( event ) {
  		this.$( '.so-row-color' ).removeClass( 'so-row-color-selected' );
  		var clickedColorElem = $( event.target );
  		var newColorLabel = clickedColorElem.data( 'color-label' );
  		var oldColorLabel = this.model.has( 'color_label' ) ? this.model.get( 'color_label' ) : 1;
  		clickedColorElem.addClass( 'so-row-color-selected' );
  		this.$el.removeClass( 'so-row-color-' + oldColorLabel );
  		this.$el.addClass( 'so-row-color-' + newColorLabel );
  		this.model.set( 'color_label', newColorLabel );
  	},
  
  	/**
  	 * Handle a new cell being added to this row view. For now we'll assume the new cell is always last
  	 */
  	handleCellAdd: function ( cell ) {
  		var cellView = new panels.view.cell( {
  			model: cell
  		} );
  		cellView.row = this;
  		cellView.render();
  		cellView.$el.appendTo( this.$( '.so-cells' ) );
  	},
  
  	/**
  	 * Handle a cell being removed from this row view
  	 */
  	handleCellRemove: function ( cell ) {
  		// Find the view that ties in to the cell we're removing
  		this.$( '.so-cells > .cell' ).each( function () {
  			var view = $( this ).data( 'view' );
  			if ( _.isUndefined( view ) ) {
  				return;
  			}
  
  			if ( view.model.cid === cell.cid ) {
  				// Remove this view
  				view.remove();
  			}
  		} );
  	},
  
  	/**
  	 * Build up the contextual menu for a row
  	 *
  	 * @param e
  	 * @param menu
  	 */
  	buildContextualMenu: function ( e, menu ) {
  		var options = [];
  		for ( var i = 1; i < 5; i ++ ) {
  			options.push( {
  				title: i + ' ' + panelsOptions.loc.contextual.column
  			} );
  		}
  
  		if( this.builder.supports( 'addRow' ) ) {
  			menu.addSection(
  				'add-row',
  				{
  					sectionTitle: panelsOptions.loc.contextual.add_row,
  					search: false
  				},
  				options,
  				function ( c ) {
  					this.builder.addHistoryEntry( 'row_added' );
  
  					var columns = Number( c ) + 1;
  					var weights = [];
  					for ( var i = 0; i < columns; i ++ ) {
  						weights.push( {weight: 100 / columns } );
  					}
  
  					// Create the actual row
  					var newRow = new panels.model.row( {
  						collection: this.collection
  					} );
  
  					var cells = new panels.collection.cells(weights);
  					cells.each(function (cell) {
  						cell.row = newRow;
  					});
  					newRow.setCells(cells);
  					newRow.builder = this.builder.model;
  
  					this.builder.model.get('rows').add( newRow, {
  						at: this.builder.model.get('rows').indexOf( this.model ) + 1
  					} );
  
  					this.builder.model.refreshPanelsData();
  				}.bind( this )
  			);
  		}
  
  		var actions = {};
  
  		if( this.builder.supports( 'editRow' ) ) {
  			actions.edit = { title: panelsOptions.loc.contextual.row_edit };
  		}
  
  		// Copy and paste functions
  		if ( panels.helpers.clipboard.canCopyPaste() ) {
  			actions.copy = { title: panelsOptions.loc.contextual.row_copy };
  			if ( this.builder.supports( 'addRow' ) && panels.helpers.clipboard.isModel( 'row-model' ) ) {
  				actions.paste = { title: panelsOptions.loc.contextual.row_paste };
  			}
  		}
  
  		if( this.builder.supports( 'addRow' ) ) {
  			actions.duplicate = { title: panelsOptions.loc.contextual.row_duplicate };
  		}
  
  		if( this.builder.supports( 'deleteRow' ) ) {
  			actions.delete = { title: panelsOptions.loc.contextual.row_delete, confirm: true };
  		}
  
  		if( ! _.isEmpty( actions ) ) {
  			menu.addSection(
  				'row-actions',
  				{
  					sectionTitle: panelsOptions.loc.contextual.row_actions,
  					search: false,
  				},
  				actions,
  				function ( c ) {
  					switch ( c ) {
  						case 'edit':
  							this.editSettingsHandler();
  							break;
  						case 'copy':
  							this.copyHandler();
  							break;
  						case 'paste':
  							this.pasteHandler();
  							break;
  						case 'duplicate':
  							this.duplicateHandler();
  							break;
  						case 'delete':
  							this.visualDestroyModel();
  							break;
  					}
  				}.bind( this )
  			);
  		}
  	},
  } );
  
  },{}],28:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = Backbone.View.extend( {
  
  	stylesLoaded: false,
  
  	initialize: function () {
  
  	},
  
  	/**
  	 * Render the visual styles object.
  	 *
  	 * @param type
  	 * @param postId
  	 */
  	render: function ( stylesType, postId, args ) {
  		if ( _.isUndefined( stylesType ) ) {
  			return;
  		}
  
  		// Add in the default args
  		args = _.extend( {
  			builderType: '',
  			dialog: null
  		}, args );
  
  		this.$el.addClass( 'so-visual-styles so-' + stylesType + '-styles' );
  
  		var postArgs = {
  			builderType: args.builderType
  		};
  
  		if ( stylesType === 'cell') {
  			postArgs.index = args.index;
  		}
  
  		// Load the form
  		$.post(
  			panelsOptions.ajaxurl,
  			{
  				action: 'so_panels_style_form',
  				type: stylesType,
  				style: this.model.get( 'style' ),
  				args: JSON.stringify( postArgs ),
  				postId: postId
  			},
  			function ( response ) {
  				this.$el.html( response );
  				this.setupFields();
  				this.stylesLoaded = true;
  				this.trigger( 'styles_loaded', ! _.isEmpty( response ) );
  				if ( ! _.isNull( args.dialog ) ) {
  					args.dialog.trigger( 'styles_loaded', ! _.isEmpty( response ) );
  				}
  			}.bind(this)
  		);
  
  		return this;
  	},
  
  	/**
  	 * Attach the style view to the DOM.
  	 *
  	 * @param wrapper
  	 */
  	attach: function ( wrapper ) {
  		wrapper.append( this.$el );
  	},
  
  	/**
  	 * Detach the styles view from the DOM
  	 */
  	detach: function () {
  		this.$el.detach();
  	},
  
  	/**
  	 * Setup all the fields
  	 */
  	setupFields: function () {
  
  		// Set up the sections as collapsible
  		this.$( '.style-section-wrapper' ).each( function () {
  			var $s = $( this );
  
  			$s.find( '.style-section-head' ).click( function ( e ) {
  				e.preventDefault();
  				$s.find( '.style-section-fields' ).slideToggle( 'fast' );
  			} );
  		} );
  
  		// Set up the color fields
  		if ( ! _.isUndefined( $.fn.wpColorPicker ) ) {
  			if ( _.isObject( panelsOptions.wpColorPickerOptions.palettes ) && ! $.isArray( panelsOptions.wpColorPickerOptions.palettes ) ) {
  				panelsOptions.wpColorPickerOptions.palettes = $.map( panelsOptions.wpColorPickerOptions.palettes, function ( el ) {
  					return el;
  				} );
  			}
  			this.$( '.so-wp-color-field' ).wpColorPicker( panelsOptions.wpColorPickerOptions );
  		}
  
  		// Set up the image select fields
  		this.$( '.style-field-image' ).each( function () {
  			var frame = null;
  			var $s = $( this );
  
  			$s.find( '.so-image-selector' ).click( function ( e ) {
  				e.preventDefault();
  
  				if ( frame === null ) {
  					// Create the media frame.
  					frame = wp.media( {
  						// Set the title of the modal.
  						title: 'choose',
  
  						// Tell the modal to show only images.
  						library: {
  							type: 'image'
  						},
  
  						// Customize the submit button.
  						button: {
  							// Set the text of the button.
  							text: 'Done',
  							close: true
  						}
  					} );
  
  					frame.on( 'select', function () {
  						var attachment = frame.state().get( 'selection' ).first().attributes;
  
  						var url = attachment.url;
  						if ( ! _.isUndefined( attachment.sizes ) ) {
  							try {
  								url = attachment.sizes.thumbnail.url;
  							}
  							catch ( e ) {
  								// We'll use the full image instead
  								url = attachment.sizes.full.url;
  							}
  						}
  						$s.find( '.current-image' ).css( 'background-image', 'url(' + url + ')' );
  
  						// Store the ID
  						$s.find( 'input' ).val( attachment.id )
  					} );
  				}
  
  				frame.open();
  
  			} );
  
  			// Handle clicking on remove
  			$s.find( '.remove-image' ).click( function ( e ) {
  				e.preventDefault();
  				$s.find( '.current-image' ).css( 'background-image', 'none' );
  				$s.find( 'input' ).val( '' );
  			} );
  		} );
  
  		// Set up all the measurement fields
  		this.$( '.style-field-measurement' ).each( function () {
  			var $$ = $( this );
  
  			var text = $$.find( 'input[type="text"]' );
  			var unit = $$.find( 'select' );
  			var hidden = $$.find( 'input[type="hidden"]' );
  
  			text.focus( function(){
  				$(this).select();
  			} );
  
  			/**
  			 * Load value into the visible input fields.
  			 * @param value
  			 */
  			var loadValue = function( value ) {
  				if( value === '' ) {
  					return;
  				}
  
  				var re = /(?:([0-9\.,\-]+)(.*))+/;
  				var valueList = hidden.val().split( ' ' );
  				var valueListValue = [];
  				for ( var i in valueList ) {
  					var match = re.exec( valueList[i] );
  					if ( ! _.isNull( match ) && ! _.isUndefined( match[1] ) && ! _.isUndefined( match[2] ) ) {
  						valueListValue.push( match[1] );
  						unit.val( match[2] );
  					}
  				}
  
  				if( text.length === 1 ) {
  					// This is a single input text field
  					text.val( valueListValue.join( ' ' ) );
  				}
  				else {
  					// We're dealing with a multiple field
  					if( valueListValue.length === 1 ) {
  						valueListValue = [ valueListValue[0], valueListValue[0], valueListValue[0], valueListValue[0] ];
  					}
  					else if( valueListValue.length === 2 ) {
  						valueListValue = [ valueListValue[0], valueListValue[1], valueListValue[0], valueListValue[1] ];
  					}
  					else if( valueListValue.length === 3 ) {
  						valueListValue = [ valueListValue[0], valueListValue[1], valueListValue[2], valueListValue[1] ];
  					}
  
  					// Store this in the visible fields
  					text.each( function( i, el ) {
  						$( el ).val( valueListValue[i] );
  					} );
  				}
  			};
  			loadValue( hidden.val() );
  
  			/**
  			 * Set value of the hidden field based on inputs
  			 */
  			var setValue = function( e ){
  				var i;
  
  				if( text.length === 1 ) {
  					// We're dealing with a single measurement
  					var fullString = text
  						.val()
  						.split( ' ' )
  						.filter( function ( value ) {
  							return value !== '';
  						} )
  						.map( function ( value ) {
  							return value + unit.val();
  						} )
  						.join( ' ' );
  					hidden.val( fullString );
  				}
  				else {
  					var target = $( e.target ),
  						valueList = [],
  						emptyIndex = [],
  						fullIndex = [];
  
  					text.each( function( i, el ) {
  						var value = $( el ).val( ) !== '' ? parseFloat( $( el ).val( ) ) : null;
  						valueList.push( value );
  
  						if( value === null ) {
  							emptyIndex.push( i );
  						}
  						else {
  							fullIndex.push( i );
  						}
  					} );
  
  					if( emptyIndex.length === 3 && fullIndex[0] === text.index( target ) ) {
  						text.val( target.val() );
  						valueList = [ target.val(), target.val(), target.val(), target.val() ];
  					}
  
  					if( JSON.stringify( valueList ) === JSON.stringify( [ null, null, null, null ] ) ) {
  						hidden.val('');
  					}
  					else {
  						hidden.val( valueList.map( function( k ){
  							return ( k === null ? 0 : k ) + unit.val();
  						} ).join( ' ' ) );
  					}
  				}
  			};
  
  			// Set the value when ever anything changes
  			text.change( setValue );
  			unit.change( setValue );
  		} );
  	}
  
  } );
  
  },{}],29:[function(require,module,exports){
  var panels = window.panels, $ = jQuery;
  
  module.exports = Backbone.View.extend( {
  	template: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-builder-widget' ).html() ) ),
  
  	// The cell view that this widget belongs to
  	cell: null,
  
  	// The edit dialog
  	dialog: null,
  
  	events: {
  		'click .widget-edit': 'editHandler',
  		'click .title h4': 'titleClickHandler',
  		'click .actions .widget-duplicate': 'duplicateHandler',
  		'click .actions .widget-delete': 'deleteHandler'
  	},
  
  	/**
  	 * Initialize the widget
  	 */
  	initialize: function () {
  		this.model.on( 'user_edit', this.editHandler, this );				 // When a user wants to edit the widget model
  		this.model.on( 'user_duplicate', this.duplicateHandler, this );	   // When a user wants to duplicate the widget model
  		this.model.on( 'destroy', this.onModelDestroy, this );
  		this.model.on( 'visual_destroy', this.visualDestroyModel, this );
  
  		this.model.on( 'change:values', this.onModelChange, this );
  		this.model.on( 'change:label', this.onLabelChange, this );
  	},
  
  	/**
  	 * Render the widget
  	 */
  	render: function ( options ) {
  		options = _.extend( {'loadForm': false}, options );
  
  		this.setElement( this.template( {
  			title: this.model.getWidgetField( 'title' ),
  			description: this.model.getTitle()
  		} ) );
  
  		this.$el.data( 'view', this );
  
  		// Remove any unsupported actions
  		if( ! this.cell.row.builder.supports( 'editWidget' ) || this.model.get( 'read_only' ) ) {
  			this.$( '.actions .widget-edit' ).remove();
  			this.$el.addClass('so-widget-no-edit');
  		}
  		if( ! this.cell.row.builder.supports( 'addWidget' ) ) {
  			this.$( '.actions .widget-duplicate' ).remove();
  			this.$el.addClass('so-widget-no-duplicate');
  		}
  		if( ! this.cell.row.builder.supports( 'deleteWidget' ) ) {
  			this.$( '.actions .widget-delete' ).remove();
  			this.$el.addClass('so-widget-no-delete');
  		}
  		if( ! this.cell.row.builder.supports( 'moveWidget' ) ) {
  			this.$el.addClass('so-widget-no-move');
  		}
  		if( !$.trim( this.$('.actions').html() ).length ) {
  			this.$( '.actions' ).remove();
  		}
  
  		if( this.model.get( 'read_only' ) ) {
  			this.$el.addClass('so-widget-read-only');
  		}
  
  		if ( _.size( this.model.get( 'values' ) ) === 0 || options.loadForm ) {
  			// If this widget doesn't have a value, create a form and save it
  			var dialog = this.getEditDialog();
  
  			// Save the widget as soon as the form is loaded
  			dialog.once( 'form_loaded', dialog.saveWidget, dialog );
  
  			// Setup the dialog to load the form
  			dialog.setupDialog();
  		}
  
  		return this;
  	},
  
  	/**
  	 * Display an animation that implies creation using a visual animation
  	 */
  	visualCreate: function () {
  		this.$el.hide().fadeIn( 'fast' );
  	},
  
  	/**
  	 * Get the dialog view of the form that edits this widget
  	 *
  	 * @returns {null}
  	 */
  	getEditDialog: function () {
  		if ( this.dialog === null ) {
  			this.dialog = new panels.dialog.widget( {
  				model: this.model
  			} );
  			this.dialog.setBuilder( this.cell.row.builder );
  
  			// Store the widget view
  			this.dialog.widgetView = this;
  		}
  		return this.dialog;
  	},
  
  	/**
  	 * Handle clicking on edit widget.
  	 *
  	 * @returns {boolean}
  	 */
  	editHandler: function () {
  		// Create a new dialog for editing this
  		this.getEditDialog().openDialog();
  	},
  
  	titleClickHandler: function( event ){
  		if ( ! this.cell.row.builder.supports( 'editWidget' ) || this.model.get( 'read_only' ) ) {
  			return this;
  		}
  		this.editHandler();
  		return this;
  	},
  
  	/**
  	 * Handle clicking on duplicate.
  	 *
  	 * @returns {boolean}
  	 */
  	duplicateHandler: function () {
  		// Add the history entry
  		this.cell.row.builder.addHistoryEntry( 'widget_duplicated' );
  
  		// Create the new widget and connect it to the widget collection for the current row
  		var newWidget = this.model.clone( this.model.cell );
  
  		this.cell.model.get('widgets').add( newWidget, {
  			// Add this after the existing model
  			at: this.model.collection.indexOf( this.model ) + 1
  		} );
  
  		this.cell.row.builder.model.refreshPanelsData();
  		return this;
  	},
  
  	/**
  	 * Copy the row to a cookie based clipboard
  	 */
  	copyHandler: function(){
  		panels.helpers.clipboard.setModel( this.model );
  	},
  
  	/**
  	 * Handle clicking on delete.
  	 *
  	 * @returns {boolean}
  	 */
  	deleteHandler: function () {
  		this.model.trigger( 'visual_destroy' );
  		return this;
  	},
  
  	onModelChange: function () {
  		// Update the description when ever the model changes
  		this.$( '.description' ).html( this.model.getTitle() );
  	},
  
  	onLabelChange: function( model ) {
  		this.$( '.title > h4' ).text( model.getWidgetField( 'title' ) );
  	},
  
  	/**
  	 * When the model is destroyed, fade it out
  	 */
  	onModelDestroy: function () {
  		this.remove();
  	},
  
  	/**
  	 * Visually destroy a model
  	 */
  	visualDestroyModel: function () {
  		// Add the history entry
  		this.cell.row.builder.addHistoryEntry( 'widget_deleted' );
  
  		var thisView = this;
  		this.$el.fadeOut( 'fast', function () {
  			thisView.cell.row.resize();
  			thisView.model.destroy();
  			thisView.cell.row.builder.model.refreshPanelsData();
  			thisView.remove();
  		} );
  
  		return this;
  	},
  
  	/**
  	 * Build up the contextual menu for a widget
  	 *
  	 * @param e
  	 * @param menu
  	 */
  	buildContextualMenu: function ( e, menu ) {
  		if( this.cell.row.builder.supports( 'addWidget' ) ) {
  			menu.addSection(
  				'add-widget-below',
  				{
  					sectionTitle: panelsOptions.loc.contextual.add_widget_below,
  					searchPlaceholder: panelsOptions.loc.contextual.search_widgets,
  					defaultDisplay: panelsOptions.contextual.default_widgets
  				},
  				panelsOptions.widgets,
  				function ( c ) {
  					this.cell.row.builder.addHistoryEntry( 'widget_added' );
  
  					var widget = new panels.model.widget( {
  						class: c
  					} );
  					widget.cell = this.cell.model;
  
  					// Insert the new widget below
  					this.cell.model.get('widgets').add( widget, {
  						// Add this after the existing model
  						at: this.model.collection.indexOf( this.model ) + 1
  					} );
  
  					this.cell.row.builder.model.refreshPanelsData();
  				}.bind( this )
  			);
  		}
  
  		var actions = {};
  
  		if( this.cell.row.builder.supports( 'editWidget' ) && ! this.model.get( 'read_only' ) ) {
  			actions.edit = { title: panelsOptions.loc.contextual.widget_edit };
  		}
  
  		// Copy and paste functions
  		if ( panels.helpers.clipboard.canCopyPaste() ) {
  			actions.copy = {title: panelsOptions.loc.contextual.widget_copy};
  		}
  
  		if( this.cell.row.builder.supports( 'addWidget' ) ) {
  			actions.duplicate = { title: panelsOptions.loc.contextual.widget_duplicate };
  		}
  
  		if( this.cell.row.builder.supports( 'deleteWidget' ) ) {
  			actions.delete = { title: panelsOptions.loc.contextual.widget_delete, confirm: true };
  		}
  
  		if( ! _.isEmpty( actions ) ) {
  			menu.addSection(
  				'widget-actions',
  				{
  					sectionTitle: panelsOptions.loc.contextual.widget_actions,
  					search: false,
  				},
  				actions,
  				function ( c ) {
  					switch ( c ) {
  						case 'edit':
  							this.editHandler();
  							break;
  						case 'copy':
  							this.copyHandler();
  							break;
  						case 'duplicate':
  							this.duplicateHandler();
  							break;
  						case 'delete':
  							this.visualDestroyModel();
  							break;
  					}
  				}.bind( this )
  			);
  		}
  
  		// Lets also add the contextual menu for the entire row
  		this.cell.buildContextualMenu( e, menu );
  	}
  
  } );
  
  },{}],30:[function(require,module,exports){
  var mediaWidget = require( './media-widget' );
  var textWidget = require( './text-widget' );
  
  var jsWidget = {
  	MEDIA_AUDIO: 'media_audio',
  	MEDIA_IMAGE: 'media_image',
  	MEDIA_VIDEO: 'media_video',
  	TEXT: 'text',
  
  	addWidget: function( widgetContainer, widgetId ) {
  		var idBase = widgetContainer.find( '> .id_base' ).val();
  		var widget;
  
  		switch ( idBase ) {
  			case this.MEDIA_AUDIO:
  			case this.MEDIA_IMAGE:
  			case this.MEDIA_VIDEO:
  				widget = mediaWidget;
  				break;
  			case this.TEXT:
  				widget = textWidget;
  				break
  		}
  
  		widget.addWidget( idBase, widgetContainer, widgetId );
  	},
  };
  
  module.exports = jsWidget;
  
  },{"./media-widget":31,"./text-widget":32}],31:[function(require,module,exports){
  var $ = jQuery;
  
  var mediaWidget = {
  	addWidget: function( idBase, widgetContainer, widgetId ) {
  		var component = wp.mediaWidgets;
  
  		var ControlConstructor = component.controlConstructors[ idBase ];
  		if ( ! ControlConstructor ) {
  			return;
  		}
  
  		var ModelConstructor = component.modelConstructors[ idBase ] || component.MediaWidgetModel;
  		var widgetContent = widgetContainer.find( '> .widget-content' );
  		var controlContainer = $( '<div class="media-widget-control"></div>' );
  		widgetContent.before( controlContainer );
  
  		var modelAttributes = {};
  		widgetContent.find( '.media-widget-instance-property' ).each( function() {
  			var input = $( this );
  			modelAttributes[ input.data( 'property' ) ] = input.val();
  		});
  		modelAttributes.widget_id = widgetId;
  
  		var widgetModel = new ModelConstructor( modelAttributes );
  
  		var widgetControl = new ControlConstructor({
  			el: controlContainer,
  			model: widgetModel
  		});
  
  		widgetControl.render();
  
  		return widgetControl;
  	}
  };
  
  module.exports = mediaWidget;
  
  },{}],32:[function(require,module,exports){
  var $ = jQuery;
  
  var textWidget = {
  	addWidget: function( idBase, widgetContainer, widgetId ) {
  		var component = wp.textWidgets;
  
  		var widgetControl = new component.TextWidgetControl({
  			el: widgetContainer
  		});
  
  		widgetControl.initializeEditor();
  
  		return widgetControl;
  	}
  };
  
  module.exports = textWidget;
  
  },{}]},{},[16]);
  