Your IP : 216.73.216.162


Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/
Upload File :
Current File : /home/x/b/o/xbodynamge/namtation/wp-content/edit.js.tar

home/xbodynamge/lebauwcentre/wp-content/plugins/tablepress/admin/js/edit.js000064400000135321151122137430023227 0ustar00/**
 * JavaScript code for the "Edit" screen
 *
 * @package TablePress
 * @subpackage Views JavaScript
 * @author Tobias Bäthge
 * @since 1.0.0
 */

/* global alert, confirm, tp, tablepress_strings, tablepress_options, ajaxurl, wpLink, tb_show, wp, JSON */

// Ensure the global `tp` object exists.
window.tp = window.tp || {};

jQuery( document ).ready( function( $ ) {

	'use strict';

	/* Wrapper to find elements in the page faster with JS-native functions */
	var $id = function( element_id ) {
		return $( document.getElementById( element_id ) );
	};

	/**
	 * TablePress object, mostly with functionality for the "Edit" screen
	 *
	 * @since 1.0.0
	 */
	tp.made_changes = false;

	tp.table = {
		id: $id( 'table-id' ).val(),
		new_id: $id( 'table-new-id' ).val(),
		rows: parseInt( $id( 'number-rows' ).val(), 10 ),
		columns: parseInt( $id( 'number-columns' ).val(), 10 ),
		head: $id( 'option-table-head' ).prop( 'checked' ),
		foot: $id( 'option-table-foot' ).prop( 'checked' ),
		no_data_columns_pre: 2,
		no_data_columns_post: 1,
		body_cells_pre: '<tr><td><span class="move-handle"></span></td><td><input type="checkbox" /><input type="hidden" class="visibility" name="table[visibility][rows][]" value="1" /></td>',
		body_cells_post: '<td><span class="move-handle"></span></td></tr>',
		body_cell: '<td><textarea rows="1"></textarea></td>',
		head_cell: '<th class="head"><span class="sort-control sort-desc" title="' + tablepress_strings.sort_desc + '"><span class="sorting-indicator"></span></span><span class="sort-control sort-asc" title="' + tablepress_strings.sort_asc + '"><span class="sorting-indicator"></span></span><span class="move-handle"></span></th>',
		foot_cell: '<th><input type="checkbox" /><input type="hidden" class="visibility" name="table[visibility][columns][]" value="1" /></th>',
		set_table_changed: function() {
			tp.made_changes = true;
		},
		unset_table_changed: function() {
			tp.made_changes = false;
			$id( 'edit-form-body' ).one( 'change', 'textarea', tp.table.set_table_changed );
			// @TODO: maybe use .tablepress-postbox-table here and further below
			$( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );
		},
		change_id: function( /* event */ ) {
			// empty table IDs are not allowed
			if ( '' === $.trim( $id( 'table-new-id' ).val() ) ) {
				alert( tablepress_strings.table_id_not_empty );
				$id( 'table-new-id' ).val( tp.table.new_id ).focus().select();
				return;
			}
			// the '0' table ID is not allowed
			if ( '0' === $.trim( $id( 'table-new-id' ).val() ) ) {
				alert( tablepress_strings.table_id_not_zero );
				$id( 'table-new-id' ).val( tp.table.new_id ).focus().select();
				return;
			}

			if ( this.value === tp.table.new_id ) {
				return;
			}

			if ( confirm( tablepress_strings.ays_change_table_id ) ) {
				tp.table.new_id = this.value;
				$( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' ).click(); // click() to focus and select
				tp.table.set_table_changed();
			} else {
				$(this).val( tp.table.new_id );
			}
		},
		change_table_head: function( /* event */ ) {
			tp.table.head = $(this).prop( 'checked' );
			$id( 'option-use-datatables' ).prop( 'disabled', ! tp.table.head ).change();
			$id( 'notice-datatables-head-row' ).toggle( ! tp.table.head );
			tp.rows.stripe();
		},
		change_table_foot: function( /* event */ ) {
			tp.table.foot = $(this).prop( 'checked' );
			tp.rows.stripe();
		},
		change_print_name_description: function( /* event */ ) {
			$id( this.id + '-position' ).prop( 'disabled', ! $(this).prop( 'checked' ) );
		},
		change_datatables: function() {
			var $datatables_checkbox = $id( 'option-use-datatables' ),
				checkboxes_disabled = ! ( $datatables_checkbox.prop( 'checked' ) && ! $datatables_checkbox.prop( 'disabled' ) );
			$datatables_checkbox.closest( 'tbody' ).find( 'input' ).not( $datatables_checkbox ).prop( 'disabled', checkboxes_disabled );
			tp.table.change_datatables_pagination();
		},
		change_datatables_pagination: function() {
			var $pagination_checkbox = $id( 'option-datatables-paginate' ),
				pagination_enabled = ( $pagination_checkbox.prop( 'checked' ) && ! $pagination_checkbox.prop( 'disabled' ) );
			$id( 'option-datatables-lengthchange' ).prop( 'disabled', ! pagination_enabled );
			$id( 'option-datatables-paginate_entries' ).prop( 'disabled', ! pagination_enabled );
		},
		prepare_ajax_request: function( wp_action, wp_nonce ) {
			var $table_body = $id( 'edit-form-body' ),
				table_data = [],
				table_options,
				table_number = { rows: tp.table.rows, columns: tp.table.columns, hidden_rows: 0, hidden_columns: 0 },
				table_visibility = { rows: [], columns: [] };

			$table_body.children().each( function( idx, row ) {
				table_data[ idx ] = $( row ).find( 'textarea' )
					.map( function() {
						return $(this).val();
					} )
					.get();
			} );
			table_data = JSON.stringify( table_data );

			// @TODO: maybe for options saving: https://stackoverflow.com/questions/1184624/convert-form-data-to-javascript-object-with-jquery
			// or each()-loop through all checkboxes/textfields/selects
			table_options = {
				// Table Options
				table_head: tp.table.head,
				table_foot: tp.table.foot,
				alternating_row_colors: $id( 'option-alternating-row-colors' ).prop( 'checked' ),
				row_hover: $id( 'option-row-hover' ).prop( 'checked' ),
				print_name: $id( 'option-print-name' ).prop( 'checked' ),
				print_description: $id( 'option-print-description' ).prop( 'checked' ),
				print_name_position: $id( 'option-print-name-position' ).val(),
				print_description_position: $id( 'option-print-description-position' ).val(),
				extra_css_classes: $id( 'option-extra-css-classes' ).val(),
				// DataTables JS features
				use_datatables: $id( 'option-use-datatables' ).prop( 'checked' ),
				datatables_sort: $id( 'option-datatables-sort' ).prop( 'checked' ),
				datatables_filter: $id( 'option-datatables-filter' ).prop( 'checked' ),
				datatables_paginate: $id( 'option-datatables-paginate' ).prop( 'checked' ),
				datatables_lengthchange: $id( 'option-datatables-lengthchange' ).prop( 'checked' ),
				datatables_paginate_entries: $id( 'option-datatables-paginate_entries' ).val(),
				datatables_info: $id( 'option-datatables-info' ).prop( 'checked' ),
				datatables_scrollx: $id( 'option-datatables-scrollx' ).prop( 'checked' ),
				datatables_custom_commands: $id( 'option-datatables-custom-commands' ).val()
			};
			table_options = JSON.stringify( table_options );

			table_visibility.rows = $table_body.find( 'input[type="hidden"]' )
				.map( function() {
					if ( '1' === $(this).val() ) {
						return 1;
					}
					table_number.hidden_rows += 1;
					return 0;
				} )
				.get();
			table_visibility.columns = $id( 'edit-form-foot' ).find( 'input[type="hidden"]' )
				.map( function() {
					if ( '1' === $(this).val() ) {
						return 1;
					}
					table_number.hidden_columns += 1;
					return 0;
				} )
				.get();
			table_visibility = JSON.stringify( table_visibility );

			// request_data =
			return {
				action: wp_action,
				_ajax_nonce : $( wp_nonce ).val(),
				tablepress: {
					id: tp.table.id,
					new_id: tp.table.new_id,
					name: $id( 'table-name' ).val(),
					description: $id( 'table-description' ).val(),
					number: table_number,
					data: table_data,
					options: table_options,
					visibility: table_visibility
				}
			};
		},
		preview: {
			trigger: function( /* event */ ) {
				if ( ! tp.made_changes ) {
					tp.table.preview.show( $(this).attr( 'href' ) + '&TB_iframe=true' );
					return false;
				}

				// validation checks
				if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
					alert( tablepress_strings.num_pagination_entries_invalid );
					$id( 'option-datatables-paginate_entries' ).focus().select();
					return;
				}
				if ( ( /[^A-Za-z0-9- _]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
					alert( tablepress_strings.extra_css_classes_invalid );
					$id( 'option-extra-css-classes' ).focus().select();
					return;
				}

				$(this).closest( 'p' ).append( '<span class="animation-preview spinner is-active" title="' + tablepress_strings.preparing_preview + '"/>' );
				$( 'body' ).addClass( 'wait' );
				$id( 'table-preview' ).empty(); // clear preview

				$.ajax({
					'type': 'POST',
					'url': ajaxurl,
					'data': tp.table.prepare_ajax_request( 'tablepress_preview_table', '#nonce-preview-table' ),
					'success': tp.table.preview.ajax_success,
					'error': tp.table.preview.ajax_error,
					'dataType': 'json'
				} );

				return false;
			},
			ajax_success: function( data, status /*, jqXHR */ ) {
				if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
					tp.table.preview.error( 'AJAX call successful, but unclear status.' );
				} else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) || ( true !== data.success ) ) {
					tp.table.preview.error( 'AJAX call successful, but unclear data.' );
				} else {
					tp.table.preview.success( data );
				}
			},
			ajax_error: function( jqXHR, status, error_thrown ) {
				tp.table.preview.error( 'AJAX call failed: ' + status + ' - ' + error_thrown );
			},
			success: function( data ) {
				$id( 'table-preview' ).empty();
				$( '<iframe id="table-preview-iframe" />' ).load( function() {
					var $iframe = $(this).contents();
					$iframe.find( 'head' ).append( data.head_html );
					$iframe.find( 'body' ).append( data.body_html );
				} ).appendTo( '#table-preview' );
				$( '.animation-preview' ).remove();
				$( 'body' ).removeClass( 'wait' );
				tp.table.preview.show( '#TB_inline?inlineId=preview-container' );
			},
			error: function( message ) {
				$( '.animation-preview' ).closest( 'p' )
					.after( '<div class="ajax-alert preview-error error"><p>' + tablepress_strings.preview_error + ': ' + message + '</p></div>' );
				$( '.animation-preview' ).remove();
				$( '.preview-error' ).delay( 6000 ).fadeOut( 2000, function() { $(this).remove(); } );
				$( 'body' ).removeClass( 'wait' );
			},
			show: function( url ) {
				var width = $( window ).width() - 120,
					height = $( window ).height() - 120;
				if ( $( '#wpadminbar' ).length ) {
					height -= parseInt( $( '#wpadminbar' ).css( 'height' ), 10 );
				}
				tb_show( $( '.show-preview-button' ).first().text(), url + '&height=' + height + '&width=' + width, false );
			}
		}
	};

	tp.rows = {
		create: function( num_rows ) {
			var i, j,
				column_idxs,
				new_rows = '';

			for ( i = 0; i < num_rows; i++ ) {
				new_rows += tp.table.body_cells_pre;
				for ( j = 0; j < tp.table.columns; j++ ) {
					new_rows += tp.table.body_cell;
				}
				new_rows += tp.table.body_cells_post;
			}

			column_idxs = $id( 'edit-form-foot' ).find( '.column-hidden' )
				.map( function() { return $(this).index(); } ).get();
			return $( new_rows ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.addClass( 'column-hidden' );
			} );
		},
		append: function( /* event */ ) {
			var num_rows = $id( 'rows-append-number' ).val();

			if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_rows ) ) {
				alert( tablepress_strings.append_num_rows_invalid );
				$id( 'rows-append-number' ).focus().select();
				return;
			}

			$id( 'edit-form-body' ).append( tp.rows.create( num_rows ) );

			tp.rows.stripe();
			tp.reindex();
		},
		insert: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.before( tp.rows.create( 1 ) );

			tp.rows.stripe();
			tp.reindex();
		},
		duplicate: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.each( function( idx, row ) {
				var $row = $( row ),
					$textareas = $row.find( 'textarea' ),
					$duplicated_row = $row.clone();
				$duplicated_row.find( 'textarea' ).removeAttr( 'id' ).each( function( idx, cell ) {
					$( cell ).val( $textareas.eq( idx ).val() ); // setting val() is necessary, as clone() doesn't copy the current value, see jQuery bugs 5524, 2285, 3016
				} );
				$row.after( $duplicated_row );
			} );

			tp.rows.stripe();
			tp.reindex();
		},
		hide: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.addClass( 'row-hidden' ).find( '.visibility' ).val( '0' );

			tp.rows.stripe();
			tp.table.set_table_changed();
		},
		unhide: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.removeClass( 'row-hidden' ).find( '.visibility' ).val( '1' );

			tp.rows.stripe();
			tp.table.set_table_changed();
		},
		remove: function( /* event */ ) {
			var confirm_message,
				$selected_rows = $id( 'edit-form-body' ).find( 'input:checked' ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			if ( tp.table.rows === $selected_rows.length ) {
				alert( tablepress_strings.no_remove_all_rows );
				return;
			}

			if ( 1 === $selected_rows.length ) {
				confirm_message = tablepress_strings.ays_remove_rows_singular;
			} else {
				confirm_message = tablepress_strings.ays_remove_rows_plural;
			}
			if ( ! confirm( confirm_message ) ) {
				return;
			}

			$selected_rows.remove();

			tp.rows.stripe();
			tp.reindex();
		},
		move: {
			start: function( event, ui ) {
				$( ui.placeholder ).removeClass( 'row-hidden' ).css( 'visibility', 'visible' )
					.html( '<td colspan="' + ( tp.table.columns + tp.table.no_data_columns_pre + tp.table.no_data_columns_post ) + '"><div/></td>' );
				$( ui.helper ).removeClass( 'odd head-row foot-row' );
			},
			change: function( event, ui ) {
				tp.rows.stripe( ui.helper );
			},
			stop: function( /* event, ui */ ) {
				tp.rows.stripe();
			}
		},
		sort: function() {
			var column_idx = $(this).parent().index(),
				direction = ( $(this).hasClass( 'sort-asc' ) ) ? 1 : -1,
				$table_body = $('#edit-form-body'),
				$head_rows = $table_body.find( '.head-row' ).prevAll().addBack(),
				$foot_rows = $table_body.find( '.foot-row' ).nextAll().addBack(),
				rows = $table_body.children().not( $head_rows ).not( $foot_rows ).get(),
				/*
				 * Natural Sort algorithm for Javascript - Version 0.8.1 - Released under MIT license
				 * Author: Jim Palmer (based on chunking idea from Dave Koelle)
				 * See: https://github.com/overset/javascript-natural-sort and http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm-with-unicode-support/
				 */
				natural_sort = function( a, b ) {
					var re = /(^([+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|^0x[\da-fA-F]+$|\d+)/g,
						sre = /^\s+|\s+$/g,   // trim pre-post whitespace
						snre = /\s+/g,        // normalize all whitespace to single ' ' character
						dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
						hre = /^0x[0-9a-f]+$/i,
						ore = /^0/,
						// strip whitespace
						x = a.replace(sre, '') || '',
						y = b.replace(sre, '') || '',
						// chunk/tokenize
						xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
						yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
						// numeric, hex or date detection
						xD = parseInt(x.match(hre), 16) || (xN.length !== 1 && Date.parse(x)),
						yD = parseInt(y.match(hre), 16) || xD && y.match(dre) && Date.parse(y) || null,
						normChunk = function(s, l) {
							// normalize spaces; find floats not starting with '0', string or 0 if not defined (Clint Priest)
							return (!s.match(ore) || l === 1) && parseFloat(s) || s.replace(snre, ' ').replace(sre, '') || 0;
						},
						oFxNcL, oFyNcL;
					// first try and sort Hex codes or Dates
					if (yD) {
						if (xD < yD) { return -1; }
						else if (xD > yD) { return 1; }
					}
					// natural sorting through split numeric strings and default strings
					for(var cLoc = 0, xNl = xN.length, yNl = yN.length, numS = Math.max(xNl, yNl); cLoc < numS; cLoc++) {
						oFxNcL = normChunk(xN[cLoc] || '', xNl);
						oFyNcL = normChunk(yN[cLoc] || '', yNl);
						// handle numeric vs string comparison - number < string - (Kyle Adams)
						if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
							return isNaN(oFxNcL) ? 1 : -1;
						}
						// if unicode use locale comparison
						if (/[^\x00-\x80]/.test(oFxNcL + oFyNcL) && oFxNcL.localeCompare) {
							var comp = oFxNcL.localeCompare(oFyNcL);
							return comp / Math.abs(comp);
						}
						if (oFxNcL < oFyNcL) { return -1; }
						else if (oFxNcL > oFyNcL) { return 1; }
					}
				};

			$.each( rows, function( row_idx, row ) {
				row.sort_key = ( '' + $( row ).children().eq( column_idx ).find( 'textarea' ).val() ).toLowerCase(); // convert to string, and lower case for case insensitive sorting
			} );

			rows.sort( function( a, b ) {
				return direction * natural_sort( a.sort_key, b.sort_key );
			} );

			// might not be necessary:
			$.each( rows, function( row_idx, row ) {
				row.sort_key = null;
			} );

			$table_body.append( $head_rows );
			$table_body.append( rows );
			$table_body.append( $foot_rows );

			tp.rows.stripe();
			tp.reindex();
		},
		stripe: function( helper ) {
			if ( 'undefined' === typeof helper ) {
				helper = null;
			}
			helper = $( helper );
			var $rows = $id( 'edit-form-body' ).children().removeClass( 'odd head-row foot-row' ).not( helper );
			$rows.filter( ':even' ).addClass( 'odd' );
			$rows = $rows.not( '.row-hidden' );
			if ( helper.hasClass( 'row-hidden' ) ) {
				$rows = $rows.not( '.ui-sortable-placeholder' );
			}
			if ( tp.table.head ) {
				$rows.first().addClass( 'head-row' );
			}
			if ( tp.table.foot ) {
				$rows.last().addClass( 'foot-row' );
			}
		}
	};

	tp.columns = {
		append: function( /* event */ ) {
			var i,
				num_columns = $id( 'columns-append-number' ).val(),
				new_head_cells = '', new_body_cells = '', new_foot_cells = '';

			if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_columns ) ) {
				alert( tablepress_strings.append_num_columns_invalid );
				$id( 'columns-append-number' ).focus().select();
				return;
			}

			for ( i = 0; i < num_columns; i++ ) {
				new_body_cells += tp.table.body_cell;
				new_head_cells += tp.table.head_cell;
				new_foot_cells += tp.table.foot_cell;
			}

			$id( 'edit-form-body' ).children().each( function( row_idx, row ) {
				$( row ).children().slice( - tp.table.no_data_columns_post )
					.before( new_body_cells );
			} );
			$id( 'edit-form-head' ).children().slice( - tp.table.no_data_columns_post )
				.before( new_head_cells );
			$id( 'edit-form-foot' ).children().slice( - tp.table.no_data_columns_post )
				.before( new_foot_cells );

			tp.reindex();
		},
		insert: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.before( tp.table.body_cell );
			} );
			$id( 'edit-form-head' ).children()
				.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
				.before( tp.table.head_cell );
			$selected_columns.before( tp.table.foot_cell );

			tp.reindex();
		},
		duplicate: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form' ).find( 'tr' ).each( function( row_idx, row ) {
				$( row ).children().each( function( idx, cell ) {
					if ( -1 !== $.inArray( idx, column_idxs ) ) {
						var $cell = $( cell ),
							$duplicated_cell = $cell.clone();
							$duplicated_cell.find( 'textarea' ).removeAttr( 'id' ).val( $cell.find( 'textarea' ).val() ); // setting val() is necessary, as clone() doesn't copy the current value, see jQuery bugs 5524, 2285, 3016
						$cell.after( $duplicated_cell );
					}
				} );
			} );

			tp.reindex();
		},
		hide: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.addClass( 'column-hidden' );
			} );
			$selected_columns.addClass( 'column-hidden' ).find( '.visibility' ).val( '0' );

			tp.table.set_table_changed();
		},
		unhide: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.removeClass( 'column-hidden' );
			} );
			$selected_columns.removeClass( 'column-hidden' ).find( '.visibility' ).val( '1' );

			tp.table.set_table_changed();
		},
		remove: function( /* event */ ) {
			var column_idxs,
				confirm_message,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			if ( tp.table.columns === $selected_columns.length ) {
				alert( tablepress_strings.no_remove_all_columns );
				return;
			}

			if ( 1 === $selected_columns.length ) {
				confirm_message = tablepress_strings.ays_remove_columns_singular;
			} else {
				confirm_message = tablepress_strings.ays_remove_columns_plural;
			}
			if ( ! confirm( confirm_message ) ) {
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.remove();
			} );
			$selected_columns.remove();

			tp.reindex();
		},
		move: {
			source_idx: -1,
			target_idx: -1,
			$rows: null,
			$row_children: null,
			$cell: null,
			$cells: null,
			$placeholder: null,
			$helper: null,
			start: function( event, ui ) {
				var $item = $( ui.item ),
					column_width;

				tp.columns.move.source_idx = $item.index();

				tp.columns.move.$rows = $id( 'edit-form-body' ).children().add( '#edit-form-foot' );

				tp.columns.move.$cells = tp.columns.move.$rows
					.find( ':nth-child(' + ( tp.columns.move.source_idx + 1 ) + ')' )
					.each( function() {
						tp.columns.move.$cell = $(this);
						$( '<td class="move-placeholder"><div/></td>' ).insertBefore( tp.columns.move.$cell );
						tp.columns.move.$cell.insertAfter( tp.columns.move.$cell.nextAll().last() )
							.clone().addClass( 'move-hover' ).insertAfter( tp.columns.move.$cell )
							.find( 'textarea' ).val( tp.columns.move.$cell.find( 'textarea' ).val() );
							// last line works around problem with clone() of textareas, see jQuery bugs 5524, 2285, 3016
					} )
					.hide();

				tp.columns.move.$helper = tp.columns.move.$rows.find( '.move-hover' );
				/* // seems not to be working for rows, so disable it for columns
					.each( function() {
						tp.columns.move.$cell = $(this);
						tp.columns.move.$cell.css( 'top', ( tp.columns.move.$cell.position().top - 3 ) + 'px' );
					} );
				*/

				column_width = tp.columns.move.$helper.eq(1).width(); // eq(0) is table foot
				tp.columns.move.$helper.eq(0).width( column_width );
				tp.columns.move.$placeholder = tp.columns.move.$rows.find( '.move-placeholder' );
				tp.columns.move.$placeholder.find( 'div' ).width( column_width );
			},
			change: function( event, ui ) {
				tp.columns.move.target_idx = $( ui.placeholder ).index();

				if ( ( tp.columns.move.target_idx - tp.columns.move.source_idx ) === 1 ) {
					tp.columns.move.target_idx += 1;
				} else {
					if ( tp.columns.move.target_idx === tp.columns.move.source_idx ) {
						tp.columns.move.target_idx -= 1;
					}
				}

				tp.columns.move.$placeholder.each( function() {
					tp.columns.move.$cell = $(this);
					tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().children().eq( tp.columns.move.target_idx ) );
				} );

				if ( tp.columns.move.target_idx > tp.columns.move.source_idx ) {
					tp.columns.move.target_idx -= 1;
				}

				tp.columns.move.source_idx = tp.columns.move.target_idx;
			},
			sort: function( event, ui ) {
				tp.columns.move.$helper.css( 'left', ui.position.left );
			},
			stop: function( /* event, ui */ ) {
				tp.columns.move.$helper.remove();
				tp.columns.move.$cells
					.each( function() {
						tp.columns.move.$cell = $(this);
						tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().find( '.move-placeholder' ) );
					} )
					.show();
				tp.columns.move.$placeholder.remove();

				tp.columns.move.source_idx = tp.columns.move.target_idx = -1;
				tp.columns.move.$rows = tp.columns.move.$row_children = tp.columns.move.$cell = tp.columns.move.$cells = tp.columns.move.$placeholder = tp.columns.move.$helper = null;

				tp.reindex();
			}
		},
		number_to_letter: function( number ) {
			var column = '';
			while ( number > 0 ) {
				column = String.fromCharCode( 65 + ( ( number-1) % 26 ) ) + column;
				number = Math.floor( (number-1) / 26 );
			}
			return column;
		}/*,
		letter_to_number: function( column ) {
			column = column.toUpperCase();
			var count = column.length,
				number = 0,
				i;
			for ( i = 0; i < count; i++ ) {
				number += ( column.charCodeAt( count-1-i ) - 64 ) * Math.pow( 26, i );
			}
			return number;
		}*/
	};

	tp.cells = {
		$focus: $( null ),
		$textarea: null,
		autogrow: function( /* event */ ) {
			tp.cells.$focus.removeClass( 'focus' );
			tp.cells.$focus = $(this).closest( 'tr' ).addClass( 'focus' );
		},
		advanced_editor: {
			prompt_shown: false,
			keyopen: function( event ) {
				if ( ! event.shiftKey ) {
					return;
				}

				var $advanced_editor = $id( 'advanced-editor-content' );
				tp.cells.$textarea = $(this).blur();
				$advanced_editor.val( tp.cells.$textarea.val() );
				$id( 'advanced-editor' ).wpdialog( 'open' );
				$advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
				$advanced_editor.focus();
			},
			buttonopen: function() {
				if ( ! tp.cells.advanced_editor.prompt_shown ) {
					if ( ! confirm( tablepress_strings.advanced_editor_open ) ) {
						return;
					}
				}

				tp.cells.advanced_editor.prompt_shown = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var $advanced_editor = $id( 'advanced-editor-content' );
					tp.cells.$textarea = $(this).blur();
					$advanced_editor.val( tp.cells.$textarea.val() );
					$id( 'advanced-editor' ).wpdialog( 'open' );
					$advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
					$advanced_editor.focus();
				} );
			},
			save: function() {
				var $ve_content = $id( 'advanced-editor-content' ).blur().val();
				if ( tp.cells.$textarea.val() !== $ve_content ) {
					tp.cells.$textarea.val( $ve_content );
					// position cursor at the end
					tp.cells.$textarea.get(0).selectionStart = tp.cells.$textarea.get(0).selectionEnd = tp.cells.$textarea.val().length;
					tp.table.set_table_changed();
				}
				tp.cells.$textarea.focus();
				tp.cells.advanced_editor.close();
			},
			close: function() {
				$id( 'advanced-editor' ).wpdialog( 'close' );
				return false;
			}
		},
		checkboxes: {
			last_clicked: { '#edit-form-body' : false, '#edit-form-foot' : false },
			multi_select: function ( event ) {
				if ( 'undefined' === event.shiftKey ) {
					return true;
				}

				if ( event.shiftKey ) {
					if ( ! tp.cells.checkboxes.last_clicked[ event.data.parent ] ) {
						return true;
					}

					var $checkboxes = $( event.data.parent ).find( ':checkbox' ),
						first_cb = $checkboxes.index( tp.cells.checkboxes.last_clicked[ event.data.parent ] ),
						last_cb = $checkboxes.index( this );
					if ( first_cb !== last_cb ) {
						$checkboxes.slice( Math.min( first_cb, last_cb ), Math.max( first_cb, last_cb ) ).prop( 'checked', $(this).prop( 'checked' ) );
					}
				}
				tp.cells.checkboxes.last_clicked[ event.data.parent ] = this;
				return true;
			}
		}
	};

	tp.content = {
		link: {
			prompt_shown: false,
			add: function( /* event */ ) {
				if ( ! tp.content.link.prompt_shown ) {
					if ( ! confirm( tablepress_strings.link_add ) ) {
						return;
					}
				}

				tp.content.link.prompt_shown = true;
				// mousedown instead of click to allow selection of text
				// mousedown will set the desired target textarea, and mouseup anywhere will show the link box
				// other approaches can lead to the wrong textarea being selected
				$id( 'edit-form-body' ).one( 'mousedown', 'textarea', function() {
					var editor_id = this.id;
					$( document ).one( 'mouseup', function() {
						if ( typeof wpLink !== 'undefined' ) {
							wpLink.open( editor_id );
							tp.table.set_table_changed();
						}
					} );
				} );
			}
		},
		image: {
			prompt_shown: false,
			add: function( /* event */ ) {
				if ( ! tp.content.image.prompt_shown ) {
					if ( ! confirm( tablepress_strings.image_add ) ) {
						return;
					}
				}

				tp.content.image.prompt_shown = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var editor = this.id,
						options = {
							frame: 'post',
							state: 'insert',
							title: wp.media.view.l10n.addMedia,
							multiple: true
						};

					// Move caret to the end, to prevent inserting right between existing text, as that's ugly in small cells (though possible in the Advanced Editor and Insert Link dialog).
					this.selectionStart = this.selectionEnd = this.value.length;

					// Remove focus from the textarea to prevent Opera from showing the outline of the textarea above the modal.
					// See: WP Core #22445
					$(this).blur();

					wp.media.editor.open( editor, options );
					tp.table.set_table_changed();
				} );
			}
		},
		span: {
			prompt_shown: false,
			add: function( span ) {
				var span_add_msg = ( '#rowspan#' === span ) ? tablepress_strings.rowspan_add : tablepress_strings.colspan_add;

				// init object, due to string keys
				if ( false === tp.content.span.prompt_shown ) {
					tp.content.span.prompt_shown = {};
					tp.content.span.prompt_shown['#rowspan#'] = tp.content.span.prompt_shown['#colspan#'] = false;
				}

				// Automatically deactivate DataTables, if cells are combined
				if ( $id( 'option-use-datatables' ).prop( 'checked' ) ) {
					if ( confirm( tablepress_strings.span_add_datatables_warning ) ) {
						$id( 'option-use-datatables' ).prop( 'checked', false ).change();
					} else {
						return;
					}
				}

				if ( ! tp.content.span.prompt_shown[ span ] ) {
					if ( ! confirm( span_add_msg ) ) {
						return;
					}
				}

				tp.content.span.prompt_shown[ span ] = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var $textarea = $(this),
						col_idx = $textarea.parent().index(),
						row_idx = $textarea.closest( 'tr' ).index();
					if ( '#rowspan#' === span ) {
						if ( 0 === row_idx ) {
							alert( tablepress_strings.no_rowspan_first_row );
							return;
						}
						if ( tp.table.head && 1 === row_idx ) {
							alert( tablepress_strings.no_rowspan_table_head );
							return;
						}
						if ( tp.table.foot && ( tp.table.rows - 1 ) === row_idx ) {
							alert( tablepress_strings.no_rowspan_table_foot );
							return;
						}
					} else if ( ( '#colspan#' === span ) && ( tp.table.no_data_columns_pre === col_idx ) ) {
						alert( tablepress_strings.no_colspan_first_col );
						return;
					}
					$textarea.val( span );
					tp.table.set_table_changed();
				} );
			}
		}
	};

	tp.check = {
		table_id: function( event ) {
			if ( ( 37 === event.which ) || ( 39 === event.which ) ) {
				return;
			}
			var $input = $(this);
			$input.val( $input.val().replace( /[^0-9a-zA-Z-_]/g, '' ) );
		},
		changes_saved: function() {
			if ( tp.made_changes ) {
				return tablepress_strings.unsaved_changes_unload;
			}
		}
	};

	tp.reindex = function() {
		var $row,
			$rows = $id( 'edit-form-body' ).children(),
			$cell, known_references = {};

		tp.table.rows = $rows.length;
		if ( tp.table.rows > 0 ) {
			tp.table.columns = $rows.first().children().length - tp.table.no_data_columns_pre - tp.table.no_data_columns_post;
		} else {
			tp.table.columns = 0;
		}

		$rows
		.each( function( row_idx, row ) {
			$row = $( row );
			$row.find( 'textarea' )
				.val( function( column_idx, value ) {
					// If the cell is not a formula, there's nothing to do here
					if ( ( '' === value ) || ( '=' !== value.charAt(0) ) ) {
						return value;
					}

					return value.replace( /([A-Z]+[0-9]+)(?::([A-Z]+[0-9]+))?/g, function( full_match, first_cell, second_cell ) {
						// first_cell must always exist, while second_cell only exists in ranges like A4:B7
						// we will use full_match as our result variable, so that we don't need an extra one

						if ( ! known_references.hasOwnProperty( first_cell ) ) {
							$cell = $id( 'cell-' + first_cell );
							if ( $cell.length ) {
								known_references[ first_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
							} else {
								known_references[ first_cell ] = first_cell;
							}
						}
						full_match = known_references[ first_cell ];

						if ( ( 'undefined' !== typeof second_cell ) && ( '' !== second_cell ) ) { // Chrome and IE pass an undefined variable, while Firefox passes an empty string
							if ( ! known_references.hasOwnProperty( second_cell ) ) {
								$cell = $id( 'cell-' + second_cell );
								if ( $cell.length ) {
									known_references[ second_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
								} else {
									known_references[ second_cell ] = second_cell;
								}
							}
							full_match += ':' + known_references[ second_cell ];
						}

						return full_match;
					} );
				} )
				.attr( 'name', function( column_idx /*, old_name */ ) {
					return 'table[data][' + row_idx + '][' + column_idx + ']';
				} );

			$row.find( '.move-handle' ).html( row_idx + 1 );
		} )
		.each( function( row_idx, row ) { // need a second loop here to not break logic in previous loop, that queries textareas by their old ID
			$( row ).find( 'textarea' ).attr( 'id', function( column_idx /*, old_id */ ) {
				return 'cell-' + tp.columns.number_to_letter( column_idx + 1 ) + ( row_idx + 1 );
			} );
		});
		$id( 'edit-form-head' ).find( '.move-handle' )
			.html( function( idx ) { return tp.columns.number_to_letter( idx + 1 ); } );

		$id( 'number-rows' ).val( tp.table.rows );
		$id( 'number-columns' ).val( tp.table.columns );

		tp.table.set_table_changed();
	};

	tp.save_changes = {
		trigger: function( event ) {
			// validation checks
			if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
				alert( tablepress_strings.num_pagination_entries_invalid );
				$id( 'option-datatables-paginate_entries' ).focus().select();
				return;
			}
			if ( ( /[^A-Za-z0-9- _]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
				alert( tablepress_strings.extra_css_classes_invalid );
				$id( 'option-extra-css-classes' ).focus().select();
				return;
			}

			if ( event.shiftKey ) {
				tp.made_changes = false; // to prevent onunload warning
				$id( 'tablepress-page' ).find( 'form' ).submit();
				return;
			}

			$(this).closest( 'p' ).append( '<span class="animation-saving spinner is-active" title="' + tablepress_strings.saving_changes + '"/>' );
			$( '.save-changes-button' ).prop( 'disabled', true );
			$( 'body' ).addClass( 'wait' );

			$.ajax({
				'type': 'POST',
				'url': ajaxurl,
				'data': tp.table.prepare_ajax_request( 'tablepress_save_table', '#nonce-edit-table' ),
				'success': tp.save_changes.ajax_success,
				'error': tp.save_changes.ajax_error,
				'dataType': 'json'
			} );
		},
		ajax_success: function( data, status /*, jqXHR */ ) {
			if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
				tp.save_changes.error( 'AJAX call successful, but unclear status. Try again while holding down the &#8220;Shift&#8221; key.' );
			} else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) ) {
				tp.save_changes.error( 'AJAX call successful, but unclear data. Try again while holding down the &#8220;Shift&#8221; key.' );
			} else if ( true !== data.success ) {
				var debug_html = '';
				// Print debug information, if we are in debug mode
				if ( ( 'undefined' !== typeof data.error_details ) && ( tablepress_options.print_debug_output ) ) {
					debug_html = '</p><p>These errors were encountered:</p><pre>' + data.error_details + '</pre><p>'; // Some HTML magic because this is wrapped in <p> when printed
				}
				tp.save_changes.error( 'AJAX call successful, internal saving process failed. Try again while holding down the &#8220;Shift&#8221; key.' + debug_html );
			} else {
				tp.save_changes.success( data );
			}
		},
		ajax_error: function( jqXHR, status, error_thrown ) {
			tp.save_changes.error( 'AJAX call failed: ' + status + ' - ' + error_thrown + '. Try again while holding down the &#8220;Shift&#8221; key.' );
		},
		success: function( data ) {
			// saving was successful, so the original ID has changed to the (maybe) new ID -> we need to adjust all occurrences
			if ( tp.table.id !== data.table_id ) {
				// update URL (for HTML5 browsers only), but only if ID really changed, to not get dummy entries in the browser history
				if ( ( 'pushState' in window.history ) && null !== window.history.pushState ) {
					window.history.pushState( '', '', window.location.href.replace( /table_id=[0-9a-zA-Z-_]+/gi, 'table_id=' + data.table_id ) );
				}
			}
			// update CSS class for data field form
			$id( 'edit-form' ).removeClass( 'tablepress-edit-screen-id-' + tp.table.id ).addClass( 'tablepress-edit-screen-id-' + data.table_id );
			// update table ID in input fields (type text and hidden)
			tp.table.id = tp.table.new_id = data.table_id;
			$id( 'table-id' ).val( tp.table.id );
			$id( 'table-new-id' ).val( tp.table.new_id );
			// update the Shortcode text field
			$( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' );
			// update the nonces
			$id( 'nonce-edit-table' ).val( data.new_edit_nonce );
			$id( 'nonce-preview-table' ).val( data.new_preview_nonce );
			// update URLs in Preview links
			var $show_preview_buttons = $( '.show-preview-button' );
			if ( $show_preview_buttons.length ) { // check necessary, because Preview button might not be visible
				$show_preview_buttons.attr( 'href',
					$show_preview_buttons.first().attr( 'href' )
						.replace( /item=[a-zA-Z0-9_-]+/g, 'item=' + data.table_id )
						.replace( /&_wpnonce=[a-z0-9]+/ig, '&_wpnonce=' + data.new_preview_nonce )
				);
			}
			// update last modified date and user nickname
			$id( 'last-modified' ).text( data.last_modified );
			$id( 'last-editor' ).text( data.last_editor );
			tp.table.unset_table_changed();
			tp.save_changes.after_saving_dialog( 'success', tablepress_strings[ data.message ] );
		},
		error: function( message ) {
			tp.save_changes.after_saving_dialog( 'error', message );
		},
		after_saving_dialog: function( type, message ) {
			if ( 'undefined' === typeof message ) {
				message = '';
			} else {
				message = ': ' + message;
			}
			var delay,
				div_class = 'save-changes-' + type;
			if ( 'success' === type ) {
				div_class += ' notice notice-success';
				delay = 3000;
			} else {
				div_class += ' notice notice-error';
				delay = 6000;
			}
			$( '.animation-saving' ).closest( 'p' )
				.after( '<div class="ajax-alert ' + div_class + '"><p>' + tablepress_strings['save_changes_' + type] + message + '</p></div>' );
			$( '.animation-saving' ).remove();
			$( '.save-changes-' + type ).delay( delay ).fadeOut( 2000, function() { $(this).remove(); } );
			$( '.save-changes-button' ).prop( 'disabled', false );
			$( 'body' ).removeClass( 'wait' );
		}
	};

	tp.init = function() {
		var callbacks = {
			'click': {
				'#rows-insert':			tp.rows.insert,
				'#columns-insert':		tp.columns.insert,
				'#rows-duplicate':		tp.rows.duplicate,
				'#columns-duplicate':	tp.columns.duplicate,
				'#rows-remove':			tp.rows.remove,
				'#columns-remove':		tp.columns.remove,
				'#rows-hide':			tp.rows.hide,
				'#columns-hide':		tp.columns.hide,
				'#rows-unhide':			tp.rows.unhide,
				'#columns-unhide':		tp.columns.unhide,
				'#rows-append':			tp.rows.append,
				'#columns-append':		tp.columns.append,
				'#link-add':			tp.content.link.add,
				'#image-add':			tp.content.image.add,
				'#span-add-rowspan':	function() { tp.content.span.add( '#rowspan#' ); },
				'#span-add-colspan':	function() { tp.content.span.add( '#colspan#' ); },
				'.show-preview-button': tp.table.preview.trigger,
				'.save-changes-button': tp.save_changes.trigger,
				'.show-help-box':		function() {
					$(this).next().wpdialog( {
						title: $(this).attr( 'title' ),
						height: 470,
						width: 320,
						modal: true,
						dialogClass: 'wp-dialog',
						resizable: false
					} );
				}
			},
			'keyup': {
				'#table-new-id':		tp.check.table_id
			},
			'change': {
				'#option-table-head':			tp.table.change_table_head,
				'#option-table-foot':			tp.table.change_table_foot,
				'#option-use-datatables':		tp.table.change_datatables,
				'#option-datatables-paginate':	tp.table.change_datatables_pagination
			},
			'blur': {
				'#table-new-id':		tp.table.change_id	// onchange would not recognize changed values from tp.check.table_id
			}
		},
		$table = $id( 'edit-form-body' );

		$.each( callbacks, function( event, event_callbacks ) {
			$.each( event_callbacks, function( selector, callback ) {
				$( selector ).on( event, callback );
			} );
		} );

		$( window ).on( 'beforeunload', tp.check.changes_saved );

		// do this before the next lines, to not trigger set_table_changed()
		$id( 'option-table-head' ).change(); // init changed/disabled states of DataTables JS features checkboxes
		$id( 'option-print-name' ).change( tp.table.change_print_name_description ).change(); // init dropdowns for name and description position
		$id( 'option-print-description' ).change( tp.table.change_print_name_description ).change();

		// just once is enough, will be reset after saving
		$table.one( 'change', 'textarea', tp.table.set_table_changed );
		$( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );

		if ( tablepress_options.cells_advanced_editor ) {
			$table.on( 'click', 'textarea', tp.cells.advanced_editor.keyopen );
			$id( 'advanced-editor-open' ).on( 'click', tp.cells.advanced_editor.buttonopen );
			$id( 'advanced-editor-confirm' ).on( 'click', tp.cells.advanced_editor.save );
			$id( 'advanced-editor-cancel' ).on( 'click', tp.cells.advanced_editor.close );
			$id( 'advanced-editor' ).wpdialog( {
				autoOpen: false,
				title: $id( 'advanced-editor-open' ).val(),
				width: 600,
				modal: true,
				dialogClass: 'wp-dialog',
				resizable: false
			} );
			// Fix issue with input fields not being usable (they are immediately losing focus without this) in the wpLink dialog when called through the "Advanced Editor"
			$id( 'wp-link' ).on( 'focus', 'input', function( event ) {
				event.stopPropagation();
			} );
		} else {
			$id( 'advanced-editor-open' ).hide();
		}

		// Fix issue with input fields not being usable (they are immediately losing focus without this) in the sidebar of the new Media Manager
		$( 'body' ).on( 'focus', '.media-modal .media-frame-content input, .media-modal .media-frame-content textarea', function( event ) {
			event.stopPropagation();
		} );

		if ( tablepress_options.cells_auto_grow ) {
			$table.on( 'focus', 'textarea', tp.cells.autogrow );
		}

		$id( 'edit-form-body' ).on( 'click', 'input:checkbox', { parent: '#edit-form-body' }, tp.cells.checkboxes.multi_select );
		$id( 'edit-form-foot' ).on( 'click', 'input:checkbox', { parent: '#edit-form-foot' }, tp.cells.checkboxes.multi_select );

		$id( 'edit-form-head' ).on( 'click', '.sort-control', tp.rows.sort );

		// on form submit: Enable disabled fields, so that they are transmitted in the POST request
		$id( 'tablepress-page' ).find( 'form' ).on( 'submit', function() {
			$(this).find( '.tablepress-postbox-table' ).find( 'input, select' ).prop( 'disabled', false );
		} );

		$table.sortable( {
			axis: 'y',
			containment: $id( 'edit-form' ), // to get better behavior when dragging before/after the first/last row
			forceHelperSize: true, // necessary?
			handle: '.move-handle',
			start: tp.rows.move.start,
			change: tp.rows.move.change,
			stop: tp.rows.move.stop,
			update: tp.reindex
		} ); // disableSelection() prohibits selection of text in textareas via keyboard

		$id( 'edit-form-head' ).sortable( {
			axis: 'x',
			items: '.head',
			containment: 'parent',
			forceHelperSize: true, // necessary?
			helper: 'clone',
			handle: '.move-handle',
			start: tp.columns.move.start,
			stop: tp.columns.move.stop,
			change: tp.columns.move.change,
			sort: tp.columns.move.sort
		} ).disableSelection();
	};

	// Run TablePress initialization.
	tp.init();

} );
home/xbodynamge/www/wp-content/plugins/tablepress/admin/js/edit.js000064400000135321151123327610021375 0ustar00/**
 * JavaScript code for the "Edit" screen
 *
 * @package TablePress
 * @subpackage Views JavaScript
 * @author Tobias Bäthge
 * @since 1.0.0
 */

/* global alert, confirm, tp, tablepress_strings, tablepress_options, ajaxurl, wpLink, tb_show, wp, JSON */

// Ensure the global `tp` object exists.
window.tp = window.tp || {};

jQuery( document ).ready( function( $ ) {

	'use strict';

	/* Wrapper to find elements in the page faster with JS-native functions */
	var $id = function( element_id ) {
		return $( document.getElementById( element_id ) );
	};

	/**
	 * TablePress object, mostly with functionality for the "Edit" screen
	 *
	 * @since 1.0.0
	 */
	tp.made_changes = false;

	tp.table = {
		id: $id( 'table-id' ).val(),
		new_id: $id( 'table-new-id' ).val(),
		rows: parseInt( $id( 'number-rows' ).val(), 10 ),
		columns: parseInt( $id( 'number-columns' ).val(), 10 ),
		head: $id( 'option-table-head' ).prop( 'checked' ),
		foot: $id( 'option-table-foot' ).prop( 'checked' ),
		no_data_columns_pre: 2,
		no_data_columns_post: 1,
		body_cells_pre: '<tr><td><span class="move-handle"></span></td><td><input type="checkbox" /><input type="hidden" class="visibility" name="table[visibility][rows][]" value="1" /></td>',
		body_cells_post: '<td><span class="move-handle"></span></td></tr>',
		body_cell: '<td><textarea rows="1"></textarea></td>',
		head_cell: '<th class="head"><span class="sort-control sort-desc" title="' + tablepress_strings.sort_desc + '"><span class="sorting-indicator"></span></span><span class="sort-control sort-asc" title="' + tablepress_strings.sort_asc + '"><span class="sorting-indicator"></span></span><span class="move-handle"></span></th>',
		foot_cell: '<th><input type="checkbox" /><input type="hidden" class="visibility" name="table[visibility][columns][]" value="1" /></th>',
		set_table_changed: function() {
			tp.made_changes = true;
		},
		unset_table_changed: function() {
			tp.made_changes = false;
			$id( 'edit-form-body' ).one( 'change', 'textarea', tp.table.set_table_changed );
			// @TODO: maybe use .tablepress-postbox-table here and further below
			$( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );
		},
		change_id: function( /* event */ ) {
			// empty table IDs are not allowed
			if ( '' === $.trim( $id( 'table-new-id' ).val() ) ) {
				alert( tablepress_strings.table_id_not_empty );
				$id( 'table-new-id' ).val( tp.table.new_id ).focus().select();
				return;
			}
			// the '0' table ID is not allowed
			if ( '0' === $.trim( $id( 'table-new-id' ).val() ) ) {
				alert( tablepress_strings.table_id_not_zero );
				$id( 'table-new-id' ).val( tp.table.new_id ).focus().select();
				return;
			}

			if ( this.value === tp.table.new_id ) {
				return;
			}

			if ( confirm( tablepress_strings.ays_change_table_id ) ) {
				tp.table.new_id = this.value;
				$( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' ).click(); // click() to focus and select
				tp.table.set_table_changed();
			} else {
				$(this).val( tp.table.new_id );
			}
		},
		change_table_head: function( /* event */ ) {
			tp.table.head = $(this).prop( 'checked' );
			$id( 'option-use-datatables' ).prop( 'disabled', ! tp.table.head ).change();
			$id( 'notice-datatables-head-row' ).toggle( ! tp.table.head );
			tp.rows.stripe();
		},
		change_table_foot: function( /* event */ ) {
			tp.table.foot = $(this).prop( 'checked' );
			tp.rows.stripe();
		},
		change_print_name_description: function( /* event */ ) {
			$id( this.id + '-position' ).prop( 'disabled', ! $(this).prop( 'checked' ) );
		},
		change_datatables: function() {
			var $datatables_checkbox = $id( 'option-use-datatables' ),
				checkboxes_disabled = ! ( $datatables_checkbox.prop( 'checked' ) && ! $datatables_checkbox.prop( 'disabled' ) );
			$datatables_checkbox.closest( 'tbody' ).find( 'input' ).not( $datatables_checkbox ).prop( 'disabled', checkboxes_disabled );
			tp.table.change_datatables_pagination();
		},
		change_datatables_pagination: function() {
			var $pagination_checkbox = $id( 'option-datatables-paginate' ),
				pagination_enabled = ( $pagination_checkbox.prop( 'checked' ) && ! $pagination_checkbox.prop( 'disabled' ) );
			$id( 'option-datatables-lengthchange' ).prop( 'disabled', ! pagination_enabled );
			$id( 'option-datatables-paginate_entries' ).prop( 'disabled', ! pagination_enabled );
		},
		prepare_ajax_request: function( wp_action, wp_nonce ) {
			var $table_body = $id( 'edit-form-body' ),
				table_data = [],
				table_options,
				table_number = { rows: tp.table.rows, columns: tp.table.columns, hidden_rows: 0, hidden_columns: 0 },
				table_visibility = { rows: [], columns: [] };

			$table_body.children().each( function( idx, row ) {
				table_data[ idx ] = $( row ).find( 'textarea' )
					.map( function() {
						return $(this).val();
					} )
					.get();
			} );
			table_data = JSON.stringify( table_data );

			// @TODO: maybe for options saving: https://stackoverflow.com/questions/1184624/convert-form-data-to-javascript-object-with-jquery
			// or each()-loop through all checkboxes/textfields/selects
			table_options = {
				// Table Options
				table_head: tp.table.head,
				table_foot: tp.table.foot,
				alternating_row_colors: $id( 'option-alternating-row-colors' ).prop( 'checked' ),
				row_hover: $id( 'option-row-hover' ).prop( 'checked' ),
				print_name: $id( 'option-print-name' ).prop( 'checked' ),
				print_description: $id( 'option-print-description' ).prop( 'checked' ),
				print_name_position: $id( 'option-print-name-position' ).val(),
				print_description_position: $id( 'option-print-description-position' ).val(),
				extra_css_classes: $id( 'option-extra-css-classes' ).val(),
				// DataTables JS features
				use_datatables: $id( 'option-use-datatables' ).prop( 'checked' ),
				datatables_sort: $id( 'option-datatables-sort' ).prop( 'checked' ),
				datatables_filter: $id( 'option-datatables-filter' ).prop( 'checked' ),
				datatables_paginate: $id( 'option-datatables-paginate' ).prop( 'checked' ),
				datatables_lengthchange: $id( 'option-datatables-lengthchange' ).prop( 'checked' ),
				datatables_paginate_entries: $id( 'option-datatables-paginate_entries' ).val(),
				datatables_info: $id( 'option-datatables-info' ).prop( 'checked' ),
				datatables_scrollx: $id( 'option-datatables-scrollx' ).prop( 'checked' ),
				datatables_custom_commands: $id( 'option-datatables-custom-commands' ).val()
			};
			table_options = JSON.stringify( table_options );

			table_visibility.rows = $table_body.find( 'input[type="hidden"]' )
				.map( function() {
					if ( '1' === $(this).val() ) {
						return 1;
					}
					table_number.hidden_rows += 1;
					return 0;
				} )
				.get();
			table_visibility.columns = $id( 'edit-form-foot' ).find( 'input[type="hidden"]' )
				.map( function() {
					if ( '1' === $(this).val() ) {
						return 1;
					}
					table_number.hidden_columns += 1;
					return 0;
				} )
				.get();
			table_visibility = JSON.stringify( table_visibility );

			// request_data =
			return {
				action: wp_action,
				_ajax_nonce : $( wp_nonce ).val(),
				tablepress: {
					id: tp.table.id,
					new_id: tp.table.new_id,
					name: $id( 'table-name' ).val(),
					description: $id( 'table-description' ).val(),
					number: table_number,
					data: table_data,
					options: table_options,
					visibility: table_visibility
				}
			};
		},
		preview: {
			trigger: function( /* event */ ) {
				if ( ! tp.made_changes ) {
					tp.table.preview.show( $(this).attr( 'href' ) + '&TB_iframe=true' );
					return false;
				}

				// validation checks
				if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
					alert( tablepress_strings.num_pagination_entries_invalid );
					$id( 'option-datatables-paginate_entries' ).focus().select();
					return;
				}
				if ( ( /[^A-Za-z0-9- _]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
					alert( tablepress_strings.extra_css_classes_invalid );
					$id( 'option-extra-css-classes' ).focus().select();
					return;
				}

				$(this).closest( 'p' ).append( '<span class="animation-preview spinner is-active" title="' + tablepress_strings.preparing_preview + '"/>' );
				$( 'body' ).addClass( 'wait' );
				$id( 'table-preview' ).empty(); // clear preview

				$.ajax({
					'type': 'POST',
					'url': ajaxurl,
					'data': tp.table.prepare_ajax_request( 'tablepress_preview_table', '#nonce-preview-table' ),
					'success': tp.table.preview.ajax_success,
					'error': tp.table.preview.ajax_error,
					'dataType': 'json'
				} );

				return false;
			},
			ajax_success: function( data, status /*, jqXHR */ ) {
				if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
					tp.table.preview.error( 'AJAX call successful, but unclear status.' );
				} else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) || ( true !== data.success ) ) {
					tp.table.preview.error( 'AJAX call successful, but unclear data.' );
				} else {
					tp.table.preview.success( data );
				}
			},
			ajax_error: function( jqXHR, status, error_thrown ) {
				tp.table.preview.error( 'AJAX call failed: ' + status + ' - ' + error_thrown );
			},
			success: function( data ) {
				$id( 'table-preview' ).empty();
				$( '<iframe id="table-preview-iframe" />' ).load( function() {
					var $iframe = $(this).contents();
					$iframe.find( 'head' ).append( data.head_html );
					$iframe.find( 'body' ).append( data.body_html );
				} ).appendTo( '#table-preview' );
				$( '.animation-preview' ).remove();
				$( 'body' ).removeClass( 'wait' );
				tp.table.preview.show( '#TB_inline?inlineId=preview-container' );
			},
			error: function( message ) {
				$( '.animation-preview' ).closest( 'p' )
					.after( '<div class="ajax-alert preview-error error"><p>' + tablepress_strings.preview_error + ': ' + message + '</p></div>' );
				$( '.animation-preview' ).remove();
				$( '.preview-error' ).delay( 6000 ).fadeOut( 2000, function() { $(this).remove(); } );
				$( 'body' ).removeClass( 'wait' );
			},
			show: function( url ) {
				var width = $( window ).width() - 120,
					height = $( window ).height() - 120;
				if ( $( '#wpadminbar' ).length ) {
					height -= parseInt( $( '#wpadminbar' ).css( 'height' ), 10 );
				}
				tb_show( $( '.show-preview-button' ).first().text(), url + '&height=' + height + '&width=' + width, false );
			}
		}
	};

	tp.rows = {
		create: function( num_rows ) {
			var i, j,
				column_idxs,
				new_rows = '';

			for ( i = 0; i < num_rows; i++ ) {
				new_rows += tp.table.body_cells_pre;
				for ( j = 0; j < tp.table.columns; j++ ) {
					new_rows += tp.table.body_cell;
				}
				new_rows += tp.table.body_cells_post;
			}

			column_idxs = $id( 'edit-form-foot' ).find( '.column-hidden' )
				.map( function() { return $(this).index(); } ).get();
			return $( new_rows ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.addClass( 'column-hidden' );
			} );
		},
		append: function( /* event */ ) {
			var num_rows = $id( 'rows-append-number' ).val();

			if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_rows ) ) {
				alert( tablepress_strings.append_num_rows_invalid );
				$id( 'rows-append-number' ).focus().select();
				return;
			}

			$id( 'edit-form-body' ).append( tp.rows.create( num_rows ) );

			tp.rows.stripe();
			tp.reindex();
		},
		insert: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.before( tp.rows.create( 1 ) );

			tp.rows.stripe();
			tp.reindex();
		},
		duplicate: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.each( function( idx, row ) {
				var $row = $( row ),
					$textareas = $row.find( 'textarea' ),
					$duplicated_row = $row.clone();
				$duplicated_row.find( 'textarea' ).removeAttr( 'id' ).each( function( idx, cell ) {
					$( cell ).val( $textareas.eq( idx ).val() ); // setting val() is necessary, as clone() doesn't copy the current value, see jQuery bugs 5524, 2285, 3016
				} );
				$row.after( $duplicated_row );
			} );

			tp.rows.stripe();
			tp.reindex();
		},
		hide: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.addClass( 'row-hidden' ).find( '.visibility' ).val( '0' );

			tp.rows.stripe();
			tp.table.set_table_changed();
		},
		unhide: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.removeClass( 'row-hidden' ).find( '.visibility' ).val( '1' );

			tp.rows.stripe();
			tp.table.set_table_changed();
		},
		remove: function( /* event */ ) {
			var confirm_message,
				$selected_rows = $id( 'edit-form-body' ).find( 'input:checked' ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			if ( tp.table.rows === $selected_rows.length ) {
				alert( tablepress_strings.no_remove_all_rows );
				return;
			}

			if ( 1 === $selected_rows.length ) {
				confirm_message = tablepress_strings.ays_remove_rows_singular;
			} else {
				confirm_message = tablepress_strings.ays_remove_rows_plural;
			}
			if ( ! confirm( confirm_message ) ) {
				return;
			}

			$selected_rows.remove();

			tp.rows.stripe();
			tp.reindex();
		},
		move: {
			start: function( event, ui ) {
				$( ui.placeholder ).removeClass( 'row-hidden' ).css( 'visibility', 'visible' )
					.html( '<td colspan="' + ( tp.table.columns + tp.table.no_data_columns_pre + tp.table.no_data_columns_post ) + '"><div/></td>' );
				$( ui.helper ).removeClass( 'odd head-row foot-row' );
			},
			change: function( event, ui ) {
				tp.rows.stripe( ui.helper );
			},
			stop: function( /* event, ui */ ) {
				tp.rows.stripe();
			}
		},
		sort: function() {
			var column_idx = $(this).parent().index(),
				direction = ( $(this).hasClass( 'sort-asc' ) ) ? 1 : -1,
				$table_body = $('#edit-form-body'),
				$head_rows = $table_body.find( '.head-row' ).prevAll().addBack(),
				$foot_rows = $table_body.find( '.foot-row' ).nextAll().addBack(),
				rows = $table_body.children().not( $head_rows ).not( $foot_rows ).get(),
				/*
				 * Natural Sort algorithm for Javascript - Version 0.8.1 - Released under MIT license
				 * Author: Jim Palmer (based on chunking idea from Dave Koelle)
				 * See: https://github.com/overset/javascript-natural-sort and http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm-with-unicode-support/
				 */
				natural_sort = function( a, b ) {
					var re = /(^([+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|^0x[\da-fA-F]+$|\d+)/g,
						sre = /^\s+|\s+$/g,   // trim pre-post whitespace
						snre = /\s+/g,        // normalize all whitespace to single ' ' character
						dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
						hre = /^0x[0-9a-f]+$/i,
						ore = /^0/,
						// strip whitespace
						x = a.replace(sre, '') || '',
						y = b.replace(sre, '') || '',
						// chunk/tokenize
						xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
						yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
						// numeric, hex or date detection
						xD = parseInt(x.match(hre), 16) || (xN.length !== 1 && Date.parse(x)),
						yD = parseInt(y.match(hre), 16) || xD && y.match(dre) && Date.parse(y) || null,
						normChunk = function(s, l) {
							// normalize spaces; find floats not starting with '0', string or 0 if not defined (Clint Priest)
							return (!s.match(ore) || l === 1) && parseFloat(s) || s.replace(snre, ' ').replace(sre, '') || 0;
						},
						oFxNcL, oFyNcL;
					// first try and sort Hex codes or Dates
					if (yD) {
						if (xD < yD) { return -1; }
						else if (xD > yD) { return 1; }
					}
					// natural sorting through split numeric strings and default strings
					for(var cLoc = 0, xNl = xN.length, yNl = yN.length, numS = Math.max(xNl, yNl); cLoc < numS; cLoc++) {
						oFxNcL = normChunk(xN[cLoc] || '', xNl);
						oFyNcL = normChunk(yN[cLoc] || '', yNl);
						// handle numeric vs string comparison - number < string - (Kyle Adams)
						if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
							return isNaN(oFxNcL) ? 1 : -1;
						}
						// if unicode use locale comparison
						if (/[^\x00-\x80]/.test(oFxNcL + oFyNcL) && oFxNcL.localeCompare) {
							var comp = oFxNcL.localeCompare(oFyNcL);
							return comp / Math.abs(comp);
						}
						if (oFxNcL < oFyNcL) { return -1; }
						else if (oFxNcL > oFyNcL) { return 1; }
					}
				};

			$.each( rows, function( row_idx, row ) {
				row.sort_key = ( '' + $( row ).children().eq( column_idx ).find( 'textarea' ).val() ).toLowerCase(); // convert to string, and lower case for case insensitive sorting
			} );

			rows.sort( function( a, b ) {
				return direction * natural_sort( a.sort_key, b.sort_key );
			} );

			// might not be necessary:
			$.each( rows, function( row_idx, row ) {
				row.sort_key = null;
			} );

			$table_body.append( $head_rows );
			$table_body.append( rows );
			$table_body.append( $foot_rows );

			tp.rows.stripe();
			tp.reindex();
		},
		stripe: function( helper ) {
			if ( 'undefined' === typeof helper ) {
				helper = null;
			}
			helper = $( helper );
			var $rows = $id( 'edit-form-body' ).children().removeClass( 'odd head-row foot-row' ).not( helper );
			$rows.filter( ':even' ).addClass( 'odd' );
			$rows = $rows.not( '.row-hidden' );
			if ( helper.hasClass( 'row-hidden' ) ) {
				$rows = $rows.not( '.ui-sortable-placeholder' );
			}
			if ( tp.table.head ) {
				$rows.first().addClass( 'head-row' );
			}
			if ( tp.table.foot ) {
				$rows.last().addClass( 'foot-row' );
			}
		}
	};

	tp.columns = {
		append: function( /* event */ ) {
			var i,
				num_columns = $id( 'columns-append-number' ).val(),
				new_head_cells = '', new_body_cells = '', new_foot_cells = '';

			if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_columns ) ) {
				alert( tablepress_strings.append_num_columns_invalid );
				$id( 'columns-append-number' ).focus().select();
				return;
			}

			for ( i = 0; i < num_columns; i++ ) {
				new_body_cells += tp.table.body_cell;
				new_head_cells += tp.table.head_cell;
				new_foot_cells += tp.table.foot_cell;
			}

			$id( 'edit-form-body' ).children().each( function( row_idx, row ) {
				$( row ).children().slice( - tp.table.no_data_columns_post )
					.before( new_body_cells );
			} );
			$id( 'edit-form-head' ).children().slice( - tp.table.no_data_columns_post )
				.before( new_head_cells );
			$id( 'edit-form-foot' ).children().slice( - tp.table.no_data_columns_post )
				.before( new_foot_cells );

			tp.reindex();
		},
		insert: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.before( tp.table.body_cell );
			} );
			$id( 'edit-form-head' ).children()
				.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
				.before( tp.table.head_cell );
			$selected_columns.before( tp.table.foot_cell );

			tp.reindex();
		},
		duplicate: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form' ).find( 'tr' ).each( function( row_idx, row ) {
				$( row ).children().each( function( idx, cell ) {
					if ( -1 !== $.inArray( idx, column_idxs ) ) {
						var $cell = $( cell ),
							$duplicated_cell = $cell.clone();
							$duplicated_cell.find( 'textarea' ).removeAttr( 'id' ).val( $cell.find( 'textarea' ).val() ); // setting val() is necessary, as clone() doesn't copy the current value, see jQuery bugs 5524, 2285, 3016
						$cell.after( $duplicated_cell );
					}
				} );
			} );

			tp.reindex();
		},
		hide: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.addClass( 'column-hidden' );
			} );
			$selected_columns.addClass( 'column-hidden' ).find( '.visibility' ).val( '0' );

			tp.table.set_table_changed();
		},
		unhide: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.removeClass( 'column-hidden' );
			} );
			$selected_columns.removeClass( 'column-hidden' ).find( '.visibility' ).val( '1' );

			tp.table.set_table_changed();
		},
		remove: function( /* event */ ) {
			var column_idxs,
				confirm_message,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			if ( tp.table.columns === $selected_columns.length ) {
				alert( tablepress_strings.no_remove_all_columns );
				return;
			}

			if ( 1 === $selected_columns.length ) {
				confirm_message = tablepress_strings.ays_remove_columns_singular;
			} else {
				confirm_message = tablepress_strings.ays_remove_columns_plural;
			}
			if ( ! confirm( confirm_message ) ) {
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.remove();
			} );
			$selected_columns.remove();

			tp.reindex();
		},
		move: {
			source_idx: -1,
			target_idx: -1,
			$rows: null,
			$row_children: null,
			$cell: null,
			$cells: null,
			$placeholder: null,
			$helper: null,
			start: function( event, ui ) {
				var $item = $( ui.item ),
					column_width;

				tp.columns.move.source_idx = $item.index();

				tp.columns.move.$rows = $id( 'edit-form-body' ).children().add( '#edit-form-foot' );

				tp.columns.move.$cells = tp.columns.move.$rows
					.find( ':nth-child(' + ( tp.columns.move.source_idx + 1 ) + ')' )
					.each( function() {
						tp.columns.move.$cell = $(this);
						$( '<td class="move-placeholder"><div/></td>' ).insertBefore( tp.columns.move.$cell );
						tp.columns.move.$cell.insertAfter( tp.columns.move.$cell.nextAll().last() )
							.clone().addClass( 'move-hover' ).insertAfter( tp.columns.move.$cell )
							.find( 'textarea' ).val( tp.columns.move.$cell.find( 'textarea' ).val() );
							// last line works around problem with clone() of textareas, see jQuery bugs 5524, 2285, 3016
					} )
					.hide();

				tp.columns.move.$helper = tp.columns.move.$rows.find( '.move-hover' );
				/* // seems not to be working for rows, so disable it for columns
					.each( function() {
						tp.columns.move.$cell = $(this);
						tp.columns.move.$cell.css( 'top', ( tp.columns.move.$cell.position().top - 3 ) + 'px' );
					} );
				*/

				column_width = tp.columns.move.$helper.eq(1).width(); // eq(0) is table foot
				tp.columns.move.$helper.eq(0).width( column_width );
				tp.columns.move.$placeholder = tp.columns.move.$rows.find( '.move-placeholder' );
				tp.columns.move.$placeholder.find( 'div' ).width( column_width );
			},
			change: function( event, ui ) {
				tp.columns.move.target_idx = $( ui.placeholder ).index();

				if ( ( tp.columns.move.target_idx - tp.columns.move.source_idx ) === 1 ) {
					tp.columns.move.target_idx += 1;
				} else {
					if ( tp.columns.move.target_idx === tp.columns.move.source_idx ) {
						tp.columns.move.target_idx -= 1;
					}
				}

				tp.columns.move.$placeholder.each( function() {
					tp.columns.move.$cell = $(this);
					tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().children().eq( tp.columns.move.target_idx ) );
				} );

				if ( tp.columns.move.target_idx > tp.columns.move.source_idx ) {
					tp.columns.move.target_idx -= 1;
				}

				tp.columns.move.source_idx = tp.columns.move.target_idx;
			},
			sort: function( event, ui ) {
				tp.columns.move.$helper.css( 'left', ui.position.left );
			},
			stop: function( /* event, ui */ ) {
				tp.columns.move.$helper.remove();
				tp.columns.move.$cells
					.each( function() {
						tp.columns.move.$cell = $(this);
						tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().find( '.move-placeholder' ) );
					} )
					.show();
				tp.columns.move.$placeholder.remove();

				tp.columns.move.source_idx = tp.columns.move.target_idx = -1;
				tp.columns.move.$rows = tp.columns.move.$row_children = tp.columns.move.$cell = tp.columns.move.$cells = tp.columns.move.$placeholder = tp.columns.move.$helper = null;

				tp.reindex();
			}
		},
		number_to_letter: function( number ) {
			var column = '';
			while ( number > 0 ) {
				column = String.fromCharCode( 65 + ( ( number-1) % 26 ) ) + column;
				number = Math.floor( (number-1) / 26 );
			}
			return column;
		}/*,
		letter_to_number: function( column ) {
			column = column.toUpperCase();
			var count = column.length,
				number = 0,
				i;
			for ( i = 0; i < count; i++ ) {
				number += ( column.charCodeAt( count-1-i ) - 64 ) * Math.pow( 26, i );
			}
			return number;
		}*/
	};

	tp.cells = {
		$focus: $( null ),
		$textarea: null,
		autogrow: function( /* event */ ) {
			tp.cells.$focus.removeClass( 'focus' );
			tp.cells.$focus = $(this).closest( 'tr' ).addClass( 'focus' );
		},
		advanced_editor: {
			prompt_shown: false,
			keyopen: function( event ) {
				if ( ! event.shiftKey ) {
					return;
				}

				var $advanced_editor = $id( 'advanced-editor-content' );
				tp.cells.$textarea = $(this).blur();
				$advanced_editor.val( tp.cells.$textarea.val() );
				$id( 'advanced-editor' ).wpdialog( 'open' );
				$advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
				$advanced_editor.focus();
			},
			buttonopen: function() {
				if ( ! tp.cells.advanced_editor.prompt_shown ) {
					if ( ! confirm( tablepress_strings.advanced_editor_open ) ) {
						return;
					}
				}

				tp.cells.advanced_editor.prompt_shown = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var $advanced_editor = $id( 'advanced-editor-content' );
					tp.cells.$textarea = $(this).blur();
					$advanced_editor.val( tp.cells.$textarea.val() );
					$id( 'advanced-editor' ).wpdialog( 'open' );
					$advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
					$advanced_editor.focus();
				} );
			},
			save: function() {
				var $ve_content = $id( 'advanced-editor-content' ).blur().val();
				if ( tp.cells.$textarea.val() !== $ve_content ) {
					tp.cells.$textarea.val( $ve_content );
					// position cursor at the end
					tp.cells.$textarea.get(0).selectionStart = tp.cells.$textarea.get(0).selectionEnd = tp.cells.$textarea.val().length;
					tp.table.set_table_changed();
				}
				tp.cells.$textarea.focus();
				tp.cells.advanced_editor.close();
			},
			close: function() {
				$id( 'advanced-editor' ).wpdialog( 'close' );
				return false;
			}
		},
		checkboxes: {
			last_clicked: { '#edit-form-body' : false, '#edit-form-foot' : false },
			multi_select: function ( event ) {
				if ( 'undefined' === event.shiftKey ) {
					return true;
				}

				if ( event.shiftKey ) {
					if ( ! tp.cells.checkboxes.last_clicked[ event.data.parent ] ) {
						return true;
					}

					var $checkboxes = $( event.data.parent ).find( ':checkbox' ),
						first_cb = $checkboxes.index( tp.cells.checkboxes.last_clicked[ event.data.parent ] ),
						last_cb = $checkboxes.index( this );
					if ( first_cb !== last_cb ) {
						$checkboxes.slice( Math.min( first_cb, last_cb ), Math.max( first_cb, last_cb ) ).prop( 'checked', $(this).prop( 'checked' ) );
					}
				}
				tp.cells.checkboxes.last_clicked[ event.data.parent ] = this;
				return true;
			}
		}
	};

	tp.content = {
		link: {
			prompt_shown: false,
			add: function( /* event */ ) {
				if ( ! tp.content.link.prompt_shown ) {
					if ( ! confirm( tablepress_strings.link_add ) ) {
						return;
					}
				}

				tp.content.link.prompt_shown = true;
				// mousedown instead of click to allow selection of text
				// mousedown will set the desired target textarea, and mouseup anywhere will show the link box
				// other approaches can lead to the wrong textarea being selected
				$id( 'edit-form-body' ).one( 'mousedown', 'textarea', function() {
					var editor_id = this.id;
					$( document ).one( 'mouseup', function() {
						if ( typeof wpLink !== 'undefined' ) {
							wpLink.open( editor_id );
							tp.table.set_table_changed();
						}
					} );
				} );
			}
		},
		image: {
			prompt_shown: false,
			add: function( /* event */ ) {
				if ( ! tp.content.image.prompt_shown ) {
					if ( ! confirm( tablepress_strings.image_add ) ) {
						return;
					}
				}

				tp.content.image.prompt_shown = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var editor = this.id,
						options = {
							frame: 'post',
							state: 'insert',
							title: wp.media.view.l10n.addMedia,
							multiple: true
						};

					// Move caret to the end, to prevent inserting right between existing text, as that's ugly in small cells (though possible in the Advanced Editor and Insert Link dialog).
					this.selectionStart = this.selectionEnd = this.value.length;

					// Remove focus from the textarea to prevent Opera from showing the outline of the textarea above the modal.
					// See: WP Core #22445
					$(this).blur();

					wp.media.editor.open( editor, options );
					tp.table.set_table_changed();
				} );
			}
		},
		span: {
			prompt_shown: false,
			add: function( span ) {
				var span_add_msg = ( '#rowspan#' === span ) ? tablepress_strings.rowspan_add : tablepress_strings.colspan_add;

				// init object, due to string keys
				if ( false === tp.content.span.prompt_shown ) {
					tp.content.span.prompt_shown = {};
					tp.content.span.prompt_shown['#rowspan#'] = tp.content.span.prompt_shown['#colspan#'] = false;
				}

				// Automatically deactivate DataTables, if cells are combined
				if ( $id( 'option-use-datatables' ).prop( 'checked' ) ) {
					if ( confirm( tablepress_strings.span_add_datatables_warning ) ) {
						$id( 'option-use-datatables' ).prop( 'checked', false ).change();
					} else {
						return;
					}
				}

				if ( ! tp.content.span.prompt_shown[ span ] ) {
					if ( ! confirm( span_add_msg ) ) {
						return;
					}
				}

				tp.content.span.prompt_shown[ span ] = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var $textarea = $(this),
						col_idx = $textarea.parent().index(),
						row_idx = $textarea.closest( 'tr' ).index();
					if ( '#rowspan#' === span ) {
						if ( 0 === row_idx ) {
							alert( tablepress_strings.no_rowspan_first_row );
							return;
						}
						if ( tp.table.head && 1 === row_idx ) {
							alert( tablepress_strings.no_rowspan_table_head );
							return;
						}
						if ( tp.table.foot && ( tp.table.rows - 1 ) === row_idx ) {
							alert( tablepress_strings.no_rowspan_table_foot );
							return;
						}
					} else if ( ( '#colspan#' === span ) && ( tp.table.no_data_columns_pre === col_idx ) ) {
						alert( tablepress_strings.no_colspan_first_col );
						return;
					}
					$textarea.val( span );
					tp.table.set_table_changed();
				} );
			}
		}
	};

	tp.check = {
		table_id: function( event ) {
			if ( ( 37 === event.which ) || ( 39 === event.which ) ) {
				return;
			}
			var $input = $(this);
			$input.val( $input.val().replace( /[^0-9a-zA-Z-_]/g, '' ) );
		},
		changes_saved: function() {
			if ( tp.made_changes ) {
				return tablepress_strings.unsaved_changes_unload;
			}
		}
	};

	tp.reindex = function() {
		var $row,
			$rows = $id( 'edit-form-body' ).children(),
			$cell, known_references = {};

		tp.table.rows = $rows.length;
		if ( tp.table.rows > 0 ) {
			tp.table.columns = $rows.first().children().length - tp.table.no_data_columns_pre - tp.table.no_data_columns_post;
		} else {
			tp.table.columns = 0;
		}

		$rows
		.each( function( row_idx, row ) {
			$row = $( row );
			$row.find( 'textarea' )
				.val( function( column_idx, value ) {
					// If the cell is not a formula, there's nothing to do here
					if ( ( '' === value ) || ( '=' !== value.charAt(0) ) ) {
						return value;
					}

					return value.replace( /([A-Z]+[0-9]+)(?::([A-Z]+[0-9]+))?/g, function( full_match, first_cell, second_cell ) {
						// first_cell must always exist, while second_cell only exists in ranges like A4:B7
						// we will use full_match as our result variable, so that we don't need an extra one

						if ( ! known_references.hasOwnProperty( first_cell ) ) {
							$cell = $id( 'cell-' + first_cell );
							if ( $cell.length ) {
								known_references[ first_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
							} else {
								known_references[ first_cell ] = first_cell;
							}
						}
						full_match = known_references[ first_cell ];

						if ( ( 'undefined' !== typeof second_cell ) && ( '' !== second_cell ) ) { // Chrome and IE pass an undefined variable, while Firefox passes an empty string
							if ( ! known_references.hasOwnProperty( second_cell ) ) {
								$cell = $id( 'cell-' + second_cell );
								if ( $cell.length ) {
									known_references[ second_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
								} else {
									known_references[ second_cell ] = second_cell;
								}
							}
							full_match += ':' + known_references[ second_cell ];
						}

						return full_match;
					} );
				} )
				.attr( 'name', function( column_idx /*, old_name */ ) {
					return 'table[data][' + row_idx + '][' + column_idx + ']';
				} );

			$row.find( '.move-handle' ).html( row_idx + 1 );
		} )
		.each( function( row_idx, row ) { // need a second loop here to not break logic in previous loop, that queries textareas by their old ID
			$( row ).find( 'textarea' ).attr( 'id', function( column_idx /*, old_id */ ) {
				return 'cell-' + tp.columns.number_to_letter( column_idx + 1 ) + ( row_idx + 1 );
			} );
		});
		$id( 'edit-form-head' ).find( '.move-handle' )
			.html( function( idx ) { return tp.columns.number_to_letter( idx + 1 ); } );

		$id( 'number-rows' ).val( tp.table.rows );
		$id( 'number-columns' ).val( tp.table.columns );

		tp.table.set_table_changed();
	};

	tp.save_changes = {
		trigger: function( event ) {
			// validation checks
			if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
				alert( tablepress_strings.num_pagination_entries_invalid );
				$id( 'option-datatables-paginate_entries' ).focus().select();
				return;
			}
			if ( ( /[^A-Za-z0-9- _]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
				alert( tablepress_strings.extra_css_classes_invalid );
				$id( 'option-extra-css-classes' ).focus().select();
				return;
			}

			if ( event.shiftKey ) {
				tp.made_changes = false; // to prevent onunload warning
				$id( 'tablepress-page' ).find( 'form' ).submit();
				return;
			}

			$(this).closest( 'p' ).append( '<span class="animation-saving spinner is-active" title="' + tablepress_strings.saving_changes + '"/>' );
			$( '.save-changes-button' ).prop( 'disabled', true );
			$( 'body' ).addClass( 'wait' );

			$.ajax({
				'type': 'POST',
				'url': ajaxurl,
				'data': tp.table.prepare_ajax_request( 'tablepress_save_table', '#nonce-edit-table' ),
				'success': tp.save_changes.ajax_success,
				'error': tp.save_changes.ajax_error,
				'dataType': 'json'
			} );
		},
		ajax_success: function( data, status /*, jqXHR */ ) {
			if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
				tp.save_changes.error( 'AJAX call successful, but unclear status. Try again while holding down the &#8220;Shift&#8221; key.' );
			} else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) ) {
				tp.save_changes.error( 'AJAX call successful, but unclear data. Try again while holding down the &#8220;Shift&#8221; key.' );
			} else if ( true !== data.success ) {
				var debug_html = '';
				// Print debug information, if we are in debug mode
				if ( ( 'undefined' !== typeof data.error_details ) && ( tablepress_options.print_debug_output ) ) {
					debug_html = '</p><p>These errors were encountered:</p><pre>' + data.error_details + '</pre><p>'; // Some HTML magic because this is wrapped in <p> when printed
				}
				tp.save_changes.error( 'AJAX call successful, internal saving process failed. Try again while holding down the &#8220;Shift&#8221; key.' + debug_html );
			} else {
				tp.save_changes.success( data );
			}
		},
		ajax_error: function( jqXHR, status, error_thrown ) {
			tp.save_changes.error( 'AJAX call failed: ' + status + ' - ' + error_thrown + '. Try again while holding down the &#8220;Shift&#8221; key.' );
		},
		success: function( data ) {
			// saving was successful, so the original ID has changed to the (maybe) new ID -> we need to adjust all occurrences
			if ( tp.table.id !== data.table_id ) {
				// update URL (for HTML5 browsers only), but only if ID really changed, to not get dummy entries in the browser history
				if ( ( 'pushState' in window.history ) && null !== window.history.pushState ) {
					window.history.pushState( '', '', window.location.href.replace( /table_id=[0-9a-zA-Z-_]+/gi, 'table_id=' + data.table_id ) );
				}
			}
			// update CSS class for data field form
			$id( 'edit-form' ).removeClass( 'tablepress-edit-screen-id-' + tp.table.id ).addClass( 'tablepress-edit-screen-id-' + data.table_id );
			// update table ID in input fields (type text and hidden)
			tp.table.id = tp.table.new_id = data.table_id;
			$id( 'table-id' ).val( tp.table.id );
			$id( 'table-new-id' ).val( tp.table.new_id );
			// update the Shortcode text field
			$( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' );
			// update the nonces
			$id( 'nonce-edit-table' ).val( data.new_edit_nonce );
			$id( 'nonce-preview-table' ).val( data.new_preview_nonce );
			// update URLs in Preview links
			var $show_preview_buttons = $( '.show-preview-button' );
			if ( $show_preview_buttons.length ) { // check necessary, because Preview button might not be visible
				$show_preview_buttons.attr( 'href',
					$show_preview_buttons.first().attr( 'href' )
						.replace( /item=[a-zA-Z0-9_-]+/g, 'item=' + data.table_id )
						.replace( /&_wpnonce=[a-z0-9]+/ig, '&_wpnonce=' + data.new_preview_nonce )
				);
			}
			// update last modified date and user nickname
			$id( 'last-modified' ).text( data.last_modified );
			$id( 'last-editor' ).text( data.last_editor );
			tp.table.unset_table_changed();
			tp.save_changes.after_saving_dialog( 'success', tablepress_strings[ data.message ] );
		},
		error: function( message ) {
			tp.save_changes.after_saving_dialog( 'error', message );
		},
		after_saving_dialog: function( type, message ) {
			if ( 'undefined' === typeof message ) {
				message = '';
			} else {
				message = ': ' + message;
			}
			var delay,
				div_class = 'save-changes-' + type;
			if ( 'success' === type ) {
				div_class += ' notice notice-success';
				delay = 3000;
			} else {
				div_class += ' notice notice-error';
				delay = 6000;
			}
			$( '.animation-saving' ).closest( 'p' )
				.after( '<div class="ajax-alert ' + div_class + '"><p>' + tablepress_strings['save_changes_' + type] + message + '</p></div>' );
			$( '.animation-saving' ).remove();
			$( '.save-changes-' + type ).delay( delay ).fadeOut( 2000, function() { $(this).remove(); } );
			$( '.save-changes-button' ).prop( 'disabled', false );
			$( 'body' ).removeClass( 'wait' );
		}
	};

	tp.init = function() {
		var callbacks = {
			'click': {
				'#rows-insert':			tp.rows.insert,
				'#columns-insert':		tp.columns.insert,
				'#rows-duplicate':		tp.rows.duplicate,
				'#columns-duplicate':	tp.columns.duplicate,
				'#rows-remove':			tp.rows.remove,
				'#columns-remove':		tp.columns.remove,
				'#rows-hide':			tp.rows.hide,
				'#columns-hide':		tp.columns.hide,
				'#rows-unhide':			tp.rows.unhide,
				'#columns-unhide':		tp.columns.unhide,
				'#rows-append':			tp.rows.append,
				'#columns-append':		tp.columns.append,
				'#link-add':			tp.content.link.add,
				'#image-add':			tp.content.image.add,
				'#span-add-rowspan':	function() { tp.content.span.add( '#rowspan#' ); },
				'#span-add-colspan':	function() { tp.content.span.add( '#colspan#' ); },
				'.show-preview-button': tp.table.preview.trigger,
				'.save-changes-button': tp.save_changes.trigger,
				'.show-help-box':		function() {
					$(this).next().wpdialog( {
						title: $(this).attr( 'title' ),
						height: 470,
						width: 320,
						modal: true,
						dialogClass: 'wp-dialog',
						resizable: false
					} );
				}
			},
			'keyup': {
				'#table-new-id':		tp.check.table_id
			},
			'change': {
				'#option-table-head':			tp.table.change_table_head,
				'#option-table-foot':			tp.table.change_table_foot,
				'#option-use-datatables':		tp.table.change_datatables,
				'#option-datatables-paginate':	tp.table.change_datatables_pagination
			},
			'blur': {
				'#table-new-id':		tp.table.change_id	// onchange would not recognize changed values from tp.check.table_id
			}
		},
		$table = $id( 'edit-form-body' );

		$.each( callbacks, function( event, event_callbacks ) {
			$.each( event_callbacks, function( selector, callback ) {
				$( selector ).on( event, callback );
			} );
		} );

		$( window ).on( 'beforeunload', tp.check.changes_saved );

		// do this before the next lines, to not trigger set_table_changed()
		$id( 'option-table-head' ).change(); // init changed/disabled states of DataTables JS features checkboxes
		$id( 'option-print-name' ).change( tp.table.change_print_name_description ).change(); // init dropdowns for name and description position
		$id( 'option-print-description' ).change( tp.table.change_print_name_description ).change();

		// just once is enough, will be reset after saving
		$table.one( 'change', 'textarea', tp.table.set_table_changed );
		$( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );

		if ( tablepress_options.cells_advanced_editor ) {
			$table.on( 'click', 'textarea', tp.cells.advanced_editor.keyopen );
			$id( 'advanced-editor-open' ).on( 'click', tp.cells.advanced_editor.buttonopen );
			$id( 'advanced-editor-confirm' ).on( 'click', tp.cells.advanced_editor.save );
			$id( 'advanced-editor-cancel' ).on( 'click', tp.cells.advanced_editor.close );
			$id( 'advanced-editor' ).wpdialog( {
				autoOpen: false,
				title: $id( 'advanced-editor-open' ).val(),
				width: 600,
				modal: true,
				dialogClass: 'wp-dialog',
				resizable: false
			} );
			// Fix issue with input fields not being usable (they are immediately losing focus without this) in the wpLink dialog when called through the "Advanced Editor"
			$id( 'wp-link' ).on( 'focus', 'input', function( event ) {
				event.stopPropagation();
			} );
		} else {
			$id( 'advanced-editor-open' ).hide();
		}

		// Fix issue with input fields not being usable (they are immediately losing focus without this) in the sidebar of the new Media Manager
		$( 'body' ).on( 'focus', '.media-modal .media-frame-content input, .media-modal .media-frame-content textarea', function( event ) {
			event.stopPropagation();
		} );

		if ( tablepress_options.cells_auto_grow ) {
			$table.on( 'focus', 'textarea', tp.cells.autogrow );
		}

		$id( 'edit-form-body' ).on( 'click', 'input:checkbox', { parent: '#edit-form-body' }, tp.cells.checkboxes.multi_select );
		$id( 'edit-form-foot' ).on( 'click', 'input:checkbox', { parent: '#edit-form-foot' }, tp.cells.checkboxes.multi_select );

		$id( 'edit-form-head' ).on( 'click', '.sort-control', tp.rows.sort );

		// on form submit: Enable disabled fields, so that they are transmitted in the POST request
		$id( 'tablepress-page' ).find( 'form' ).on( 'submit', function() {
			$(this).find( '.tablepress-postbox-table' ).find( 'input, select' ).prop( 'disabled', false );
		} );

		$table.sortable( {
			axis: 'y',
			containment: $id( 'edit-form' ), // to get better behavior when dragging before/after the first/last row
			forceHelperSize: true, // necessary?
			handle: '.move-handle',
			start: tp.rows.move.start,
			change: tp.rows.move.change,
			stop: tp.rows.move.stop,
			update: tp.reindex
		} ); // disableSelection() prohibits selection of text in textareas via keyboard

		$id( 'edit-form-head' ).sortable( {
			axis: 'x',
			items: '.head',
			containment: 'parent',
			forceHelperSize: true, // necessary?
			helper: 'clone',
			handle: '.move-handle',
			start: tp.columns.move.start,
			stop: tp.columns.move.stop,
			change: tp.columns.move.change,
			sort: tp.columns.move.sort
		} ).disableSelection();
	};

	// Run TablePress initialization.
	tp.init();

} );
home/xbodynamge/namtation/wp-content/plugins/tablepress/admin/js/edit.js000064400000135321151131643720022545 0ustar00/**
 * JavaScript code for the "Edit" screen
 *
 * @package TablePress
 * @subpackage Views JavaScript
 * @author Tobias Bäthge
 * @since 1.0.0
 */

/* global alert, confirm, tp, tablepress_strings, tablepress_options, ajaxurl, wpLink, tb_show, wp, JSON */

// Ensure the global `tp` object exists.
window.tp = window.tp || {};

jQuery( document ).ready( function( $ ) {

	'use strict';

	/* Wrapper to find elements in the page faster with JS-native functions */
	var $id = function( element_id ) {
		return $( document.getElementById( element_id ) );
	};

	/**
	 * TablePress object, mostly with functionality for the "Edit" screen
	 *
	 * @since 1.0.0
	 */
	tp.made_changes = false;

	tp.table = {
		id: $id( 'table-id' ).val(),
		new_id: $id( 'table-new-id' ).val(),
		rows: parseInt( $id( 'number-rows' ).val(), 10 ),
		columns: parseInt( $id( 'number-columns' ).val(), 10 ),
		head: $id( 'option-table-head' ).prop( 'checked' ),
		foot: $id( 'option-table-foot' ).prop( 'checked' ),
		no_data_columns_pre: 2,
		no_data_columns_post: 1,
		body_cells_pre: '<tr><td><span class="move-handle"></span></td><td><input type="checkbox" /><input type="hidden" class="visibility" name="table[visibility][rows][]" value="1" /></td>',
		body_cells_post: '<td><span class="move-handle"></span></td></tr>',
		body_cell: '<td><textarea rows="1"></textarea></td>',
		head_cell: '<th class="head"><span class="sort-control sort-desc" title="' + tablepress_strings.sort_desc + '"><span class="sorting-indicator"></span></span><span class="sort-control sort-asc" title="' + tablepress_strings.sort_asc + '"><span class="sorting-indicator"></span></span><span class="move-handle"></span></th>',
		foot_cell: '<th><input type="checkbox" /><input type="hidden" class="visibility" name="table[visibility][columns][]" value="1" /></th>',
		set_table_changed: function() {
			tp.made_changes = true;
		},
		unset_table_changed: function() {
			tp.made_changes = false;
			$id( 'edit-form-body' ).one( 'change', 'textarea', tp.table.set_table_changed );
			// @TODO: maybe use .tablepress-postbox-table here and further below
			$( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );
		},
		change_id: function( /* event */ ) {
			// empty table IDs are not allowed
			if ( '' === $.trim( $id( 'table-new-id' ).val() ) ) {
				alert( tablepress_strings.table_id_not_empty );
				$id( 'table-new-id' ).val( tp.table.new_id ).focus().select();
				return;
			}
			// the '0' table ID is not allowed
			if ( '0' === $.trim( $id( 'table-new-id' ).val() ) ) {
				alert( tablepress_strings.table_id_not_zero );
				$id( 'table-new-id' ).val( tp.table.new_id ).focus().select();
				return;
			}

			if ( this.value === tp.table.new_id ) {
				return;
			}

			if ( confirm( tablepress_strings.ays_change_table_id ) ) {
				tp.table.new_id = this.value;
				$( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' ).click(); // click() to focus and select
				tp.table.set_table_changed();
			} else {
				$(this).val( tp.table.new_id );
			}
		},
		change_table_head: function( /* event */ ) {
			tp.table.head = $(this).prop( 'checked' );
			$id( 'option-use-datatables' ).prop( 'disabled', ! tp.table.head ).change();
			$id( 'notice-datatables-head-row' ).toggle( ! tp.table.head );
			tp.rows.stripe();
		},
		change_table_foot: function( /* event */ ) {
			tp.table.foot = $(this).prop( 'checked' );
			tp.rows.stripe();
		},
		change_print_name_description: function( /* event */ ) {
			$id( this.id + '-position' ).prop( 'disabled', ! $(this).prop( 'checked' ) );
		},
		change_datatables: function() {
			var $datatables_checkbox = $id( 'option-use-datatables' ),
				checkboxes_disabled = ! ( $datatables_checkbox.prop( 'checked' ) && ! $datatables_checkbox.prop( 'disabled' ) );
			$datatables_checkbox.closest( 'tbody' ).find( 'input' ).not( $datatables_checkbox ).prop( 'disabled', checkboxes_disabled );
			tp.table.change_datatables_pagination();
		},
		change_datatables_pagination: function() {
			var $pagination_checkbox = $id( 'option-datatables-paginate' ),
				pagination_enabled = ( $pagination_checkbox.prop( 'checked' ) && ! $pagination_checkbox.prop( 'disabled' ) );
			$id( 'option-datatables-lengthchange' ).prop( 'disabled', ! pagination_enabled );
			$id( 'option-datatables-paginate_entries' ).prop( 'disabled', ! pagination_enabled );
		},
		prepare_ajax_request: function( wp_action, wp_nonce ) {
			var $table_body = $id( 'edit-form-body' ),
				table_data = [],
				table_options,
				table_number = { rows: tp.table.rows, columns: tp.table.columns, hidden_rows: 0, hidden_columns: 0 },
				table_visibility = { rows: [], columns: [] };

			$table_body.children().each( function( idx, row ) {
				table_data[ idx ] = $( row ).find( 'textarea' )
					.map( function() {
						return $(this).val();
					} )
					.get();
			} );
			table_data = JSON.stringify( table_data );

			// @TODO: maybe for options saving: https://stackoverflow.com/questions/1184624/convert-form-data-to-javascript-object-with-jquery
			// or each()-loop through all checkboxes/textfields/selects
			table_options = {
				// Table Options
				table_head: tp.table.head,
				table_foot: tp.table.foot,
				alternating_row_colors: $id( 'option-alternating-row-colors' ).prop( 'checked' ),
				row_hover: $id( 'option-row-hover' ).prop( 'checked' ),
				print_name: $id( 'option-print-name' ).prop( 'checked' ),
				print_description: $id( 'option-print-description' ).prop( 'checked' ),
				print_name_position: $id( 'option-print-name-position' ).val(),
				print_description_position: $id( 'option-print-description-position' ).val(),
				extra_css_classes: $id( 'option-extra-css-classes' ).val(),
				// DataTables JS features
				use_datatables: $id( 'option-use-datatables' ).prop( 'checked' ),
				datatables_sort: $id( 'option-datatables-sort' ).prop( 'checked' ),
				datatables_filter: $id( 'option-datatables-filter' ).prop( 'checked' ),
				datatables_paginate: $id( 'option-datatables-paginate' ).prop( 'checked' ),
				datatables_lengthchange: $id( 'option-datatables-lengthchange' ).prop( 'checked' ),
				datatables_paginate_entries: $id( 'option-datatables-paginate_entries' ).val(),
				datatables_info: $id( 'option-datatables-info' ).prop( 'checked' ),
				datatables_scrollx: $id( 'option-datatables-scrollx' ).prop( 'checked' ),
				datatables_custom_commands: $id( 'option-datatables-custom-commands' ).val()
			};
			table_options = JSON.stringify( table_options );

			table_visibility.rows = $table_body.find( 'input[type="hidden"]' )
				.map( function() {
					if ( '1' === $(this).val() ) {
						return 1;
					}
					table_number.hidden_rows += 1;
					return 0;
				} )
				.get();
			table_visibility.columns = $id( 'edit-form-foot' ).find( 'input[type="hidden"]' )
				.map( function() {
					if ( '1' === $(this).val() ) {
						return 1;
					}
					table_number.hidden_columns += 1;
					return 0;
				} )
				.get();
			table_visibility = JSON.stringify( table_visibility );

			// request_data =
			return {
				action: wp_action,
				_ajax_nonce : $( wp_nonce ).val(),
				tablepress: {
					id: tp.table.id,
					new_id: tp.table.new_id,
					name: $id( 'table-name' ).val(),
					description: $id( 'table-description' ).val(),
					number: table_number,
					data: table_data,
					options: table_options,
					visibility: table_visibility
				}
			};
		},
		preview: {
			trigger: function( /* event */ ) {
				if ( ! tp.made_changes ) {
					tp.table.preview.show( $(this).attr( 'href' ) + '&TB_iframe=true' );
					return false;
				}

				// validation checks
				if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
					alert( tablepress_strings.num_pagination_entries_invalid );
					$id( 'option-datatables-paginate_entries' ).focus().select();
					return;
				}
				if ( ( /[^A-Za-z0-9- _]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
					alert( tablepress_strings.extra_css_classes_invalid );
					$id( 'option-extra-css-classes' ).focus().select();
					return;
				}

				$(this).closest( 'p' ).append( '<span class="animation-preview spinner is-active" title="' + tablepress_strings.preparing_preview + '"/>' );
				$( 'body' ).addClass( 'wait' );
				$id( 'table-preview' ).empty(); // clear preview

				$.ajax({
					'type': 'POST',
					'url': ajaxurl,
					'data': tp.table.prepare_ajax_request( 'tablepress_preview_table', '#nonce-preview-table' ),
					'success': tp.table.preview.ajax_success,
					'error': tp.table.preview.ajax_error,
					'dataType': 'json'
				} );

				return false;
			},
			ajax_success: function( data, status /*, jqXHR */ ) {
				if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
					tp.table.preview.error( 'AJAX call successful, but unclear status.' );
				} else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) || ( true !== data.success ) ) {
					tp.table.preview.error( 'AJAX call successful, but unclear data.' );
				} else {
					tp.table.preview.success( data );
				}
			},
			ajax_error: function( jqXHR, status, error_thrown ) {
				tp.table.preview.error( 'AJAX call failed: ' + status + ' - ' + error_thrown );
			},
			success: function( data ) {
				$id( 'table-preview' ).empty();
				$( '<iframe id="table-preview-iframe" />' ).load( function() {
					var $iframe = $(this).contents();
					$iframe.find( 'head' ).append( data.head_html );
					$iframe.find( 'body' ).append( data.body_html );
				} ).appendTo( '#table-preview' );
				$( '.animation-preview' ).remove();
				$( 'body' ).removeClass( 'wait' );
				tp.table.preview.show( '#TB_inline?inlineId=preview-container' );
			},
			error: function( message ) {
				$( '.animation-preview' ).closest( 'p' )
					.after( '<div class="ajax-alert preview-error error"><p>' + tablepress_strings.preview_error + ': ' + message + '</p></div>' );
				$( '.animation-preview' ).remove();
				$( '.preview-error' ).delay( 6000 ).fadeOut( 2000, function() { $(this).remove(); } );
				$( 'body' ).removeClass( 'wait' );
			},
			show: function( url ) {
				var width = $( window ).width() - 120,
					height = $( window ).height() - 120;
				if ( $( '#wpadminbar' ).length ) {
					height -= parseInt( $( '#wpadminbar' ).css( 'height' ), 10 );
				}
				tb_show( $( '.show-preview-button' ).first().text(), url + '&height=' + height + '&width=' + width, false );
			}
		}
	};

	tp.rows = {
		create: function( num_rows ) {
			var i, j,
				column_idxs,
				new_rows = '';

			for ( i = 0; i < num_rows; i++ ) {
				new_rows += tp.table.body_cells_pre;
				for ( j = 0; j < tp.table.columns; j++ ) {
					new_rows += tp.table.body_cell;
				}
				new_rows += tp.table.body_cells_post;
			}

			column_idxs = $id( 'edit-form-foot' ).find( '.column-hidden' )
				.map( function() { return $(this).index(); } ).get();
			return $( new_rows ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.addClass( 'column-hidden' );
			} );
		},
		append: function( /* event */ ) {
			var num_rows = $id( 'rows-append-number' ).val();

			if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_rows ) ) {
				alert( tablepress_strings.append_num_rows_invalid );
				$id( 'rows-append-number' ).focus().select();
				return;
			}

			$id( 'edit-form-body' ).append( tp.rows.create( num_rows ) );

			tp.rows.stripe();
			tp.reindex();
		},
		insert: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.before( tp.rows.create( 1 ) );

			tp.rows.stripe();
			tp.reindex();
		},
		duplicate: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.each( function( idx, row ) {
				var $row = $( row ),
					$textareas = $row.find( 'textarea' ),
					$duplicated_row = $row.clone();
				$duplicated_row.find( 'textarea' ).removeAttr( 'id' ).each( function( idx, cell ) {
					$( cell ).val( $textareas.eq( idx ).val() ); // setting val() is necessary, as clone() doesn't copy the current value, see jQuery bugs 5524, 2285, 3016
				} );
				$row.after( $duplicated_row );
			} );

			tp.rows.stripe();
			tp.reindex();
		},
		hide: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.addClass( 'row-hidden' ).find( '.visibility' ).val( '0' );

			tp.rows.stripe();
			tp.table.set_table_changed();
		},
		unhide: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.removeClass( 'row-hidden' ).find( '.visibility' ).val( '1' );

			tp.rows.stripe();
			tp.table.set_table_changed();
		},
		remove: function( /* event */ ) {
			var confirm_message,
				$selected_rows = $id( 'edit-form-body' ).find( 'input:checked' ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			if ( tp.table.rows === $selected_rows.length ) {
				alert( tablepress_strings.no_remove_all_rows );
				return;
			}

			if ( 1 === $selected_rows.length ) {
				confirm_message = tablepress_strings.ays_remove_rows_singular;
			} else {
				confirm_message = tablepress_strings.ays_remove_rows_plural;
			}
			if ( ! confirm( confirm_message ) ) {
				return;
			}

			$selected_rows.remove();

			tp.rows.stripe();
			tp.reindex();
		},
		move: {
			start: function( event, ui ) {
				$( ui.placeholder ).removeClass( 'row-hidden' ).css( 'visibility', 'visible' )
					.html( '<td colspan="' + ( tp.table.columns + tp.table.no_data_columns_pre + tp.table.no_data_columns_post ) + '"><div/></td>' );
				$( ui.helper ).removeClass( 'odd head-row foot-row' );
			},
			change: function( event, ui ) {
				tp.rows.stripe( ui.helper );
			},
			stop: function( /* event, ui */ ) {
				tp.rows.stripe();
			}
		},
		sort: function() {
			var column_idx = $(this).parent().index(),
				direction = ( $(this).hasClass( 'sort-asc' ) ) ? 1 : -1,
				$table_body = $('#edit-form-body'),
				$head_rows = $table_body.find( '.head-row' ).prevAll().addBack(),
				$foot_rows = $table_body.find( '.foot-row' ).nextAll().addBack(),
				rows = $table_body.children().not( $head_rows ).not( $foot_rows ).get(),
				/*
				 * Natural Sort algorithm for Javascript - Version 0.8.1 - Released under MIT license
				 * Author: Jim Palmer (based on chunking idea from Dave Koelle)
				 * See: https://github.com/overset/javascript-natural-sort and http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm-with-unicode-support/
				 */
				natural_sort = function( a, b ) {
					var re = /(^([+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|^0x[\da-fA-F]+$|\d+)/g,
						sre = /^\s+|\s+$/g,   // trim pre-post whitespace
						snre = /\s+/g,        // normalize all whitespace to single ' ' character
						dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
						hre = /^0x[0-9a-f]+$/i,
						ore = /^0/,
						// strip whitespace
						x = a.replace(sre, '') || '',
						y = b.replace(sre, '') || '',
						// chunk/tokenize
						xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
						yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
						// numeric, hex or date detection
						xD = parseInt(x.match(hre), 16) || (xN.length !== 1 && Date.parse(x)),
						yD = parseInt(y.match(hre), 16) || xD && y.match(dre) && Date.parse(y) || null,
						normChunk = function(s, l) {
							// normalize spaces; find floats not starting with '0', string or 0 if not defined (Clint Priest)
							return (!s.match(ore) || l === 1) && parseFloat(s) || s.replace(snre, ' ').replace(sre, '') || 0;
						},
						oFxNcL, oFyNcL;
					// first try and sort Hex codes or Dates
					if (yD) {
						if (xD < yD) { return -1; }
						else if (xD > yD) { return 1; }
					}
					// natural sorting through split numeric strings and default strings
					for(var cLoc = 0, xNl = xN.length, yNl = yN.length, numS = Math.max(xNl, yNl); cLoc < numS; cLoc++) {
						oFxNcL = normChunk(xN[cLoc] || '', xNl);
						oFyNcL = normChunk(yN[cLoc] || '', yNl);
						// handle numeric vs string comparison - number < string - (Kyle Adams)
						if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
							return isNaN(oFxNcL) ? 1 : -1;
						}
						// if unicode use locale comparison
						if (/[^\x00-\x80]/.test(oFxNcL + oFyNcL) && oFxNcL.localeCompare) {
							var comp = oFxNcL.localeCompare(oFyNcL);
							return comp / Math.abs(comp);
						}
						if (oFxNcL < oFyNcL) { return -1; }
						else if (oFxNcL > oFyNcL) { return 1; }
					}
				};

			$.each( rows, function( row_idx, row ) {
				row.sort_key = ( '' + $( row ).children().eq( column_idx ).find( 'textarea' ).val() ).toLowerCase(); // convert to string, and lower case for case insensitive sorting
			} );

			rows.sort( function( a, b ) {
				return direction * natural_sort( a.sort_key, b.sort_key );
			} );

			// might not be necessary:
			$.each( rows, function( row_idx, row ) {
				row.sort_key = null;
			} );

			$table_body.append( $head_rows );
			$table_body.append( rows );
			$table_body.append( $foot_rows );

			tp.rows.stripe();
			tp.reindex();
		},
		stripe: function( helper ) {
			if ( 'undefined' === typeof helper ) {
				helper = null;
			}
			helper = $( helper );
			var $rows = $id( 'edit-form-body' ).children().removeClass( 'odd head-row foot-row' ).not( helper );
			$rows.filter( ':even' ).addClass( 'odd' );
			$rows = $rows.not( '.row-hidden' );
			if ( helper.hasClass( 'row-hidden' ) ) {
				$rows = $rows.not( '.ui-sortable-placeholder' );
			}
			if ( tp.table.head ) {
				$rows.first().addClass( 'head-row' );
			}
			if ( tp.table.foot ) {
				$rows.last().addClass( 'foot-row' );
			}
		}
	};

	tp.columns = {
		append: function( /* event */ ) {
			var i,
				num_columns = $id( 'columns-append-number' ).val(),
				new_head_cells = '', new_body_cells = '', new_foot_cells = '';

			if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_columns ) ) {
				alert( tablepress_strings.append_num_columns_invalid );
				$id( 'columns-append-number' ).focus().select();
				return;
			}

			for ( i = 0; i < num_columns; i++ ) {
				new_body_cells += tp.table.body_cell;
				new_head_cells += tp.table.head_cell;
				new_foot_cells += tp.table.foot_cell;
			}

			$id( 'edit-form-body' ).children().each( function( row_idx, row ) {
				$( row ).children().slice( - tp.table.no_data_columns_post )
					.before( new_body_cells );
			} );
			$id( 'edit-form-head' ).children().slice( - tp.table.no_data_columns_post )
				.before( new_head_cells );
			$id( 'edit-form-foot' ).children().slice( - tp.table.no_data_columns_post )
				.before( new_foot_cells );

			tp.reindex();
		},
		insert: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.before( tp.table.body_cell );
			} );
			$id( 'edit-form-head' ).children()
				.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
				.before( tp.table.head_cell );
			$selected_columns.before( tp.table.foot_cell );

			tp.reindex();
		},
		duplicate: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form' ).find( 'tr' ).each( function( row_idx, row ) {
				$( row ).children().each( function( idx, cell ) {
					if ( -1 !== $.inArray( idx, column_idxs ) ) {
						var $cell = $( cell ),
							$duplicated_cell = $cell.clone();
							$duplicated_cell.find( 'textarea' ).removeAttr( 'id' ).val( $cell.find( 'textarea' ).val() ); // setting val() is necessary, as clone() doesn't copy the current value, see jQuery bugs 5524, 2285, 3016
						$cell.after( $duplicated_cell );
					}
				} );
			} );

			tp.reindex();
		},
		hide: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.addClass( 'column-hidden' );
			} );
			$selected_columns.addClass( 'column-hidden' ).find( '.visibility' ).val( '0' );

			tp.table.set_table_changed();
		},
		unhide: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.removeClass( 'column-hidden' );
			} );
			$selected_columns.removeClass( 'column-hidden' ).find( '.visibility' ).val( '1' );

			tp.table.set_table_changed();
		},
		remove: function( /* event */ ) {
			var column_idxs,
				confirm_message,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			if ( tp.table.columns === $selected_columns.length ) {
				alert( tablepress_strings.no_remove_all_columns );
				return;
			}

			if ( 1 === $selected_columns.length ) {
				confirm_message = tablepress_strings.ays_remove_columns_singular;
			} else {
				confirm_message = tablepress_strings.ays_remove_columns_plural;
			}
			if ( ! confirm( confirm_message ) ) {
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.remove();
			} );
			$selected_columns.remove();

			tp.reindex();
		},
		move: {
			source_idx: -1,
			target_idx: -1,
			$rows: null,
			$row_children: null,
			$cell: null,
			$cells: null,
			$placeholder: null,
			$helper: null,
			start: function( event, ui ) {
				var $item = $( ui.item ),
					column_width;

				tp.columns.move.source_idx = $item.index();

				tp.columns.move.$rows = $id( 'edit-form-body' ).children().add( '#edit-form-foot' );

				tp.columns.move.$cells = tp.columns.move.$rows
					.find( ':nth-child(' + ( tp.columns.move.source_idx + 1 ) + ')' )
					.each( function() {
						tp.columns.move.$cell = $(this);
						$( '<td class="move-placeholder"><div/></td>' ).insertBefore( tp.columns.move.$cell );
						tp.columns.move.$cell.insertAfter( tp.columns.move.$cell.nextAll().last() )
							.clone().addClass( 'move-hover' ).insertAfter( tp.columns.move.$cell )
							.find( 'textarea' ).val( tp.columns.move.$cell.find( 'textarea' ).val() );
							// last line works around problem with clone() of textareas, see jQuery bugs 5524, 2285, 3016
					} )
					.hide();

				tp.columns.move.$helper = tp.columns.move.$rows.find( '.move-hover' );
				/* // seems not to be working for rows, so disable it for columns
					.each( function() {
						tp.columns.move.$cell = $(this);
						tp.columns.move.$cell.css( 'top', ( tp.columns.move.$cell.position().top - 3 ) + 'px' );
					} );
				*/

				column_width = tp.columns.move.$helper.eq(1).width(); // eq(0) is table foot
				tp.columns.move.$helper.eq(0).width( column_width );
				tp.columns.move.$placeholder = tp.columns.move.$rows.find( '.move-placeholder' );
				tp.columns.move.$placeholder.find( 'div' ).width( column_width );
			},
			change: function( event, ui ) {
				tp.columns.move.target_idx = $( ui.placeholder ).index();

				if ( ( tp.columns.move.target_idx - tp.columns.move.source_idx ) === 1 ) {
					tp.columns.move.target_idx += 1;
				} else {
					if ( tp.columns.move.target_idx === tp.columns.move.source_idx ) {
						tp.columns.move.target_idx -= 1;
					}
				}

				tp.columns.move.$placeholder.each( function() {
					tp.columns.move.$cell = $(this);
					tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().children().eq( tp.columns.move.target_idx ) );
				} );

				if ( tp.columns.move.target_idx > tp.columns.move.source_idx ) {
					tp.columns.move.target_idx -= 1;
				}

				tp.columns.move.source_idx = tp.columns.move.target_idx;
			},
			sort: function( event, ui ) {
				tp.columns.move.$helper.css( 'left', ui.position.left );
			},
			stop: function( /* event, ui */ ) {
				tp.columns.move.$helper.remove();
				tp.columns.move.$cells
					.each( function() {
						tp.columns.move.$cell = $(this);
						tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().find( '.move-placeholder' ) );
					} )
					.show();
				tp.columns.move.$placeholder.remove();

				tp.columns.move.source_idx = tp.columns.move.target_idx = -1;
				tp.columns.move.$rows = tp.columns.move.$row_children = tp.columns.move.$cell = tp.columns.move.$cells = tp.columns.move.$placeholder = tp.columns.move.$helper = null;

				tp.reindex();
			}
		},
		number_to_letter: function( number ) {
			var column = '';
			while ( number > 0 ) {
				column = String.fromCharCode( 65 + ( ( number-1) % 26 ) ) + column;
				number = Math.floor( (number-1) / 26 );
			}
			return column;
		}/*,
		letter_to_number: function( column ) {
			column = column.toUpperCase();
			var count = column.length,
				number = 0,
				i;
			for ( i = 0; i < count; i++ ) {
				number += ( column.charCodeAt( count-1-i ) - 64 ) * Math.pow( 26, i );
			}
			return number;
		}*/
	};

	tp.cells = {
		$focus: $( null ),
		$textarea: null,
		autogrow: function( /* event */ ) {
			tp.cells.$focus.removeClass( 'focus' );
			tp.cells.$focus = $(this).closest( 'tr' ).addClass( 'focus' );
		},
		advanced_editor: {
			prompt_shown: false,
			keyopen: function( event ) {
				if ( ! event.shiftKey ) {
					return;
				}

				var $advanced_editor = $id( 'advanced-editor-content' );
				tp.cells.$textarea = $(this).blur();
				$advanced_editor.val( tp.cells.$textarea.val() );
				$id( 'advanced-editor' ).wpdialog( 'open' );
				$advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
				$advanced_editor.focus();
			},
			buttonopen: function() {
				if ( ! tp.cells.advanced_editor.prompt_shown ) {
					if ( ! confirm( tablepress_strings.advanced_editor_open ) ) {
						return;
					}
				}

				tp.cells.advanced_editor.prompt_shown = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var $advanced_editor = $id( 'advanced-editor-content' );
					tp.cells.$textarea = $(this).blur();
					$advanced_editor.val( tp.cells.$textarea.val() );
					$id( 'advanced-editor' ).wpdialog( 'open' );
					$advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
					$advanced_editor.focus();
				} );
			},
			save: function() {
				var $ve_content = $id( 'advanced-editor-content' ).blur().val();
				if ( tp.cells.$textarea.val() !== $ve_content ) {
					tp.cells.$textarea.val( $ve_content );
					// position cursor at the end
					tp.cells.$textarea.get(0).selectionStart = tp.cells.$textarea.get(0).selectionEnd = tp.cells.$textarea.val().length;
					tp.table.set_table_changed();
				}
				tp.cells.$textarea.focus();
				tp.cells.advanced_editor.close();
			},
			close: function() {
				$id( 'advanced-editor' ).wpdialog( 'close' );
				return false;
			}
		},
		checkboxes: {
			last_clicked: { '#edit-form-body' : false, '#edit-form-foot' : false },
			multi_select: function ( event ) {
				if ( 'undefined' === event.shiftKey ) {
					return true;
				}

				if ( event.shiftKey ) {
					if ( ! tp.cells.checkboxes.last_clicked[ event.data.parent ] ) {
						return true;
					}

					var $checkboxes = $( event.data.parent ).find( ':checkbox' ),
						first_cb = $checkboxes.index( tp.cells.checkboxes.last_clicked[ event.data.parent ] ),
						last_cb = $checkboxes.index( this );
					if ( first_cb !== last_cb ) {
						$checkboxes.slice( Math.min( first_cb, last_cb ), Math.max( first_cb, last_cb ) ).prop( 'checked', $(this).prop( 'checked' ) );
					}
				}
				tp.cells.checkboxes.last_clicked[ event.data.parent ] = this;
				return true;
			}
		}
	};

	tp.content = {
		link: {
			prompt_shown: false,
			add: function( /* event */ ) {
				if ( ! tp.content.link.prompt_shown ) {
					if ( ! confirm( tablepress_strings.link_add ) ) {
						return;
					}
				}

				tp.content.link.prompt_shown = true;
				// mousedown instead of click to allow selection of text
				// mousedown will set the desired target textarea, and mouseup anywhere will show the link box
				// other approaches can lead to the wrong textarea being selected
				$id( 'edit-form-body' ).one( 'mousedown', 'textarea', function() {
					var editor_id = this.id;
					$( document ).one( 'mouseup', function() {
						if ( typeof wpLink !== 'undefined' ) {
							wpLink.open( editor_id );
							tp.table.set_table_changed();
						}
					} );
				} );
			}
		},
		image: {
			prompt_shown: false,
			add: function( /* event */ ) {
				if ( ! tp.content.image.prompt_shown ) {
					if ( ! confirm( tablepress_strings.image_add ) ) {
						return;
					}
				}

				tp.content.image.prompt_shown = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var editor = this.id,
						options = {
							frame: 'post',
							state: 'insert',
							title: wp.media.view.l10n.addMedia,
							multiple: true
						};

					// Move caret to the end, to prevent inserting right between existing text, as that's ugly in small cells (though possible in the Advanced Editor and Insert Link dialog).
					this.selectionStart = this.selectionEnd = this.value.length;

					// Remove focus from the textarea to prevent Opera from showing the outline of the textarea above the modal.
					// See: WP Core #22445
					$(this).blur();

					wp.media.editor.open( editor, options );
					tp.table.set_table_changed();
				} );
			}
		},
		span: {
			prompt_shown: false,
			add: function( span ) {
				var span_add_msg = ( '#rowspan#' === span ) ? tablepress_strings.rowspan_add : tablepress_strings.colspan_add;

				// init object, due to string keys
				if ( false === tp.content.span.prompt_shown ) {
					tp.content.span.prompt_shown = {};
					tp.content.span.prompt_shown['#rowspan#'] = tp.content.span.prompt_shown['#colspan#'] = false;
				}

				// Automatically deactivate DataTables, if cells are combined
				if ( $id( 'option-use-datatables' ).prop( 'checked' ) ) {
					if ( confirm( tablepress_strings.span_add_datatables_warning ) ) {
						$id( 'option-use-datatables' ).prop( 'checked', false ).change();
					} else {
						return;
					}
				}

				if ( ! tp.content.span.prompt_shown[ span ] ) {
					if ( ! confirm( span_add_msg ) ) {
						return;
					}
				}

				tp.content.span.prompt_shown[ span ] = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var $textarea = $(this),
						col_idx = $textarea.parent().index(),
						row_idx = $textarea.closest( 'tr' ).index();
					if ( '#rowspan#' === span ) {
						if ( 0 === row_idx ) {
							alert( tablepress_strings.no_rowspan_first_row );
							return;
						}
						if ( tp.table.head && 1 === row_idx ) {
							alert( tablepress_strings.no_rowspan_table_head );
							return;
						}
						if ( tp.table.foot && ( tp.table.rows - 1 ) === row_idx ) {
							alert( tablepress_strings.no_rowspan_table_foot );
							return;
						}
					} else if ( ( '#colspan#' === span ) && ( tp.table.no_data_columns_pre === col_idx ) ) {
						alert( tablepress_strings.no_colspan_first_col );
						return;
					}
					$textarea.val( span );
					tp.table.set_table_changed();
				} );
			}
		}
	};

	tp.check = {
		table_id: function( event ) {
			if ( ( 37 === event.which ) || ( 39 === event.which ) ) {
				return;
			}
			var $input = $(this);
			$input.val( $input.val().replace( /[^0-9a-zA-Z-_]/g, '' ) );
		},
		changes_saved: function() {
			if ( tp.made_changes ) {
				return tablepress_strings.unsaved_changes_unload;
			}
		}
	};

	tp.reindex = function() {
		var $row,
			$rows = $id( 'edit-form-body' ).children(),
			$cell, known_references = {};

		tp.table.rows = $rows.length;
		if ( tp.table.rows > 0 ) {
			tp.table.columns = $rows.first().children().length - tp.table.no_data_columns_pre - tp.table.no_data_columns_post;
		} else {
			tp.table.columns = 0;
		}

		$rows
		.each( function( row_idx, row ) {
			$row = $( row );
			$row.find( 'textarea' )
				.val( function( column_idx, value ) {
					// If the cell is not a formula, there's nothing to do here
					if ( ( '' === value ) || ( '=' !== value.charAt(0) ) ) {
						return value;
					}

					return value.replace( /([A-Z]+[0-9]+)(?::([A-Z]+[0-9]+))?/g, function( full_match, first_cell, second_cell ) {
						// first_cell must always exist, while second_cell only exists in ranges like A4:B7
						// we will use full_match as our result variable, so that we don't need an extra one

						if ( ! known_references.hasOwnProperty( first_cell ) ) {
							$cell = $id( 'cell-' + first_cell );
							if ( $cell.length ) {
								known_references[ first_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
							} else {
								known_references[ first_cell ] = first_cell;
							}
						}
						full_match = known_references[ first_cell ];

						if ( ( 'undefined' !== typeof second_cell ) && ( '' !== second_cell ) ) { // Chrome and IE pass an undefined variable, while Firefox passes an empty string
							if ( ! known_references.hasOwnProperty( second_cell ) ) {
								$cell = $id( 'cell-' + second_cell );
								if ( $cell.length ) {
									known_references[ second_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
								} else {
									known_references[ second_cell ] = second_cell;
								}
							}
							full_match += ':' + known_references[ second_cell ];
						}

						return full_match;
					} );
				} )
				.attr( 'name', function( column_idx /*, old_name */ ) {
					return 'table[data][' + row_idx + '][' + column_idx + ']';
				} );

			$row.find( '.move-handle' ).html( row_idx + 1 );
		} )
		.each( function( row_idx, row ) { // need a second loop here to not break logic in previous loop, that queries textareas by their old ID
			$( row ).find( 'textarea' ).attr( 'id', function( column_idx /*, old_id */ ) {
				return 'cell-' + tp.columns.number_to_letter( column_idx + 1 ) + ( row_idx + 1 );
			} );
		});
		$id( 'edit-form-head' ).find( '.move-handle' )
			.html( function( idx ) { return tp.columns.number_to_letter( idx + 1 ); } );

		$id( 'number-rows' ).val( tp.table.rows );
		$id( 'number-columns' ).val( tp.table.columns );

		tp.table.set_table_changed();
	};

	tp.save_changes = {
		trigger: function( event ) {
			// validation checks
			if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
				alert( tablepress_strings.num_pagination_entries_invalid );
				$id( 'option-datatables-paginate_entries' ).focus().select();
				return;
			}
			if ( ( /[^A-Za-z0-9- _]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
				alert( tablepress_strings.extra_css_classes_invalid );
				$id( 'option-extra-css-classes' ).focus().select();
				return;
			}

			if ( event.shiftKey ) {
				tp.made_changes = false; // to prevent onunload warning
				$id( 'tablepress-page' ).find( 'form' ).submit();
				return;
			}

			$(this).closest( 'p' ).append( '<span class="animation-saving spinner is-active" title="' + tablepress_strings.saving_changes + '"/>' );
			$( '.save-changes-button' ).prop( 'disabled', true );
			$( 'body' ).addClass( 'wait' );

			$.ajax({
				'type': 'POST',
				'url': ajaxurl,
				'data': tp.table.prepare_ajax_request( 'tablepress_save_table', '#nonce-edit-table' ),
				'success': tp.save_changes.ajax_success,
				'error': tp.save_changes.ajax_error,
				'dataType': 'json'
			} );
		},
		ajax_success: function( data, status /*, jqXHR */ ) {
			if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
				tp.save_changes.error( 'AJAX call successful, but unclear status. Try again while holding down the &#8220;Shift&#8221; key.' );
			} else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) ) {
				tp.save_changes.error( 'AJAX call successful, but unclear data. Try again while holding down the &#8220;Shift&#8221; key.' );
			} else if ( true !== data.success ) {
				var debug_html = '';
				// Print debug information, if we are in debug mode
				if ( ( 'undefined' !== typeof data.error_details ) && ( tablepress_options.print_debug_output ) ) {
					debug_html = '</p><p>These errors were encountered:</p><pre>' + data.error_details + '</pre><p>'; // Some HTML magic because this is wrapped in <p> when printed
				}
				tp.save_changes.error( 'AJAX call successful, internal saving process failed. Try again while holding down the &#8220;Shift&#8221; key.' + debug_html );
			} else {
				tp.save_changes.success( data );
			}
		},
		ajax_error: function( jqXHR, status, error_thrown ) {
			tp.save_changes.error( 'AJAX call failed: ' + status + ' - ' + error_thrown + '. Try again while holding down the &#8220;Shift&#8221; key.' );
		},
		success: function( data ) {
			// saving was successful, so the original ID has changed to the (maybe) new ID -> we need to adjust all occurrences
			if ( tp.table.id !== data.table_id ) {
				// update URL (for HTML5 browsers only), but only if ID really changed, to not get dummy entries in the browser history
				if ( ( 'pushState' in window.history ) && null !== window.history.pushState ) {
					window.history.pushState( '', '', window.location.href.replace( /table_id=[0-9a-zA-Z-_]+/gi, 'table_id=' + data.table_id ) );
				}
			}
			// update CSS class for data field form
			$id( 'edit-form' ).removeClass( 'tablepress-edit-screen-id-' + tp.table.id ).addClass( 'tablepress-edit-screen-id-' + data.table_id );
			// update table ID in input fields (type text and hidden)
			tp.table.id = tp.table.new_id = data.table_id;
			$id( 'table-id' ).val( tp.table.id );
			$id( 'table-new-id' ).val( tp.table.new_id );
			// update the Shortcode text field
			$( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' );
			// update the nonces
			$id( 'nonce-edit-table' ).val( data.new_edit_nonce );
			$id( 'nonce-preview-table' ).val( data.new_preview_nonce );
			// update URLs in Preview links
			var $show_preview_buttons = $( '.show-preview-button' );
			if ( $show_preview_buttons.length ) { // check necessary, because Preview button might not be visible
				$show_preview_buttons.attr( 'href',
					$show_preview_buttons.first().attr( 'href' )
						.replace( /item=[a-zA-Z0-9_-]+/g, 'item=' + data.table_id )
						.replace( /&_wpnonce=[a-z0-9]+/ig, '&_wpnonce=' + data.new_preview_nonce )
				);
			}
			// update last modified date and user nickname
			$id( 'last-modified' ).text( data.last_modified );
			$id( 'last-editor' ).text( data.last_editor );
			tp.table.unset_table_changed();
			tp.save_changes.after_saving_dialog( 'success', tablepress_strings[ data.message ] );
		},
		error: function( message ) {
			tp.save_changes.after_saving_dialog( 'error', message );
		},
		after_saving_dialog: function( type, message ) {
			if ( 'undefined' === typeof message ) {
				message = '';
			} else {
				message = ': ' + message;
			}
			var delay,
				div_class = 'save-changes-' + type;
			if ( 'success' === type ) {
				div_class += ' notice notice-success';
				delay = 3000;
			} else {
				div_class += ' notice notice-error';
				delay = 6000;
			}
			$( '.animation-saving' ).closest( 'p' )
				.after( '<div class="ajax-alert ' + div_class + '"><p>' + tablepress_strings['save_changes_' + type] + message + '</p></div>' );
			$( '.animation-saving' ).remove();
			$( '.save-changes-' + type ).delay( delay ).fadeOut( 2000, function() { $(this).remove(); } );
			$( '.save-changes-button' ).prop( 'disabled', false );
			$( 'body' ).removeClass( 'wait' );
		}
	};

	tp.init = function() {
		var callbacks = {
			'click': {
				'#rows-insert':			tp.rows.insert,
				'#columns-insert':		tp.columns.insert,
				'#rows-duplicate':		tp.rows.duplicate,
				'#columns-duplicate':	tp.columns.duplicate,
				'#rows-remove':			tp.rows.remove,
				'#columns-remove':		tp.columns.remove,
				'#rows-hide':			tp.rows.hide,
				'#columns-hide':		tp.columns.hide,
				'#rows-unhide':			tp.rows.unhide,
				'#columns-unhide':		tp.columns.unhide,
				'#rows-append':			tp.rows.append,
				'#columns-append':		tp.columns.append,
				'#link-add':			tp.content.link.add,
				'#image-add':			tp.content.image.add,
				'#span-add-rowspan':	function() { tp.content.span.add( '#rowspan#' ); },
				'#span-add-colspan':	function() { tp.content.span.add( '#colspan#' ); },
				'.show-preview-button': tp.table.preview.trigger,
				'.save-changes-button': tp.save_changes.trigger,
				'.show-help-box':		function() {
					$(this).next().wpdialog( {
						title: $(this).attr( 'title' ),
						height: 470,
						width: 320,
						modal: true,
						dialogClass: 'wp-dialog',
						resizable: false
					} );
				}
			},
			'keyup': {
				'#table-new-id':		tp.check.table_id
			},
			'change': {
				'#option-table-head':			tp.table.change_table_head,
				'#option-table-foot':			tp.table.change_table_foot,
				'#option-use-datatables':		tp.table.change_datatables,
				'#option-datatables-paginate':	tp.table.change_datatables_pagination
			},
			'blur': {
				'#table-new-id':		tp.table.change_id	// onchange would not recognize changed values from tp.check.table_id
			}
		},
		$table = $id( 'edit-form-body' );

		$.each( callbacks, function( event, event_callbacks ) {
			$.each( event_callbacks, function( selector, callback ) {
				$( selector ).on( event, callback );
			} );
		} );

		$( window ).on( 'beforeunload', tp.check.changes_saved );

		// do this before the next lines, to not trigger set_table_changed()
		$id( 'option-table-head' ).change(); // init changed/disabled states of DataTables JS features checkboxes
		$id( 'option-print-name' ).change( tp.table.change_print_name_description ).change(); // init dropdowns for name and description position
		$id( 'option-print-description' ).change( tp.table.change_print_name_description ).change();

		// just once is enough, will be reset after saving
		$table.one( 'change', 'textarea', tp.table.set_table_changed );
		$( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );

		if ( tablepress_options.cells_advanced_editor ) {
			$table.on( 'click', 'textarea', tp.cells.advanced_editor.keyopen );
			$id( 'advanced-editor-open' ).on( 'click', tp.cells.advanced_editor.buttonopen );
			$id( 'advanced-editor-confirm' ).on( 'click', tp.cells.advanced_editor.save );
			$id( 'advanced-editor-cancel' ).on( 'click', tp.cells.advanced_editor.close );
			$id( 'advanced-editor' ).wpdialog( {
				autoOpen: false,
				title: $id( 'advanced-editor-open' ).val(),
				width: 600,
				modal: true,
				dialogClass: 'wp-dialog',
				resizable: false
			} );
			// Fix issue with input fields not being usable (they are immediately losing focus without this) in the wpLink dialog when called through the "Advanced Editor"
			$id( 'wp-link' ).on( 'focus', 'input', function( event ) {
				event.stopPropagation();
			} );
		} else {
			$id( 'advanced-editor-open' ).hide();
		}

		// Fix issue with input fields not being usable (they are immediately losing focus without this) in the sidebar of the new Media Manager
		$( 'body' ).on( 'focus', '.media-modal .media-frame-content input, .media-modal .media-frame-content textarea', function( event ) {
			event.stopPropagation();
		} );

		if ( tablepress_options.cells_auto_grow ) {
			$table.on( 'focus', 'textarea', tp.cells.autogrow );
		}

		$id( 'edit-form-body' ).on( 'click', 'input:checkbox', { parent: '#edit-form-body' }, tp.cells.checkboxes.multi_select );
		$id( 'edit-form-foot' ).on( 'click', 'input:checkbox', { parent: '#edit-form-foot' }, tp.cells.checkboxes.multi_select );

		$id( 'edit-form-head' ).on( 'click', '.sort-control', tp.rows.sort );

		// on form submit: Enable disabled fields, so that they are transmitted in the POST request
		$id( 'tablepress-page' ).find( 'form' ).on( 'submit', function() {
			$(this).find( '.tablepress-postbox-table' ).find( 'input, select' ).prop( 'disabled', false );
		} );

		$table.sortable( {
			axis: 'y',
			containment: $id( 'edit-form' ), // to get better behavior when dragging before/after the first/last row
			forceHelperSize: true, // necessary?
			handle: '.move-handle',
			start: tp.rows.move.start,
			change: tp.rows.move.change,
			stop: tp.rows.move.stop,
			update: tp.reindex
		} ); // disableSelection() prohibits selection of text in textareas via keyboard

		$id( 'edit-form-head' ).sortable( {
			axis: 'x',
			items: '.head',
			containment: 'parent',
			forceHelperSize: true, // necessary?
			helper: 'clone',
			handle: '.move-handle',
			start: tp.columns.move.start,
			stop: tp.columns.move.stop,
			change: tp.columns.move.change,
			sort: tp.columns.move.sort
		} ).disableSelection();
	};

	// Run TablePress initialization.
	tp.init();

} );
home/xbodynamge/crosstraining/wp-content/plugins/tablepress/admin/js/edit.js000060400000135321151133763440023440 0ustar00/**
 * JavaScript code for the "Edit" screen
 *
 * @package TablePress
 * @subpackage Views JavaScript
 * @author Tobias Bäthge
 * @since 1.0.0
 */

/* global alert, confirm, tp, tablepress_strings, tablepress_options, ajaxurl, wpLink, tb_show, wp, JSON */

// Ensure the global `tp` object exists.
window.tp = window.tp || {};

jQuery( document ).ready( function( $ ) {

	'use strict';

	/* Wrapper to find elements in the page faster with JS-native functions */
	var $id = function( element_id ) {
		return $( document.getElementById( element_id ) );
	};

	/**
	 * TablePress object, mostly with functionality for the "Edit" screen
	 *
	 * @since 1.0.0
	 */
	tp.made_changes = false;

	tp.table = {
		id: $id( 'table-id' ).val(),
		new_id: $id( 'table-new-id' ).val(),
		rows: parseInt( $id( 'number-rows' ).val(), 10 ),
		columns: parseInt( $id( 'number-columns' ).val(), 10 ),
		head: $id( 'option-table-head' ).prop( 'checked' ),
		foot: $id( 'option-table-foot' ).prop( 'checked' ),
		no_data_columns_pre: 2,
		no_data_columns_post: 1,
		body_cells_pre: '<tr><td><span class="move-handle"></span></td><td><input type="checkbox" /><input type="hidden" class="visibility" name="table[visibility][rows][]" value="1" /></td>',
		body_cells_post: '<td><span class="move-handle"></span></td></tr>',
		body_cell: '<td><textarea rows="1"></textarea></td>',
		head_cell: '<th class="head"><span class="sort-control sort-desc" title="' + tablepress_strings.sort_desc + '"><span class="sorting-indicator"></span></span><span class="sort-control sort-asc" title="' + tablepress_strings.sort_asc + '"><span class="sorting-indicator"></span></span><span class="move-handle"></span></th>',
		foot_cell: '<th><input type="checkbox" /><input type="hidden" class="visibility" name="table[visibility][columns][]" value="1" /></th>',
		set_table_changed: function() {
			tp.made_changes = true;
		},
		unset_table_changed: function() {
			tp.made_changes = false;
			$id( 'edit-form-body' ).one( 'change', 'textarea', tp.table.set_table_changed );
			// @TODO: maybe use .tablepress-postbox-table here and further below
			$( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );
		},
		change_id: function( /* event */ ) {
			// empty table IDs are not allowed
			if ( '' === $.trim( $id( 'table-new-id' ).val() ) ) {
				alert( tablepress_strings.table_id_not_empty );
				$id( 'table-new-id' ).val( tp.table.new_id ).focus().select();
				return;
			}
			// the '0' table ID is not allowed
			if ( '0' === $.trim( $id( 'table-new-id' ).val() ) ) {
				alert( tablepress_strings.table_id_not_zero );
				$id( 'table-new-id' ).val( tp.table.new_id ).focus().select();
				return;
			}

			if ( this.value === tp.table.new_id ) {
				return;
			}

			if ( confirm( tablepress_strings.ays_change_table_id ) ) {
				tp.table.new_id = this.value;
				$( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' ).click(); // click() to focus and select
				tp.table.set_table_changed();
			} else {
				$(this).val( tp.table.new_id );
			}
		},
		change_table_head: function( /* event */ ) {
			tp.table.head = $(this).prop( 'checked' );
			$id( 'option-use-datatables' ).prop( 'disabled', ! tp.table.head ).change();
			$id( 'notice-datatables-head-row' ).toggle( ! tp.table.head );
			tp.rows.stripe();
		},
		change_table_foot: function( /* event */ ) {
			tp.table.foot = $(this).prop( 'checked' );
			tp.rows.stripe();
		},
		change_print_name_description: function( /* event */ ) {
			$id( this.id + '-position' ).prop( 'disabled', ! $(this).prop( 'checked' ) );
		},
		change_datatables: function() {
			var $datatables_checkbox = $id( 'option-use-datatables' ),
				checkboxes_disabled = ! ( $datatables_checkbox.prop( 'checked' ) && ! $datatables_checkbox.prop( 'disabled' ) );
			$datatables_checkbox.closest( 'tbody' ).find( 'input' ).not( $datatables_checkbox ).prop( 'disabled', checkboxes_disabled );
			tp.table.change_datatables_pagination();
		},
		change_datatables_pagination: function() {
			var $pagination_checkbox = $id( 'option-datatables-paginate' ),
				pagination_enabled = ( $pagination_checkbox.prop( 'checked' ) && ! $pagination_checkbox.prop( 'disabled' ) );
			$id( 'option-datatables-lengthchange' ).prop( 'disabled', ! pagination_enabled );
			$id( 'option-datatables-paginate_entries' ).prop( 'disabled', ! pagination_enabled );
		},
		prepare_ajax_request: function( wp_action, wp_nonce ) {
			var $table_body = $id( 'edit-form-body' ),
				table_data = [],
				table_options,
				table_number = { rows: tp.table.rows, columns: tp.table.columns, hidden_rows: 0, hidden_columns: 0 },
				table_visibility = { rows: [], columns: [] };

			$table_body.children().each( function( idx, row ) {
				table_data[ idx ] = $( row ).find( 'textarea' )
					.map( function() {
						return $(this).val();
					} )
					.get();
			} );
			table_data = JSON.stringify( table_data );

			// @TODO: maybe for options saving: https://stackoverflow.com/questions/1184624/convert-form-data-to-javascript-object-with-jquery
			// or each()-loop through all checkboxes/textfields/selects
			table_options = {
				// Table Options
				table_head: tp.table.head,
				table_foot: tp.table.foot,
				alternating_row_colors: $id( 'option-alternating-row-colors' ).prop( 'checked' ),
				row_hover: $id( 'option-row-hover' ).prop( 'checked' ),
				print_name: $id( 'option-print-name' ).prop( 'checked' ),
				print_description: $id( 'option-print-description' ).prop( 'checked' ),
				print_name_position: $id( 'option-print-name-position' ).val(),
				print_description_position: $id( 'option-print-description-position' ).val(),
				extra_css_classes: $id( 'option-extra-css-classes' ).val(),
				// DataTables JS features
				use_datatables: $id( 'option-use-datatables' ).prop( 'checked' ),
				datatables_sort: $id( 'option-datatables-sort' ).prop( 'checked' ),
				datatables_filter: $id( 'option-datatables-filter' ).prop( 'checked' ),
				datatables_paginate: $id( 'option-datatables-paginate' ).prop( 'checked' ),
				datatables_lengthchange: $id( 'option-datatables-lengthchange' ).prop( 'checked' ),
				datatables_paginate_entries: $id( 'option-datatables-paginate_entries' ).val(),
				datatables_info: $id( 'option-datatables-info' ).prop( 'checked' ),
				datatables_scrollx: $id( 'option-datatables-scrollx' ).prop( 'checked' ),
				datatables_custom_commands: $id( 'option-datatables-custom-commands' ).val()
			};
			table_options = JSON.stringify( table_options );

			table_visibility.rows = $table_body.find( 'input[type="hidden"]' )
				.map( function() {
					if ( '1' === $(this).val() ) {
						return 1;
					}
					table_number.hidden_rows += 1;
					return 0;
				} )
				.get();
			table_visibility.columns = $id( 'edit-form-foot' ).find( 'input[type="hidden"]' )
				.map( function() {
					if ( '1' === $(this).val() ) {
						return 1;
					}
					table_number.hidden_columns += 1;
					return 0;
				} )
				.get();
			table_visibility = JSON.stringify( table_visibility );

			// request_data =
			return {
				action: wp_action,
				_ajax_nonce : $( wp_nonce ).val(),
				tablepress: {
					id: tp.table.id,
					new_id: tp.table.new_id,
					name: $id( 'table-name' ).val(),
					description: $id( 'table-description' ).val(),
					number: table_number,
					data: table_data,
					options: table_options,
					visibility: table_visibility
				}
			};
		},
		preview: {
			trigger: function( /* event */ ) {
				if ( ! tp.made_changes ) {
					tp.table.preview.show( $(this).attr( 'href' ) + '&TB_iframe=true' );
					return false;
				}

				// validation checks
				if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
					alert( tablepress_strings.num_pagination_entries_invalid );
					$id( 'option-datatables-paginate_entries' ).focus().select();
					return;
				}
				if ( ( /[^A-Za-z0-9- _]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
					alert( tablepress_strings.extra_css_classes_invalid );
					$id( 'option-extra-css-classes' ).focus().select();
					return;
				}

				$(this).closest( 'p' ).append( '<span class="animation-preview spinner is-active" title="' + tablepress_strings.preparing_preview + '"/>' );
				$( 'body' ).addClass( 'wait' );
				$id( 'table-preview' ).empty(); // clear preview

				$.ajax({
					'type': 'POST',
					'url': ajaxurl,
					'data': tp.table.prepare_ajax_request( 'tablepress_preview_table', '#nonce-preview-table' ),
					'success': tp.table.preview.ajax_success,
					'error': tp.table.preview.ajax_error,
					'dataType': 'json'
				} );

				return false;
			},
			ajax_success: function( data, status /*, jqXHR */ ) {
				if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
					tp.table.preview.error( 'AJAX call successful, but unclear status.' );
				} else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) || ( true !== data.success ) ) {
					tp.table.preview.error( 'AJAX call successful, but unclear data.' );
				} else {
					tp.table.preview.success( data );
				}
			},
			ajax_error: function( jqXHR, status, error_thrown ) {
				tp.table.preview.error( 'AJAX call failed: ' + status + ' - ' + error_thrown );
			},
			success: function( data ) {
				$id( 'table-preview' ).empty();
				$( '<iframe id="table-preview-iframe" />' ).load( function() {
					var $iframe = $(this).contents();
					$iframe.find( 'head' ).append( data.head_html );
					$iframe.find( 'body' ).append( data.body_html );
				} ).appendTo( '#table-preview' );
				$( '.animation-preview' ).remove();
				$( 'body' ).removeClass( 'wait' );
				tp.table.preview.show( '#TB_inline?inlineId=preview-container' );
			},
			error: function( message ) {
				$( '.animation-preview' ).closest( 'p' )
					.after( '<div class="ajax-alert preview-error error"><p>' + tablepress_strings.preview_error + ': ' + message + '</p></div>' );
				$( '.animation-preview' ).remove();
				$( '.preview-error' ).delay( 6000 ).fadeOut( 2000, function() { $(this).remove(); } );
				$( 'body' ).removeClass( 'wait' );
			},
			show: function( url ) {
				var width = $( window ).width() - 120,
					height = $( window ).height() - 120;
				if ( $( '#wpadminbar' ).length ) {
					height -= parseInt( $( '#wpadminbar' ).css( 'height' ), 10 );
				}
				tb_show( $( '.show-preview-button' ).first().text(), url + '&height=' + height + '&width=' + width, false );
			}
		}
	};

	tp.rows = {
		create: function( num_rows ) {
			var i, j,
				column_idxs,
				new_rows = '';

			for ( i = 0; i < num_rows; i++ ) {
				new_rows += tp.table.body_cells_pre;
				for ( j = 0; j < tp.table.columns; j++ ) {
					new_rows += tp.table.body_cell;
				}
				new_rows += tp.table.body_cells_post;
			}

			column_idxs = $id( 'edit-form-foot' ).find( '.column-hidden' )
				.map( function() { return $(this).index(); } ).get();
			return $( new_rows ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.addClass( 'column-hidden' );
			} );
		},
		append: function( /* event */ ) {
			var num_rows = $id( 'rows-append-number' ).val();

			if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_rows ) ) {
				alert( tablepress_strings.append_num_rows_invalid );
				$id( 'rows-append-number' ).focus().select();
				return;
			}

			$id( 'edit-form-body' ).append( tp.rows.create( num_rows ) );

			tp.rows.stripe();
			tp.reindex();
		},
		insert: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.before( tp.rows.create( 1 ) );

			tp.rows.stripe();
			tp.reindex();
		},
		duplicate: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.each( function( idx, row ) {
				var $row = $( row ),
					$textareas = $row.find( 'textarea' ),
					$duplicated_row = $row.clone();
				$duplicated_row.find( 'textarea' ).removeAttr( 'id' ).each( function( idx, cell ) {
					$( cell ).val( $textareas.eq( idx ).val() ); // setting val() is necessary, as clone() doesn't copy the current value, see jQuery bugs 5524, 2285, 3016
				} );
				$row.after( $duplicated_row );
			} );

			tp.rows.stripe();
			tp.reindex();
		},
		hide: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.addClass( 'row-hidden' ).find( '.visibility' ).val( '0' );

			tp.rows.stripe();
			tp.table.set_table_changed();
		},
		unhide: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.removeClass( 'row-hidden' ).find( '.visibility' ).val( '1' );

			tp.rows.stripe();
			tp.table.set_table_changed();
		},
		remove: function( /* event */ ) {
			var confirm_message,
				$selected_rows = $id( 'edit-form-body' ).find( 'input:checked' ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			if ( tp.table.rows === $selected_rows.length ) {
				alert( tablepress_strings.no_remove_all_rows );
				return;
			}

			if ( 1 === $selected_rows.length ) {
				confirm_message = tablepress_strings.ays_remove_rows_singular;
			} else {
				confirm_message = tablepress_strings.ays_remove_rows_plural;
			}
			if ( ! confirm( confirm_message ) ) {
				return;
			}

			$selected_rows.remove();

			tp.rows.stripe();
			tp.reindex();
		},
		move: {
			start: function( event, ui ) {
				$( ui.placeholder ).removeClass( 'row-hidden' ).css( 'visibility', 'visible' )
					.html( '<td colspan="' + ( tp.table.columns + tp.table.no_data_columns_pre + tp.table.no_data_columns_post ) + '"><div/></td>' );
				$( ui.helper ).removeClass( 'odd head-row foot-row' );
			},
			change: function( event, ui ) {
				tp.rows.stripe( ui.helper );
			},
			stop: function( /* event, ui */ ) {
				tp.rows.stripe();
			}
		},
		sort: function() {
			var column_idx = $(this).parent().index(),
				direction = ( $(this).hasClass( 'sort-asc' ) ) ? 1 : -1,
				$table_body = $('#edit-form-body'),
				$head_rows = $table_body.find( '.head-row' ).prevAll().addBack(),
				$foot_rows = $table_body.find( '.foot-row' ).nextAll().addBack(),
				rows = $table_body.children().not( $head_rows ).not( $foot_rows ).get(),
				/*
				 * Natural Sort algorithm for Javascript - Version 0.8.1 - Released under MIT license
				 * Author: Jim Palmer (based on chunking idea from Dave Koelle)
				 * See: https://github.com/overset/javascript-natural-sort and http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm-with-unicode-support/
				 */
				natural_sort = function( a, b ) {
					var re = /(^([+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|^0x[\da-fA-F]+$|\d+)/g,
						sre = /^\s+|\s+$/g,   // trim pre-post whitespace
						snre = /\s+/g,        // normalize all whitespace to single ' ' character
						dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
						hre = /^0x[0-9a-f]+$/i,
						ore = /^0/,
						// strip whitespace
						x = a.replace(sre, '') || '',
						y = b.replace(sre, '') || '',
						// chunk/tokenize
						xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
						yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
						// numeric, hex or date detection
						xD = parseInt(x.match(hre), 16) || (xN.length !== 1 && Date.parse(x)),
						yD = parseInt(y.match(hre), 16) || xD && y.match(dre) && Date.parse(y) || null,
						normChunk = function(s, l) {
							// normalize spaces; find floats not starting with '0', string or 0 if not defined (Clint Priest)
							return (!s.match(ore) || l === 1) && parseFloat(s) || s.replace(snre, ' ').replace(sre, '') || 0;
						},
						oFxNcL, oFyNcL;
					// first try and sort Hex codes or Dates
					if (yD) {
						if (xD < yD) { return -1; }
						else if (xD > yD) { return 1; }
					}
					// natural sorting through split numeric strings and default strings
					for(var cLoc = 0, xNl = xN.length, yNl = yN.length, numS = Math.max(xNl, yNl); cLoc < numS; cLoc++) {
						oFxNcL = normChunk(xN[cLoc] || '', xNl);
						oFyNcL = normChunk(yN[cLoc] || '', yNl);
						// handle numeric vs string comparison - number < string - (Kyle Adams)
						if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
							return isNaN(oFxNcL) ? 1 : -1;
						}
						// if unicode use locale comparison
						if (/[^\x00-\x80]/.test(oFxNcL + oFyNcL) && oFxNcL.localeCompare) {
							var comp = oFxNcL.localeCompare(oFyNcL);
							return comp / Math.abs(comp);
						}
						if (oFxNcL < oFyNcL) { return -1; }
						else if (oFxNcL > oFyNcL) { return 1; }
					}
				};

			$.each( rows, function( row_idx, row ) {
				row.sort_key = ( '' + $( row ).children().eq( column_idx ).find( 'textarea' ).val() ).toLowerCase(); // convert to string, and lower case for case insensitive sorting
			} );

			rows.sort( function( a, b ) {
				return direction * natural_sort( a.sort_key, b.sort_key );
			} );

			// might not be necessary:
			$.each( rows, function( row_idx, row ) {
				row.sort_key = null;
			} );

			$table_body.append( $head_rows );
			$table_body.append( rows );
			$table_body.append( $foot_rows );

			tp.rows.stripe();
			tp.reindex();
		},
		stripe: function( helper ) {
			if ( 'undefined' === typeof helper ) {
				helper = null;
			}
			helper = $( helper );
			var $rows = $id( 'edit-form-body' ).children().removeClass( 'odd head-row foot-row' ).not( helper );
			$rows.filter( ':even' ).addClass( 'odd' );
			$rows = $rows.not( '.row-hidden' );
			if ( helper.hasClass( 'row-hidden' ) ) {
				$rows = $rows.not( '.ui-sortable-placeholder' );
			}
			if ( tp.table.head ) {
				$rows.first().addClass( 'head-row' );
			}
			if ( tp.table.foot ) {
				$rows.last().addClass( 'foot-row' );
			}
		}
	};

	tp.columns = {
		append: function( /* event */ ) {
			var i,
				num_columns = $id( 'columns-append-number' ).val(),
				new_head_cells = '', new_body_cells = '', new_foot_cells = '';

			if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_columns ) ) {
				alert( tablepress_strings.append_num_columns_invalid );
				$id( 'columns-append-number' ).focus().select();
				return;
			}

			for ( i = 0; i < num_columns; i++ ) {
				new_body_cells += tp.table.body_cell;
				new_head_cells += tp.table.head_cell;
				new_foot_cells += tp.table.foot_cell;
			}

			$id( 'edit-form-body' ).children().each( function( row_idx, row ) {
				$( row ).children().slice( - tp.table.no_data_columns_post )
					.before( new_body_cells );
			} );
			$id( 'edit-form-head' ).children().slice( - tp.table.no_data_columns_post )
				.before( new_head_cells );
			$id( 'edit-form-foot' ).children().slice( - tp.table.no_data_columns_post )
				.before( new_foot_cells );

			tp.reindex();
		},
		insert: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.before( tp.table.body_cell );
			} );
			$id( 'edit-form-head' ).children()
				.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
				.before( tp.table.head_cell );
			$selected_columns.before( tp.table.foot_cell );

			tp.reindex();
		},
		duplicate: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form' ).find( 'tr' ).each( function( row_idx, row ) {
				$( row ).children().each( function( idx, cell ) {
					if ( -1 !== $.inArray( idx, column_idxs ) ) {
						var $cell = $( cell ),
							$duplicated_cell = $cell.clone();
							$duplicated_cell.find( 'textarea' ).removeAttr( 'id' ).val( $cell.find( 'textarea' ).val() ); // setting val() is necessary, as clone() doesn't copy the current value, see jQuery bugs 5524, 2285, 3016
						$cell.after( $duplicated_cell );
					}
				} );
			} );

			tp.reindex();
		},
		hide: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.addClass( 'column-hidden' );
			} );
			$selected_columns.addClass( 'column-hidden' ).find( '.visibility' ).val( '0' );

			tp.table.set_table_changed();
		},
		unhide: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.removeClass( 'column-hidden' );
			} );
			$selected_columns.removeClass( 'column-hidden' ).find( '.visibility' ).val( '1' );

			tp.table.set_table_changed();
		},
		remove: function( /* event */ ) {
			var column_idxs,
				confirm_message,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			if ( tp.table.columns === $selected_columns.length ) {
				alert( tablepress_strings.no_remove_all_columns );
				return;
			}

			if ( 1 === $selected_columns.length ) {
				confirm_message = tablepress_strings.ays_remove_columns_singular;
			} else {
				confirm_message = tablepress_strings.ays_remove_columns_plural;
			}
			if ( ! confirm( confirm_message ) ) {
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.remove();
			} );
			$selected_columns.remove();

			tp.reindex();
		},
		move: {
			source_idx: -1,
			target_idx: -1,
			$rows: null,
			$row_children: null,
			$cell: null,
			$cells: null,
			$placeholder: null,
			$helper: null,
			start: function( event, ui ) {
				var $item = $( ui.item ),
					column_width;

				tp.columns.move.source_idx = $item.index();

				tp.columns.move.$rows = $id( 'edit-form-body' ).children().add( '#edit-form-foot' );

				tp.columns.move.$cells = tp.columns.move.$rows
					.find( ':nth-child(' + ( tp.columns.move.source_idx + 1 ) + ')' )
					.each( function() {
						tp.columns.move.$cell = $(this);
						$( '<td class="move-placeholder"><div/></td>' ).insertBefore( tp.columns.move.$cell );
						tp.columns.move.$cell.insertAfter( tp.columns.move.$cell.nextAll().last() )
							.clone().addClass( 'move-hover' ).insertAfter( tp.columns.move.$cell )
							.find( 'textarea' ).val( tp.columns.move.$cell.find( 'textarea' ).val() );
							// last line works around problem with clone() of textareas, see jQuery bugs 5524, 2285, 3016
					} )
					.hide();

				tp.columns.move.$helper = tp.columns.move.$rows.find( '.move-hover' );
				/* // seems not to be working for rows, so disable it for columns
					.each( function() {
						tp.columns.move.$cell = $(this);
						tp.columns.move.$cell.css( 'top', ( tp.columns.move.$cell.position().top - 3 ) + 'px' );
					} );
				*/

				column_width = tp.columns.move.$helper.eq(1).width(); // eq(0) is table foot
				tp.columns.move.$helper.eq(0).width( column_width );
				tp.columns.move.$placeholder = tp.columns.move.$rows.find( '.move-placeholder' );
				tp.columns.move.$placeholder.find( 'div' ).width( column_width );
			},
			change: function( event, ui ) {
				tp.columns.move.target_idx = $( ui.placeholder ).index();

				if ( ( tp.columns.move.target_idx - tp.columns.move.source_idx ) === 1 ) {
					tp.columns.move.target_idx += 1;
				} else {
					if ( tp.columns.move.target_idx === tp.columns.move.source_idx ) {
						tp.columns.move.target_idx -= 1;
					}
				}

				tp.columns.move.$placeholder.each( function() {
					tp.columns.move.$cell = $(this);
					tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().children().eq( tp.columns.move.target_idx ) );
				} );

				if ( tp.columns.move.target_idx > tp.columns.move.source_idx ) {
					tp.columns.move.target_idx -= 1;
				}

				tp.columns.move.source_idx = tp.columns.move.target_idx;
			},
			sort: function( event, ui ) {
				tp.columns.move.$helper.css( 'left', ui.position.left );
			},
			stop: function( /* event, ui */ ) {
				tp.columns.move.$helper.remove();
				tp.columns.move.$cells
					.each( function() {
						tp.columns.move.$cell = $(this);
						tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().find( '.move-placeholder' ) );
					} )
					.show();
				tp.columns.move.$placeholder.remove();

				tp.columns.move.source_idx = tp.columns.move.target_idx = -1;
				tp.columns.move.$rows = tp.columns.move.$row_children = tp.columns.move.$cell = tp.columns.move.$cells = tp.columns.move.$placeholder = tp.columns.move.$helper = null;

				tp.reindex();
			}
		},
		number_to_letter: function( number ) {
			var column = '';
			while ( number > 0 ) {
				column = String.fromCharCode( 65 + ( ( number-1) % 26 ) ) + column;
				number = Math.floor( (number-1) / 26 );
			}
			return column;
		}/*,
		letter_to_number: function( column ) {
			column = column.toUpperCase();
			var count = column.length,
				number = 0,
				i;
			for ( i = 0; i < count; i++ ) {
				number += ( column.charCodeAt( count-1-i ) - 64 ) * Math.pow( 26, i );
			}
			return number;
		}*/
	};

	tp.cells = {
		$focus: $( null ),
		$textarea: null,
		autogrow: function( /* event */ ) {
			tp.cells.$focus.removeClass( 'focus' );
			tp.cells.$focus = $(this).closest( 'tr' ).addClass( 'focus' );
		},
		advanced_editor: {
			prompt_shown: false,
			keyopen: function( event ) {
				if ( ! event.shiftKey ) {
					return;
				}

				var $advanced_editor = $id( 'advanced-editor-content' );
				tp.cells.$textarea = $(this).blur();
				$advanced_editor.val( tp.cells.$textarea.val() );
				$id( 'advanced-editor' ).wpdialog( 'open' );
				$advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
				$advanced_editor.focus();
			},
			buttonopen: function() {
				if ( ! tp.cells.advanced_editor.prompt_shown ) {
					if ( ! confirm( tablepress_strings.advanced_editor_open ) ) {
						return;
					}
				}

				tp.cells.advanced_editor.prompt_shown = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var $advanced_editor = $id( 'advanced-editor-content' );
					tp.cells.$textarea = $(this).blur();
					$advanced_editor.val( tp.cells.$textarea.val() );
					$id( 'advanced-editor' ).wpdialog( 'open' );
					$advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
					$advanced_editor.focus();
				} );
			},
			save: function() {
				var $ve_content = $id( 'advanced-editor-content' ).blur().val();
				if ( tp.cells.$textarea.val() !== $ve_content ) {
					tp.cells.$textarea.val( $ve_content );
					// position cursor at the end
					tp.cells.$textarea.get(0).selectionStart = tp.cells.$textarea.get(0).selectionEnd = tp.cells.$textarea.val().length;
					tp.table.set_table_changed();
				}
				tp.cells.$textarea.focus();
				tp.cells.advanced_editor.close();
			},
			close: function() {
				$id( 'advanced-editor' ).wpdialog( 'close' );
				return false;
			}
		},
		checkboxes: {
			last_clicked: { '#edit-form-body' : false, '#edit-form-foot' : false },
			multi_select: function ( event ) {
				if ( 'undefined' === event.shiftKey ) {
					return true;
				}

				if ( event.shiftKey ) {
					if ( ! tp.cells.checkboxes.last_clicked[ event.data.parent ] ) {
						return true;
					}

					var $checkboxes = $( event.data.parent ).find( ':checkbox' ),
						first_cb = $checkboxes.index( tp.cells.checkboxes.last_clicked[ event.data.parent ] ),
						last_cb = $checkboxes.index( this );
					if ( first_cb !== last_cb ) {
						$checkboxes.slice( Math.min( first_cb, last_cb ), Math.max( first_cb, last_cb ) ).prop( 'checked', $(this).prop( 'checked' ) );
					}
				}
				tp.cells.checkboxes.last_clicked[ event.data.parent ] = this;
				return true;
			}
		}
	};

	tp.content = {
		link: {
			prompt_shown: false,
			add: function( /* event */ ) {
				if ( ! tp.content.link.prompt_shown ) {
					if ( ! confirm( tablepress_strings.link_add ) ) {
						return;
					}
				}

				tp.content.link.prompt_shown = true;
				// mousedown instead of click to allow selection of text
				// mousedown will set the desired target textarea, and mouseup anywhere will show the link box
				// other approaches can lead to the wrong textarea being selected
				$id( 'edit-form-body' ).one( 'mousedown', 'textarea', function() {
					var editor_id = this.id;
					$( document ).one( 'mouseup', function() {
						if ( typeof wpLink !== 'undefined' ) {
							wpLink.open( editor_id );
							tp.table.set_table_changed();
						}
					} );
				} );
			}
		},
		image: {
			prompt_shown: false,
			add: function( /* event */ ) {
				if ( ! tp.content.image.prompt_shown ) {
					if ( ! confirm( tablepress_strings.image_add ) ) {
						return;
					}
				}

				tp.content.image.prompt_shown = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var editor = this.id,
						options = {
							frame: 'post',
							state: 'insert',
							title: wp.media.view.l10n.addMedia,
							multiple: true
						};

					// Move caret to the end, to prevent inserting right between existing text, as that's ugly in small cells (though possible in the Advanced Editor and Insert Link dialog).
					this.selectionStart = this.selectionEnd = this.value.length;

					// Remove focus from the textarea to prevent Opera from showing the outline of the textarea above the modal.
					// See: WP Core #22445
					$(this).blur();

					wp.media.editor.open( editor, options );
					tp.table.set_table_changed();
				} );
			}
		},
		span: {
			prompt_shown: false,
			add: function( span ) {
				var span_add_msg = ( '#rowspan#' === span ) ? tablepress_strings.rowspan_add : tablepress_strings.colspan_add;

				// init object, due to string keys
				if ( false === tp.content.span.prompt_shown ) {
					tp.content.span.prompt_shown = {};
					tp.content.span.prompt_shown['#rowspan#'] = tp.content.span.prompt_shown['#colspan#'] = false;
				}

				// Automatically deactivate DataTables, if cells are combined
				if ( $id( 'option-use-datatables' ).prop( 'checked' ) ) {
					if ( confirm( tablepress_strings.span_add_datatables_warning ) ) {
						$id( 'option-use-datatables' ).prop( 'checked', false ).change();
					} else {
						return;
					}
				}

				if ( ! tp.content.span.prompt_shown[ span ] ) {
					if ( ! confirm( span_add_msg ) ) {
						return;
					}
				}

				tp.content.span.prompt_shown[ span ] = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var $textarea = $(this),
						col_idx = $textarea.parent().index(),
						row_idx = $textarea.closest( 'tr' ).index();
					if ( '#rowspan#' === span ) {
						if ( 0 === row_idx ) {
							alert( tablepress_strings.no_rowspan_first_row );
							return;
						}
						if ( tp.table.head && 1 === row_idx ) {
							alert( tablepress_strings.no_rowspan_table_head );
							return;
						}
						if ( tp.table.foot && ( tp.table.rows - 1 ) === row_idx ) {
							alert( tablepress_strings.no_rowspan_table_foot );
							return;
						}
					} else if ( ( '#colspan#' === span ) && ( tp.table.no_data_columns_pre === col_idx ) ) {
						alert( tablepress_strings.no_colspan_first_col );
						return;
					}
					$textarea.val( span );
					tp.table.set_table_changed();
				} );
			}
		}
	};

	tp.check = {
		table_id: function( event ) {
			if ( ( 37 === event.which ) || ( 39 === event.which ) ) {
				return;
			}
			var $input = $(this);
			$input.val( $input.val().replace( /[^0-9a-zA-Z-_]/g, '' ) );
		},
		changes_saved: function() {
			if ( tp.made_changes ) {
				return tablepress_strings.unsaved_changes_unload;
			}
		}
	};

	tp.reindex = function() {
		var $row,
			$rows = $id( 'edit-form-body' ).children(),
			$cell, known_references = {};

		tp.table.rows = $rows.length;
		if ( tp.table.rows > 0 ) {
			tp.table.columns = $rows.first().children().length - tp.table.no_data_columns_pre - tp.table.no_data_columns_post;
		} else {
			tp.table.columns = 0;
		}

		$rows
		.each( function( row_idx, row ) {
			$row = $( row );
			$row.find( 'textarea' )
				.val( function( column_idx, value ) {
					// If the cell is not a formula, there's nothing to do here
					if ( ( '' === value ) || ( '=' !== value.charAt(0) ) ) {
						return value;
					}

					return value.replace( /([A-Z]+[0-9]+)(?::([A-Z]+[0-9]+))?/g, function( full_match, first_cell, second_cell ) {
						// first_cell must always exist, while second_cell only exists in ranges like A4:B7
						// we will use full_match as our result variable, so that we don't need an extra one

						if ( ! known_references.hasOwnProperty( first_cell ) ) {
							$cell = $id( 'cell-' + first_cell );
							if ( $cell.length ) {
								known_references[ first_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
							} else {
								known_references[ first_cell ] = first_cell;
							}
						}
						full_match = known_references[ first_cell ];

						if ( ( 'undefined' !== typeof second_cell ) && ( '' !== second_cell ) ) { // Chrome and IE pass an undefined variable, while Firefox passes an empty string
							if ( ! known_references.hasOwnProperty( second_cell ) ) {
								$cell = $id( 'cell-' + second_cell );
								if ( $cell.length ) {
									known_references[ second_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
								} else {
									known_references[ second_cell ] = second_cell;
								}
							}
							full_match += ':' + known_references[ second_cell ];
						}

						return full_match;
					} );
				} )
				.attr( 'name', function( column_idx /*, old_name */ ) {
					return 'table[data][' + row_idx + '][' + column_idx + ']';
				} );

			$row.find( '.move-handle' ).html( row_idx + 1 );
		} )
		.each( function( row_idx, row ) { // need a second loop here to not break logic in previous loop, that queries textareas by their old ID
			$( row ).find( 'textarea' ).attr( 'id', function( column_idx /*, old_id */ ) {
				return 'cell-' + tp.columns.number_to_letter( column_idx + 1 ) + ( row_idx + 1 );
			} );
		});
		$id( 'edit-form-head' ).find( '.move-handle' )
			.html( function( idx ) { return tp.columns.number_to_letter( idx + 1 ); } );

		$id( 'number-rows' ).val( tp.table.rows );
		$id( 'number-columns' ).val( tp.table.columns );

		tp.table.set_table_changed();
	};

	tp.save_changes = {
		trigger: function( event ) {
			// validation checks
			if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
				alert( tablepress_strings.num_pagination_entries_invalid );
				$id( 'option-datatables-paginate_entries' ).focus().select();
				return;
			}
			if ( ( /[^A-Za-z0-9- _]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
				alert( tablepress_strings.extra_css_classes_invalid );
				$id( 'option-extra-css-classes' ).focus().select();
				return;
			}

			if ( event.shiftKey ) {
				tp.made_changes = false; // to prevent onunload warning
				$id( 'tablepress-page' ).find( 'form' ).submit();
				return;
			}

			$(this).closest( 'p' ).append( '<span class="animation-saving spinner is-active" title="' + tablepress_strings.saving_changes + '"/>' );
			$( '.save-changes-button' ).prop( 'disabled', true );
			$( 'body' ).addClass( 'wait' );

			$.ajax({
				'type': 'POST',
				'url': ajaxurl,
				'data': tp.table.prepare_ajax_request( 'tablepress_save_table', '#nonce-edit-table' ),
				'success': tp.save_changes.ajax_success,
				'error': tp.save_changes.ajax_error,
				'dataType': 'json'
			} );
		},
		ajax_success: function( data, status /*, jqXHR */ ) {
			if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
				tp.save_changes.error( 'AJAX call successful, but unclear status. Try again while holding down the &#8220;Shift&#8221; key.' );
			} else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) ) {
				tp.save_changes.error( 'AJAX call successful, but unclear data. Try again while holding down the &#8220;Shift&#8221; key.' );
			} else if ( true !== data.success ) {
				var debug_html = '';
				// Print debug information, if we are in debug mode
				if ( ( 'undefined' !== typeof data.error_details ) && ( tablepress_options.print_debug_output ) ) {
					debug_html = '</p><p>These errors were encountered:</p><pre>' + data.error_details + '</pre><p>'; // Some HTML magic because this is wrapped in <p> when printed
				}
				tp.save_changes.error( 'AJAX call successful, internal saving process failed. Try again while holding down the &#8220;Shift&#8221; key.' + debug_html );
			} else {
				tp.save_changes.success( data );
			}
		},
		ajax_error: function( jqXHR, status, error_thrown ) {
			tp.save_changes.error( 'AJAX call failed: ' + status + ' - ' + error_thrown + '. Try again while holding down the &#8220;Shift&#8221; key.' );
		},
		success: function( data ) {
			// saving was successful, so the original ID has changed to the (maybe) new ID -> we need to adjust all occurrences
			if ( tp.table.id !== data.table_id ) {
				// update URL (for HTML5 browsers only), but only if ID really changed, to not get dummy entries in the browser history
				if ( ( 'pushState' in window.history ) && null !== window.history.pushState ) {
					window.history.pushState( '', '', window.location.href.replace( /table_id=[0-9a-zA-Z-_]+/gi, 'table_id=' + data.table_id ) );
				}
			}
			// update CSS class for data field form
			$id( 'edit-form' ).removeClass( 'tablepress-edit-screen-id-' + tp.table.id ).addClass( 'tablepress-edit-screen-id-' + data.table_id );
			// update table ID in input fields (type text and hidden)
			tp.table.id = tp.table.new_id = data.table_id;
			$id( 'table-id' ).val( tp.table.id );
			$id( 'table-new-id' ).val( tp.table.new_id );
			// update the Shortcode text field
			$( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' );
			// update the nonces
			$id( 'nonce-edit-table' ).val( data.new_edit_nonce );
			$id( 'nonce-preview-table' ).val( data.new_preview_nonce );
			// update URLs in Preview links
			var $show_preview_buttons = $( '.show-preview-button' );
			if ( $show_preview_buttons.length ) { // check necessary, because Preview button might not be visible
				$show_preview_buttons.attr( 'href',
					$show_preview_buttons.first().attr( 'href' )
						.replace( /item=[a-zA-Z0-9_-]+/g, 'item=' + data.table_id )
						.replace( /&_wpnonce=[a-z0-9]+/ig, '&_wpnonce=' + data.new_preview_nonce )
				);
			}
			// update last modified date and user nickname
			$id( 'last-modified' ).text( data.last_modified );
			$id( 'last-editor' ).text( data.last_editor );
			tp.table.unset_table_changed();
			tp.save_changes.after_saving_dialog( 'success', tablepress_strings[ data.message ] );
		},
		error: function( message ) {
			tp.save_changes.after_saving_dialog( 'error', message );
		},
		after_saving_dialog: function( type, message ) {
			if ( 'undefined' === typeof message ) {
				message = '';
			} else {
				message = ': ' + message;
			}
			var delay,
				div_class = 'save-changes-' + type;
			if ( 'success' === type ) {
				div_class += ' notice notice-success';
				delay = 3000;
			} else {
				div_class += ' notice notice-error';
				delay = 6000;
			}
			$( '.animation-saving' ).closest( 'p' )
				.after( '<div class="ajax-alert ' + div_class + '"><p>' + tablepress_strings['save_changes_' + type] + message + '</p></div>' );
			$( '.animation-saving' ).remove();
			$( '.save-changes-' + type ).delay( delay ).fadeOut( 2000, function() { $(this).remove(); } );
			$( '.save-changes-button' ).prop( 'disabled', false );
			$( 'body' ).removeClass( 'wait' );
		}
	};

	tp.init = function() {
		var callbacks = {
			'click': {
				'#rows-insert':			tp.rows.insert,
				'#columns-insert':		tp.columns.insert,
				'#rows-duplicate':		tp.rows.duplicate,
				'#columns-duplicate':	tp.columns.duplicate,
				'#rows-remove':			tp.rows.remove,
				'#columns-remove':		tp.columns.remove,
				'#rows-hide':			tp.rows.hide,
				'#columns-hide':		tp.columns.hide,
				'#rows-unhide':			tp.rows.unhide,
				'#columns-unhide':		tp.columns.unhide,
				'#rows-append':			tp.rows.append,
				'#columns-append':		tp.columns.append,
				'#link-add':			tp.content.link.add,
				'#image-add':			tp.content.image.add,
				'#span-add-rowspan':	function() { tp.content.span.add( '#rowspan#' ); },
				'#span-add-colspan':	function() { tp.content.span.add( '#colspan#' ); },
				'.show-preview-button': tp.table.preview.trigger,
				'.save-changes-button': tp.save_changes.trigger,
				'.show-help-box':		function() {
					$(this).next().wpdialog( {
						title: $(this).attr( 'title' ),
						height: 470,
						width: 320,
						modal: true,
						dialogClass: 'wp-dialog',
						resizable: false
					} );
				}
			},
			'keyup': {
				'#table-new-id':		tp.check.table_id
			},
			'change': {
				'#option-table-head':			tp.table.change_table_head,
				'#option-table-foot':			tp.table.change_table_foot,
				'#option-use-datatables':		tp.table.change_datatables,
				'#option-datatables-paginate':	tp.table.change_datatables_pagination
			},
			'blur': {
				'#table-new-id':		tp.table.change_id	// onchange would not recognize changed values from tp.check.table_id
			}
		},
		$table = $id( 'edit-form-body' );

		$.each( callbacks, function( event, event_callbacks ) {
			$.each( event_callbacks, function( selector, callback ) {
				$( selector ).on( event, callback );
			} );
		} );

		$( window ).on( 'beforeunload', tp.check.changes_saved );

		// do this before the next lines, to not trigger set_table_changed()
		$id( 'option-table-head' ).change(); // init changed/disabled states of DataTables JS features checkboxes
		$id( 'option-print-name' ).change( tp.table.change_print_name_description ).change(); // init dropdowns for name and description position
		$id( 'option-print-description' ).change( tp.table.change_print_name_description ).change();

		// just once is enough, will be reset after saving
		$table.one( 'change', 'textarea', tp.table.set_table_changed );
		$( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );

		if ( tablepress_options.cells_advanced_editor ) {
			$table.on( 'click', 'textarea', tp.cells.advanced_editor.keyopen );
			$id( 'advanced-editor-open' ).on( 'click', tp.cells.advanced_editor.buttonopen );
			$id( 'advanced-editor-confirm' ).on( 'click', tp.cells.advanced_editor.save );
			$id( 'advanced-editor-cancel' ).on( 'click', tp.cells.advanced_editor.close );
			$id( 'advanced-editor' ).wpdialog( {
				autoOpen: false,
				title: $id( 'advanced-editor-open' ).val(),
				width: 600,
				modal: true,
				dialogClass: 'wp-dialog',
				resizable: false
			} );
			// Fix issue with input fields not being usable (they are immediately losing focus without this) in the wpLink dialog when called through the "Advanced Editor"
			$id( 'wp-link' ).on( 'focus', 'input', function( event ) {
				event.stopPropagation();
			} );
		} else {
			$id( 'advanced-editor-open' ).hide();
		}

		// Fix issue with input fields not being usable (they are immediately losing focus without this) in the sidebar of the new Media Manager
		$( 'body' ).on( 'focus', '.media-modal .media-frame-content input, .media-modal .media-frame-content textarea', function( event ) {
			event.stopPropagation();
		} );

		if ( tablepress_options.cells_auto_grow ) {
			$table.on( 'focus', 'textarea', tp.cells.autogrow );
		}

		$id( 'edit-form-body' ).on( 'click', 'input:checkbox', { parent: '#edit-form-body' }, tp.cells.checkboxes.multi_select );
		$id( 'edit-form-foot' ).on( 'click', 'input:checkbox', { parent: '#edit-form-foot' }, tp.cells.checkboxes.multi_select );

		$id( 'edit-form-head' ).on( 'click', '.sort-control', tp.rows.sort );

		// on form submit: Enable disabled fields, so that they are transmitted in the POST request
		$id( 'tablepress-page' ).find( 'form' ).on( 'submit', function() {
			$(this).find( '.tablepress-postbox-table' ).find( 'input, select' ).prop( 'disabled', false );
		} );

		$table.sortable( {
			axis: 'y',
			containment: $id( 'edit-form' ), // to get better behavior when dragging before/after the first/last row
			forceHelperSize: true, // necessary?
			handle: '.move-handle',
			start: tp.rows.move.start,
			change: tp.rows.move.change,
			stop: tp.rows.move.stop,
			update: tp.reindex
		} ); // disableSelection() prohibits selection of text in textareas via keyboard

		$id( 'edit-form-head' ).sortable( {
			axis: 'x',
			items: '.head',
			containment: 'parent',
			forceHelperSize: true, // necessary?
			helper: 'clone',
			handle: '.move-handle',
			start: tp.columns.move.start,
			stop: tp.columns.move.stop,
			change: tp.columns.move.change,
			sort: tp.columns.move.sort
		} ).disableSelection();
	};

	// Run TablePress initialization.
	tp.init();

} );
crosstraining/wp-content/plugins/file-manager-advanced/application/library/js/commands/edit.js000064400000104346151142453260031772 0ustar00home/xbodynamge/**
 * @class elFinder command "edit". 
 * Edit text file in dialog window
 *
 * @author Dmitry (dio) Levashov, dio@std42.ru
 **/
elFinder.prototype.commands.edit = function() {
	"use strict";
	var self  = this,
		fm    = this.fm,
		clsEditing = fm.res('class', 'editing'),
		mimesSingle = [],
		mimes = [],
		allowAll = false,
		rtrim = function(str){
			return str.replace(/\s+$/, '');
		},
		getEncSelect = function(heads) {
			var sel = $('<select class="ui-corner-all"></select>'),
				hval;
			if (heads) {
				$.each(heads, function(i, head) {
					hval = fm.escape(head.value);
					sel.append('<option value="'+hval+'">'+(head.caption? fm.escape(head.caption) : hval)+'</option>');
				});
			}
			$.each(self.options.encodings, function(i, v) {
				sel.append('<option value="'+v+'">'+v+'</option>');
			});
			return sel;
		},
		getDlgWidth = function() {
			var win = fm.options.dialogContained? fm.getUI() : $(window),
				m, width;
			if (typeof self.options.dialogWidth === 'string' && (m = self.options.dialogWidth.match(/(\d+)%/))) {
				width = parseInt(win.width() * (m[1] / 100));
			} else {
				width = parseInt(self.options.dialogWidth || 650);
			}
			return Math.min(width, win.width());
		},
		getDlgHeight = function() {
			if (!self.options.dialogHeight) {
				return void(0);
			}
			var win = fm.options.dialogContained? fm.getUI() : $(window),
				m, height;
			if (typeof self.options.dialogHeight === 'string' && (m = self.options.dialogHeight.match(/(\d+)%/))) {
				height = parseInt(win.height() * (m[1] / 100));
			} else {
				height = parseInt(self.options.dialogHeight || win.height());
			}
			return Math.min(height, win.height());
		},

		/**
		 * Return files acceptable to edit
		 *
		 * @param  Array  files hashes
		 * @return Array
		 **/
		filter = function(files) {
			var cnt = files.length,
				mime, ext, skip;
			
			if (cnt > 1) {
				mime = files[0].mime;
				ext = files[0].name.replace(/^.*(\.[^.]+)$/, '$1');
			}
			return $.grep(files, function(file) {
				var res;
				if (skip || file.mime === 'directory') {
					return false;
				}
				res = file.read
					&& (allowAll || fm.mimeIsText(file.mime) || $.inArray(file.mime, cnt === 1? mimesSingle : mimes) !== -1) 
					&& (!self.onlyMimes.length || $.inArray(file.mime, self.onlyMimes) !== -1)
					&& (cnt === 1 || (file.mime === mime && file.name.substr(ext.length * -1) === ext))
					&& (fm.uploadMimeCheck(file.mime, file.phash)? true : false)
					&& setEditors(file, cnt)
					&& Object.keys(editors).length;
				if (!res) {
					skip = true;
				}
				return res;
			});
		},

		fileSync = function(hash) {
			var old = fm.file(hash),
				f;
			fm.request({
				cmd: 'info',
				targets: [hash],
				preventDefault: true
			}).done(function(data) {
				var changed;
				if (data && data.files && data.files.length) {
					f = data.files[0];
					if (old.ts != f.ts || old.size != f.size) {
						changed = { changed: [ f ] };
						fm.updateCache(changed);
						fm.change(changed);
					}
				}
			});
		},

		/**
		 * Open dialog with textarea to edit file
		 *
		 * @param  String  id       dialog id
		 * @param  Object  file     file object
		 * @param  String  content  file content
		 * @return $.Deferred
		 **/
		dialog = function(id, file, content, encoding, editor, toasts) {

			var dfrd = $.Deferred(),
				_loaded = false,
				loaded = function() {
					if (!_loaded) {
						fm.toast({
							mode: 'warning',
							msg: fm.i18n('nowLoading')
						});
						return false;
					}
					return true;
				},
				makeToasts = function() {
					// make toast message
					if (toasts && Array.isArray(toasts)) {
						$.each(toasts, function() {
							this.msg && fm.toast(this);
						});
					}
				},
				save = function() {
					var encord = selEncoding? selEncoding.val():void(0),
						saveDfd = $.Deferred().fail(function(err) {
							dialogNode.show().find('button.elfinder-btncnt-0,button.elfinder-btncnt-1').hide();
						}),
						conf, res, tm;
					if (!loaded()) {
						return saveDfd.resolve();
					}
					if (ta.editor) {
						ta.editor.save(ta[0], ta.editor.instance);
						conf = ta.editor.confObj;
						if (conf.info && (conf.info.schemeContent || conf.info.arrayBufferContent)) {
							encord = 'scheme';
						}
					}
					res = getContent();
					setOld(res);
					if (res.promise) {
						tm = setTimeout(function() {
							fm.notify({
								type : 'chkcontent',
								cnt : 1,
								hideCnt: true,
								cancel : function() {
									res.reject();
								}
							});
						}, 100);
						res.always(function() {
							tm && clearTimeout(tm);
							fm.notify({ type : 'chkcontent', cnt: -1 });
						}).done(function(data) {
							dfrd.notifyWith(ta, [encord, ta.data('hash'), old, saveDfd]);
						}).fail(function(err) {
							saveDfd.reject(err);
						});
					} else {
						dfrd.notifyWith(ta, [encord, ta.data('hash'), old, saveDfd]);
					}
					return saveDfd;
				},
				saveon = function() {
					if (!loaded()) { return; }
					save().fail(function(err) {
						err && fm.error(err);
					});
				},
				cancel = function() {
					ta.elfinderdialog('close');
				},
				savecl = function() {
					if (!loaded()) { return; }
					dialogNode.hide();
					save().done(function() {
						_loaded = false;
						dialogNode.show();
						cancel();
					}).fail(function(err) {
						dialogNode.show();
						err && fm.error(err);
					});
				},
				saveAs = function() {
					if (!loaded()) { return; }
					var prevOld = old,
						phash = file.phash,
						fail = function(err) {
							dialogs.addClass(clsEditing).fadeIn(function() {
								err && fm.error(err);
							});
							old = prevOld;
							fm.disable();
						},
						make = function() {
							self.mime = saveAsFile.mime || file.mime;
							self.prefix = (saveAsFile.name || file.name).replace(/ \d+(\.[^.]+)?$/, '$1');
							self.requestCmd = 'mkfile';
							self.nextAction = {};
							self.data = {target : phash};
							$.proxy(fm.res('mixin', 'make'), self)()
								.done(function(data) {
									var oldHash;
									if (data.added && data.added.length) {
										oldHash = ta.data('hash');
										ta.data('hash', data.added[0].hash);
										save().done(function() {
											_loaded = false;
											dialogNode.show();
											cancel();
											dialogs.fadeIn();
										}).fail(function() {
											fm.exec('rm', [data.added[0].hash], { forceRm: true, quiet: true });
											ta.data('hash', oldHash);
											dialogNode.find('button.elfinder-btncnt-2').hide();
											fail();
										});
									} else {
										fail();
									}
								})
								.progress(function(err) {
									if (err && err === 'errUploadMime') {
										ta.trigger('saveAsFail');
									}
								})
								.fail(fail)
								.always(function() {
									delete self.mime;
									delete self.prefix;
									delete self.nextAction;
									delete self.data;
								});
							fm.trigger('unselectfiles', { files: [ file.hash ] });
						},
						reqOpen = null,
						reqInfo = null,
						dialogs = fm.getUI().children('.' + self.dialogClass + ':visible');
						if (dialogNode.is(':hidden')) {
							dialogs = dialogs.add(dialogNode);
						}
						dialogs.removeClass(clsEditing).fadeOut();
					
					fm.enable();
					
					if (fm.searchStatus.state < 2 && phash !== fm.cwd().hash) {
						reqOpen = fm.exec('open', [phash], {thash: phash});
					} else if (!fm.file(phash)) {
						reqInfo = fm.request({cmd: 'info', targets: [phash]}); 
					}
					
					$.when([reqOpen, reqInfo]).done(function() {
						if (reqInfo) {
							fm.one('infodone', function() {
								fm.file(phash)? make() : fail('errFolderNotFound');
							});
						} else {
							reqOpen? fm.one('cwdrender', make) : make();
						}
					}).fail(fail);
				},
				changed = function() {
					var dfd = $.Deferred(),
						res, tm;
					if (!_loaded) {
						return dfd.resolve(false);
					}
					ta.editor && ta.editor.save(ta[0], ta.editor.instance);
					res = getContent();
					if (res && res.promise) {
						tm = setTimeout(function() {
							fm.notify({
								type : 'chkcontent',
								cnt : 1,
								hideCnt: true,
								cancel : function() {
									res.reject();
								}
							});
						}, 100);
						res.always(function() {
							tm && clearTimeout(tm);
							fm.notify({ type : 'chkcontent', cnt: -1 });
						}).done(function(d) {
							dfd.resolve(old !== d);
						}).fail(function(err) {
							dfd.resolve(err || (old === undefined? false : true));
						});
					} else {
						dfd.resolve(old !== res);
					}
					return dfd;
				},
				opts = {
					title   : fm.escape(file.name),
					width   : getDlgWidth(),
					height  : getDlgHeight(),
					buttons : {},
					cssClass  : clsEditing,
					maxWidth  : 'window',
					maxHeight : 'window',
					allowMinimize : true,
					allowMaximize : true,
					openMaximized : editorMaximized() || (editor && editor.info && editor.info.openMaximized),
					btnHoverFocus : false,
					closeOnEscape : false,
					propagationEvents : ['mousemove', 'mouseup', 'click'],
					minimize : function() {
						var conf;
						if (ta.editor && dialogNode.closest('.ui-dialog').is(':hidden')) {
							conf = ta.editor.confObj;
							if (conf.info && conf.info.syncInterval) {
								fileSync(file.hash);
							}
						}
					},
					close   : function() {
						var close = function() {
								var conf;
								dfrd.resolve();
								if (ta.editor) {
									ta.editor.close(ta[0], ta.editor.instance);
									conf = ta.editor.confObj;
									if (conf.info && conf.info.syncInterval) {
										fileSync(file.hash);
									}
								}
								ta.elfinderdialog('destroy');
							},
							onlySaveAs = (typeof saveAsFile.name !== 'undefined'),
							accept = onlySaveAs? {
								label    : 'btnSaveAs',
								callback : function() {
									requestAnimationFrame(saveAs);
								}
							} : {
								label    : 'btnSaveClose',
								callback : function() {
									save().done(function() {
										close();
									});
								}
							};
						changed().done(function(change) {
							var msgs = ['confirmNotSave'];
							if (change) {
								if (typeof change === 'string') {
									msgs.unshift(change);
								}
								fm.confirm({
									title  : self.title,
									text   : msgs,
									accept : accept,
									cancel : {
										label    : 'btnClose',
										callback : close
									},
									buttons : onlySaveAs? null : [{
										label    : 'btnSaveAs',
										callback : function() {
											requestAnimationFrame(saveAs);
										}
									}]
								});
							} else {
								close();
							}
						});
					},
					open    : function() {
						var loadRes, conf, interval;
						ta.initEditArea.call(ta, id, file, content, fm);
						if (ta.editor) {
							loadRes = ta.editor.load(ta[0]) || null;
							if (loadRes && loadRes.done) {
								loadRes.always(function() {
									_loaded = true;
								}).done(function(instance) {
									ta.editor.instance = instance;
									ta.editor.focus(ta[0], ta.editor.instance);
									setOld(getContent());
									requestAnimationFrame(function() {
										dialogNode.trigger('resize');
									});
								}).fail(function(error) {
									error && fm.error(error);
									ta.elfinderdialog('destroy');
									return;
								}).always(makeToasts);
							} else {
								_loaded = true;
								if (loadRes && (typeof loadRes === 'string' || Array.isArray(loadRes))) {
									fm.error(loadRes);
									ta.elfinderdialog('destroy');
									return;
								}
								ta.editor.instance = loadRes;
								ta.editor.focus(ta[0], ta.editor.instance);
								setOld(getContent());
								requestAnimationFrame(function() {
									dialogNode.trigger('resize');
								});
								makeToasts();
							}
							conf = ta.editor.confObj;
							if (conf.info && conf.info.syncInterval) {
								if (interval = parseInt(conf.info.syncInterval)) {
									setTimeout(function() {
										autoSync(interval);
									}, interval);
								}
							}
						} else {
							_loaded = true;
							setOld(getContent());
						}
					},
					resize : function(e, data) {
						ta.editor && ta.editor.resize(ta[0], ta.editor.instance, e, data || {});
					}
				},
				getContent = function() {
					var res = ta.getContent.call(ta, ta[0]);
					if (res === undefined || res === false || res === null) {
						res = $.Deferred().reject();
					}
					return res;
				},
				setOld = function(res) {
					if (res && res.promise) {
						res.done(function(d) {
							old = d;
						});
					} else {
						old = res;
					}
				},
				autoSync = function(interval) {
					if (dialogNode.is(':visible')) {
						fileSync(file.hash);
						setTimeout(function() {
							autoSync(interval);
						}, interval);
					}
				},
				stateChange = function() {
					if (selEncoding) {
						changed().done(function(change) {
							if (change) {
								selEncoding.attr('title', fm.i18n('saveAsEncoding')).addClass('elfinder-edit-changed');
							} else {
								selEncoding.attr('title', fm.i18n('openAsEncoding')).removeClass('elfinder-edit-changed');
							}
						});
					}
				},
				saveAsFile = {},
				ta, old, dialogNode, selEncoding, extEditor, maxW, syncInterval;
				
			if (editor) {
				if (editor.html) {
					ta = $(editor.html);
				}
				extEditor = {
					init     : editor.init || null,
					load     : editor.load,
					getContent : editor.getContent || null,
					save     : editor.save,
					beforeclose : typeof editor.beforeclose == 'function' ? editor.beforeclose : void 0,
					close    : typeof editor.close == 'function' ? editor.close : function() {},
					focus    : typeof editor.focus == 'function' ? editor.focus : function() {},
					resize   : typeof editor.resize == 'function' ? editor.resize : function() {},
					instance : null,
					doSave   : saveon,
					doCancel : cancel,
					doClose  : savecl,
					file     : file,
					fm       : fm,
					confObj  : editor,
					trigger  : function(evName, data) {
						fm.trigger('editEditor' + evName, Object.assign({}, editor.info || {}, data));
					}
				};
			}
			
			if (!ta) {
				if (!fm.mimeIsText(file.mime)) {
					return dfrd.reject('errEditorNotFound');
				}
				(function() {
					ta = $('<textarea class="elfinder-file-edit" rows="20" id="'+id+'-ta"></textarea>')
						.on('input propertychange', stateChange);
					
					if (!editor || !editor.info || editor.info.useTextAreaEvent) {
						ta.on('keydown', function(e) {
							var code = e.keyCode,
								value, start;
							
							e.stopPropagation();
							if (code == $.ui.keyCode.TAB) {
								e.preventDefault();
								// insert tab on tab press
								if (this.setSelectionRange) {
									value = this.value;
									start = this.selectionStart;
									this.value = value.substr(0, start) + "\t" + value.substr(this.selectionEnd);
									start += 1;
									this.setSelectionRange(start, start);
								}
							}
							
							if (e.ctrlKey || e.metaKey) {
								// close on ctrl+w/q
								if (code == 'Q'.charCodeAt(0) || code == 'W'.charCodeAt(0)) {
									e.preventDefault();
									cancel();
								}
								if (code == 'S'.charCodeAt(0)) {
									e.preventDefault();
									saveon();
								}
							}
							
						})
						.on('mouseenter', function(){this.focus();});
					}

					ta.initEditArea = function(id, file, content) {
						// ta.hide() for performance tune. Need ta.show() in `load()` if use textarea node.
						ta.hide().val(content);
						this._setupSelEncoding(content);
					};
				})();
			}

			// extended function to setup selector of encoding for text editor
			ta._setupSelEncoding = function(content) {
				var heads = (encoding && encoding !== 'unknown')? [{value: encoding}] : [],
					wfake = $('<select></select>').hide(),
					setSelW = function(init) {
						init && wfake.appendTo(selEncoding.parent());
						wfake.empty().append($('<option></option>').text(selEncoding.val()));
						selEncoding.width(wfake.width());
					};
				if (content === '' || ! encoding || encoding !== 'UTF-8') {
					heads.push({value: 'UTF-8'});
				}
				selEncoding = getEncSelect(heads).on('touchstart', function(e) {
					// for touch punch event handler
					e.stopPropagation();
				}).on('change', function() {
					// reload to change encoding if not edited
					changed().done(function(change) {
						if (! change && getContent() !== '') {
							cancel();
							edit(file, selEncoding.val(), editor).fail(function(err) { err && fm.error(err); });
						}
					});
					setSelW();
				}).on('mouseover', stateChange);
				ta.parent().next().prepend($('<div class="ui-dialog-buttonset elfinder-edit-extras"></div>').append(selEncoding));
				setSelW(true);
			};

			ta.data('hash', file.hash);
			
			if (extEditor) {
				ta.editor = extEditor;
				
				if (typeof extEditor.beforeclose === 'function') {
					opts.beforeclose = function() {
						return extEditor.beforeclose(ta[0], extEditor.instance);
					};
				}
				
				if (typeof extEditor.init === 'function') {
					ta.initEditArea = extEditor.init;
				}
				
				if (typeof extEditor.getContent === 'function') {
					ta.getContent = extEditor.getContent;
				}
			}
			
			if (! ta.initEditArea) {
				ta.initEditArea = function() {};
			}
			
			if (! ta.getContent) {
				ta.getContent = function() {
					return rtrim(ta.val());
				};
			}
			
			if (!editor || !editor.info || !editor.info.preventGet) {
				opts.buttons[fm.i18n('btnSave')]      = saveon;
				opts.buttons[fm.i18n('btnSaveClose')] = savecl;
				opts.buttons[fm.i18n('btnSaveAs')]    = saveAs;
				opts.buttons[fm.i18n('btnCancel')]    = cancel;
			}
			
			if (editor && typeof editor.prepare === 'function') {
				editor.prepare(ta, opts, file);
			}
			
			dialogNode = self.fmDialog(ta, opts)
				.attr('id', id)
				.on('keydown keyup keypress', function(e) {
					e.stopPropagation();
				})
				.css({ overflow: 'hidden', minHeight: '7em' })
				.addClass('elfinder-edit-editor')
				.closest('.ui-dialog')
				.on('changeType', function(e, data) {
					if (data.extention && data.mime) {
						var ext = data.extention,
							mime = data.mime,
							btnSet = $(this).children('.ui-dialog-buttonpane').children('.ui-dialog-buttonset');
						btnSet.children('.elfinder-btncnt-0,.elfinder-btncnt-1').hide();
						saveAsFile.name = fm.splitFileExtention(file.name)[0] + '.' + data.extention;
						saveAsFile.mime = data.mime;
						if (!data.keepEditor) {
							btnSet.children('.elfinder-btncnt-2').trigger('click');
						}
					}
				});
			
			// care to viewport scale change with mobile devices
			maxW = (fm.options.dialogContained? fm.getUI() : $(window)).width();
			(dialogNode.width() > maxW) && dialogNode.width(maxW);
			
			return dfrd.promise();
		},
		
		/**
		 * Get file content and
		 * open dialog with textarea to edit file content
		 *
		 * @param  String  file hash
		 * @return jQuery.Deferred
		 **/
		edit = function(file, convert, editor) {
			var hash   = file.hash,
				opts   = fm.options,
				dfrd   = $.Deferred(), 
				id     = 'edit-'+fm.namespace+'-'+file.hash,
				d      = fm.getUI().find('#'+id),
				conv   = !convert? 0 : convert,
				noContent = false,
				req, error, res;
			
			
			if (d.length) {
				d.elfinderdialog('toTop');
				return dfrd.resolve();
			}
			
			if (!file.read || (!file.write && (!editor.info || !editor.info.converter))) {
				error = ['errOpen', file.name, 'errPerm'];
				return dfrd.reject(error);
			}
			
			if (editor && editor.info) {
				if (typeof editor.info.edit === 'function') {
					res = editor.info.edit.call(fm, file, editor);
					if (res.promise) {
						res.done(function() {
							dfrd.resolve();
						}).fail(function(error) {
							dfrd.reject(error);
						});
					} else {
						res? dfrd.resolve() : dfrd.reject();
					}
					return dfrd;
				}

				noContent = editor.info.preventGet || editor.info.noContent;
				if (editor.info.urlAsContent || noContent) {
					req = $.Deferred();
					if (editor.info.urlAsContent) {
						fm.url(hash, { async: true, onetime: true, temporary: true }).done(function(url) {
							req.resolve({content: url});
						});
					} else {
						req.resolve({});
					}
				} else {
					if (conv) {
						file.encoding = conv;
						fm.cache(file, 'change');
					}
					req = fm.request({
						data           : {cmd : 'get', target : hash, conv : conv, _t : file.ts},
						options        : {type: 'get', cache : true},
						notify         : {type : 'file', cnt : 1},
						preventDefault : true
					});
				}

				req.done(function(data) {
					var selEncoding, reg, m, res;
					if (data.doconv) {
						fm.confirm({
							title  : self.title,
							text   : data.doconv === 'unknown'? 'confirmNonUTF8' : 'confirmConvUTF8',
							accept : {
								label    : 'btnConv',
								callback : function() {  
									dfrd = edit(file, selEncoding.val(), editor);
								}
							},
							cancel : {
								label    : 'btnCancel',
								callback : function() { dfrd.reject(); }
							},
							optionsCallback : function(options) {
								options.create = function() {
									var base = $('<div class="elfinder-dialog-confirm-encoding"></div>'),
										head = {value: data.doconv},
										detected;
									
									if (data.doconv === 'unknown') {
										head.caption = '-';
									}
									selEncoding = getEncSelect([head]);
									$(this).next().find('.ui-dialog-buttonset')
										.prepend(base.append($('<label>'+fm.i18n('encoding')+' </label>').append(selEncoding)));
								};
							}
						});
					} else {
						if (!noContent && fm.mimeIsText(file.mime)) {
							reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i');
							if (!editor.info.dataScheme) {
								if (window.atob && (m = data.content.match(reg))) {
									data.content = atob(data.content.substr(m[1].length));
								}
							} else {
								if (window.btoa && !data.content.match(reg)) {
									data.content = 'data:'+file.mime+';base64,'+btoa(data.content);
								}
							}
						}
						dialog(id, file, data.content, data.encoding, editor, data.toasts)
							.done(function(data) {
								dfrd.resolve(data);
							})
							.progress(function(encoding, newHash, data, saveDfd) {
								var ta = this;
								if (newHash) {
									hash = newHash;
								}
								fm.request({
									options : {type : 'post'},
									data : {
										cmd     : 'put',
										target  : hash,
										encoding : encoding || data.encoding,
										content : data
									},
									notify : {type : 'save', cnt : 1},
									syncOnFail : true,
									preventFail : true,
									navigate : {
										target : 'changed',
										toast : {
											inbuffer : {msg: fm.i18n(['complete', fm.i18n('btnSave')])}
										}
									}
								})
								.fail(function(error) {
									dfrd.reject(error);
									saveDfd.reject();
								})
								.done(function(data) {
									requestAnimationFrame(function(){
										ta.trigger('focus');
										ta.editor && ta.editor.focus(ta[0], ta.editor.instance);
									});
									saveDfd.resolve();
								});
							})
							.fail(function(error) {
								dfrd.reject(error);
							});
					}
				})
				.fail(function(error) {
					var err = fm.parseError(error);
					err = Array.isArray(err)? err[0] : err;
					if (file.encoding) {
						file.encoding = '';
						fm.cache(file, 'change');
					}
					(err !== 'errConvUTF8') && fm.sync();
					dfrd.reject(error);
				});
			}

			return dfrd.promise();
		},
		
		/**
		 * Current editors of selected files
		 * 
		 * @type Object
		 */
		editors = {},
		
		/**
		 * Fallback editor (Simple text editor)
		 * 
		 * @type Object
		 */
		fallbackEditor = {
			// Simple Text (basic textarea editor)
			info : {
				id : 'textarea',
				name : 'TextArea',
				useTextAreaEvent : true
			},
			load : function(textarea) {
				// trigger event 'editEditorPrepare'
				this.trigger('Prepare', {
					node: textarea,
					editorObj: void(0),
					instance: void(0),
					opts: {}
				});
				textarea.setSelectionRange && textarea.setSelectionRange(0, 0);
				$(textarea).trigger('focus').show();
			},
			save : function(){}
		},

		/**
		 * Set current editors
		 * 
		 * @param  Object  file object
		 * @param  Number  cnt  count of selected items
		 * @return Void
		 */
		setEditors = function(file, cnt) {
			var mimeMatch = function(fileMime, editorMimes){
					if (!editorMimes) {
						return fm.mimeIsText(fileMime);
					} else {
						if (editorMimes[0] === '*' || $.inArray(fileMime, editorMimes) !== -1) {
							return true;
						}
						var i, l;
						l = editorMimes.length;
						for (i = 0; i < l; i++) {
							if (fileMime.indexOf(editorMimes[i]) === 0) {
								return true;
							}
						}
						return false;
					}
				},
				extMatch = function(fileName, editorExts){
					if (!editorExts || !editorExts.length) {
						return true;
					}
					var ext = fileName.replace(/^.+\.([^.]+)|(.+)$/, '$1$2').toLowerCase(),
					i, l;
					l = editorExts.length;
					for (i = 0; i < l; i++) {
						if (ext === editorExts[i].toLowerCase()) {
							return true;
						}
					}
					return false;
				},
				optEditors = self.options.editors || [],
				cwdWrite = fm.cwd().write;
			
			stored = fm.storage('storedEditors') || {};
			editors = {};
			if (!optEditors.length) {
				optEditors = [fallbackEditor];
			}
			$.each(optEditors, function(i, editor) {
				var name;
				if ((cnt === 1 || !editor.info.single)
						&& ((!editor.info || !editor.info.converter)? file.write : cwdWrite)
						&& (file.size > 0 || (!editor.info.converter && editor.info.canMakeEmpty !== false && fm.mimesCanMakeEmpty[file.mime]))
						&& (!editor.info.maxSize || file.size <= editor.info.maxSize)
						&& mimeMatch(file.mime, editor.mimes || null)
						&& extMatch(file.name, editor.exts || null)
						&& typeof editor.load == 'function'
						&& typeof editor.save == 'function') {
					
					name = editor.info.name? editor.info.name : ('Edit Code');
					editor.id = editor.info.id? editor.info.id : ('editor' + i),
					editor.name = name;
					editor.i18n = fm.i18n(name);
					editors[editor.id] = editor;
				}
			});
			return Object.keys(editors).length? true : false;
		},
		store = function(mime, editor) {
			if (mime && editor) {
				if (!$.isPlainObject(stored)) {
					stored = {};
				}
				stored[mime] = editor.id;
				fm.storage('storedEditors', stored);
				fm.trigger('selectfiles', {files : fm.selected()});
			}
		},
		useStoredEditor = function() {
			var d = fm.storage('useStoredEditor');
			return d? (d > 0) : self.options.useStoredEditor;
		},
		editorMaximized = function() {
			var d = fm.storage('editorMaximized');
			return d? (d > 0) : self.options.editorMaximized;
		},
		getSubMenuRaw = function(files, callback) {
			var subMenuRaw = [];
			$.each(editors, function(id, ed) {
				subMenuRaw.push(
					{
						label    : fm.escape(ed.i18n),
						icon     : ed.info && ed.info.icon? ed.info.icon : 'edit',
						options  : { iconImg: ed.info && ed.info.iconImg? fm.baseUrl + ed.info.iconImg : void(0) },
						callback : function() {
							store(files[0].mime, ed);
							callback && callback.call(ed);
						}
					}		
				);
			});
			return subMenuRaw;
		},
		getStoreId = function(name) {
			// for compatibility to previous version
			return name.toLowerCase().replace(/ +/g, '');
		},
		getStoredEditor = function(mime) {
			var name = stored[mime];
			return name && Object.keys(editors).length? editors[getStoreId(name)] : void(0);
		},
		infoRequest = function() {

		},
		stored;
	
	// make public method
	this.getEncSelect = getEncSelect;

	this.shortcuts = [{
		pattern     : 'ctrl+e'
	}];
	
	this.init = function() {
		var self = this,
			fm   = this.fm,
			opts = this.options,
			cmdChecks = [],
			ccData, dfd;
		
		this.onlyMimes = this.options.mimes || [];
		
		fm.one('open', function() {
			// editors setup
			if (opts.editors && Array.isArray(opts.editors)) {
				fm.trigger('canMakeEmptyFile', {mimes: Object.keys(fm.storage('mkfileTextMimes') || {}).concat(opts.makeTextMimes || ['text/plain'])});
				$.each(opts.editors, function(i, editor) {
					if (editor.info && editor.info.cmdCheck) {
						cmdChecks.push(editor.info.cmdCheck);
					}
				});
				if (cmdChecks.length) {
					if (fm.api >= 2.1030) {
						dfd = fm.request({
							data : {
								cmd: 'editor',
								name: cmdChecks,
								method: 'enabled'
							},
							preventDefault : true
						}).done(function(d) {
							ccData = d;
						}).fail(function() {
							ccData = {};
						});
					} else {
						ccData = {};
						dfd = $.Deferred().resolve();
					}
				} else {
					dfd = $.Deferred().resolve();
				}
				
				dfd.always(function() {
					if (ccData) {
						opts.editors = $.grep(opts.editors, function(e) {
							if (e.info && e.info.cmdCheck) {
								return ccData[e.info.cmdCheck]? true : false;
							} else {
								return true;
							}
						});
					}
					$.each(opts.editors, function(i, editor) {
						if (editor.setup && typeof editor.setup === 'function') {
							editor.setup.call(editor, opts, fm);
						}
						if (!editor.disabled) {
							if (editor.mimes && Array.isArray(editor.mimes)) {
								mimesSingle = mimesSingle.concat(editor.mimes);
								if (!editor.info || !editor.info.single) {
									mimes = mimes.concat(editor.mimes);
								}
							}
							if (!allowAll && editor.mimes && editor.mimes[0] === '*') {
								allowAll = true;
							}
							if (!editor.info) {
								editor.info = {};
							}
							if (editor.info.integrate) {
								fm.trigger('helpIntegration', Object.assign({cmd: 'edit'}, editor.info.integrate));
							}
							if (editor.info.canMakeEmpty) {
								fm.trigger('canMakeEmptyFile', {mimes: Array.isArray(editor.info.canMakeEmpty)? editor.info.canMakeEmpty : editor.mimes});
							}
						}
					});
					
					mimesSingle = ($.uniqueSort || $.unique)(mimesSingle);
					mimes = ($.uniqueSort || $.unique)(mimes);
					
					opts.editors = $.grep(opts.editors, function(e) {
						return e.disabled? false : true;
					});
				});
			}
		})
		.bind('select', function() {
			editors = null;
		})
		.bind('contextmenucreate', function(e) {
			var file, editor,
				single = function(editor) {
					var title = self.title;
					fm.one('contextmenucreatedone', function() {
						self.title = title;
					});
					self.title = fm.escape(editor.i18n);
					if (editor.info && editor.info.iconImg) {
						self.contextmenuOpts = {
							iconImg: fm.baseUrl + editor.info.iconImg
						};
					}
					delete self.variants;
				};
			
			self.contextmenuOpts = void(0);
			if (e.data.type === 'files' && self.enabled()) {
				file = fm.file(e.data.targets[0]);
				if (setEditors(file, e.data.targets.length)) {
					if (Object.keys(editors).length > 1) {
						if (!useStoredEditor() || !(editor = getStoredEditor(file.mime))) {
							delete self.extra;
							self.variants = [];
							$.each(editors, function(id, editor) {
								self.variants.push([{ editor: editor }, editor.i18n, editor.info && editor.info.iconImg? fm.baseUrl + editor.info.iconImg : 'edit']);
							});
						} else {
							single(editor);
							self.extra = {
								icon: 'menu',
								node: $('<span></span>')
									.attr({title: fm.i18n('select')})
									.on('click touchstart', function(e){
										if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
											return;
										}
										var node = $(this);
										e.stopPropagation();
										e.preventDefault();
										fm.trigger('contextmenu', {
											raw: getSubMenuRaw(fm.selectedFiles(), function() {
												var hashes = fm.selected();
												fm.exec('edit', hashes, {editor: this});
												fm.trigger('selectfiles', {files : hashes});
											}),
											x: node.offset().left,
											y: node.offset().top
										});
									})
							};
						}
					} else {
						single(editors[Object.keys(editors)[0]]);
						delete self.extra;
					}
				}
			}
		})
		.bind('canMakeEmptyFile', function(e) {
			if (e.data && e.data.resetTexts) {
				var defs = fm.arrayFlip(self.options.makeTextMimes || ['text/plain']),
					hides = self.getMkfileHides();

				$.each((fm.storage('mkfileTextMimes') || {}), function(mime, type) {
					if (!defs[mime]) {
						delete fm.mimesCanMakeEmpty[mime];
						delete hides[mime];
					}
				});
				fm.storage('mkfileTextMimes', null);
				if (Object.keys(hides).length) {
					fm.storage('mkfileHides', hides);
				} else {
					fm.storage('mkfileHides', null);
				}
			}
		});
	};
	
	this.getstate = function(select) {
		var sel = this.files(select),
			cnt = sel.length;

		return cnt && filter(sel).length == cnt ? 0 : -1;
	};
	
	this.exec = function(select, opts) {
		var fm    = this.fm, 
			files = filter(this.files(select)),
			hashes = $.map(files, function(f) { return f.hash; }),
			list  = [],
			editor = opts && opts.editor? opts.editor : null,
			node = $(opts && opts._currentNode? opts._currentNode : fm.cwdHash2Elm(hashes[0])),
			getEditor = function() {
				var dfd = $.Deferred(),
					storedId;
				
				if (!editor && Object.keys(editors).length > 1) {
					if (useStoredEditor() && (editor = getStoredEditor(files[0].mime))) {
						return dfd.resolve(editor);
					}
					fm.trigger('contextmenu', {
						raw: getSubMenuRaw(files, function() {
							dfd.resolve(this);
						}),
						x: node.offset().left,
						y: node.offset().top + 22,
						opened: function() {
							fm.one('closecontextmenu',function() {
								requestAnimationFrame(function() {
									if (dfd.state() === 'pending') {
										dfd.reject();
									}
								});
							});
						}
					});
					
					fm.trigger('selectfiles', {files : hashes});
					
					return dfd;
				} else {
					Object.keys(editors).length > 1 && editor && store(files[0].mime, editor);
					return dfd.resolve(editor? editor : (Object.keys(editors).length? editors[Object.keys(editors)[0]] : null));
				}
			},
			dfrd = $.Deferred(),
			file;

		if (editors === null) {
			setEditors(files[0], hashes.length);
		}
		
		if (!node.length) {
			node = fm.getUI('cwd');
		}
		
		getEditor().done(function(editor) {
			while ((file = files.shift())) {
				list.push(edit(file, (file.encoding || void(0)), editor).fail(function(error) {
					error && fm.error(error);
				}));
			}
			
			if (list.length) { 
				$.when.apply(null, list).done(function() {
					dfrd.resolve();
				}).fail(function() {
					dfrd.reject();
				});
			} else {
				dfrd.reject();
			}
		}).fail(function() {
			dfrd.reject();
		});
		
		return dfrd;
	};

	this.getMkfileHides = function() {
		return fm.storage('mkfileHides') || fm.arrayFlip(self.options.mkfileHideMimes || []);
	};

};
home/xbodynamge/dev/wp-content/plugins/tablepress/admin/js/edit.js000064400000135321151142745210021330 0ustar00/**
 * JavaScript code for the "Edit" screen
 *
 * @package TablePress
 * @subpackage Views JavaScript
 * @author Tobias Bäthge
 * @since 1.0.0
 */

/* global alert, confirm, tp, tablepress_strings, tablepress_options, ajaxurl, wpLink, tb_show, wp, JSON */

// Ensure the global `tp` object exists.
window.tp = window.tp || {};

jQuery( document ).ready( function( $ ) {

	'use strict';

	/* Wrapper to find elements in the page faster with JS-native functions */
	var $id = function( element_id ) {
		return $( document.getElementById( element_id ) );
	};

	/**
	 * TablePress object, mostly with functionality for the "Edit" screen
	 *
	 * @since 1.0.0
	 */
	tp.made_changes = false;

	tp.table = {
		id: $id( 'table-id' ).val(),
		new_id: $id( 'table-new-id' ).val(),
		rows: parseInt( $id( 'number-rows' ).val(), 10 ),
		columns: parseInt( $id( 'number-columns' ).val(), 10 ),
		head: $id( 'option-table-head' ).prop( 'checked' ),
		foot: $id( 'option-table-foot' ).prop( 'checked' ),
		no_data_columns_pre: 2,
		no_data_columns_post: 1,
		body_cells_pre: '<tr><td><span class="move-handle"></span></td><td><input type="checkbox" /><input type="hidden" class="visibility" name="table[visibility][rows][]" value="1" /></td>',
		body_cells_post: '<td><span class="move-handle"></span></td></tr>',
		body_cell: '<td><textarea rows="1"></textarea></td>',
		head_cell: '<th class="head"><span class="sort-control sort-desc" title="' + tablepress_strings.sort_desc + '"><span class="sorting-indicator"></span></span><span class="sort-control sort-asc" title="' + tablepress_strings.sort_asc + '"><span class="sorting-indicator"></span></span><span class="move-handle"></span></th>',
		foot_cell: '<th><input type="checkbox" /><input type="hidden" class="visibility" name="table[visibility][columns][]" value="1" /></th>',
		set_table_changed: function() {
			tp.made_changes = true;
		},
		unset_table_changed: function() {
			tp.made_changes = false;
			$id( 'edit-form-body' ).one( 'change', 'textarea', tp.table.set_table_changed );
			// @TODO: maybe use .tablepress-postbox-table here and further below
			$( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );
		},
		change_id: function( /* event */ ) {
			// empty table IDs are not allowed
			if ( '' === $.trim( $id( 'table-new-id' ).val() ) ) {
				alert( tablepress_strings.table_id_not_empty );
				$id( 'table-new-id' ).val( tp.table.new_id ).focus().select();
				return;
			}
			// the '0' table ID is not allowed
			if ( '0' === $.trim( $id( 'table-new-id' ).val() ) ) {
				alert( tablepress_strings.table_id_not_zero );
				$id( 'table-new-id' ).val( tp.table.new_id ).focus().select();
				return;
			}

			if ( this.value === tp.table.new_id ) {
				return;
			}

			if ( confirm( tablepress_strings.ays_change_table_id ) ) {
				tp.table.new_id = this.value;
				$( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' ).click(); // click() to focus and select
				tp.table.set_table_changed();
			} else {
				$(this).val( tp.table.new_id );
			}
		},
		change_table_head: function( /* event */ ) {
			tp.table.head = $(this).prop( 'checked' );
			$id( 'option-use-datatables' ).prop( 'disabled', ! tp.table.head ).change();
			$id( 'notice-datatables-head-row' ).toggle( ! tp.table.head );
			tp.rows.stripe();
		},
		change_table_foot: function( /* event */ ) {
			tp.table.foot = $(this).prop( 'checked' );
			tp.rows.stripe();
		},
		change_print_name_description: function( /* event */ ) {
			$id( this.id + '-position' ).prop( 'disabled', ! $(this).prop( 'checked' ) );
		},
		change_datatables: function() {
			var $datatables_checkbox = $id( 'option-use-datatables' ),
				checkboxes_disabled = ! ( $datatables_checkbox.prop( 'checked' ) && ! $datatables_checkbox.prop( 'disabled' ) );
			$datatables_checkbox.closest( 'tbody' ).find( 'input' ).not( $datatables_checkbox ).prop( 'disabled', checkboxes_disabled );
			tp.table.change_datatables_pagination();
		},
		change_datatables_pagination: function() {
			var $pagination_checkbox = $id( 'option-datatables-paginate' ),
				pagination_enabled = ( $pagination_checkbox.prop( 'checked' ) && ! $pagination_checkbox.prop( 'disabled' ) );
			$id( 'option-datatables-lengthchange' ).prop( 'disabled', ! pagination_enabled );
			$id( 'option-datatables-paginate_entries' ).prop( 'disabled', ! pagination_enabled );
		},
		prepare_ajax_request: function( wp_action, wp_nonce ) {
			var $table_body = $id( 'edit-form-body' ),
				table_data = [],
				table_options,
				table_number = { rows: tp.table.rows, columns: tp.table.columns, hidden_rows: 0, hidden_columns: 0 },
				table_visibility = { rows: [], columns: [] };

			$table_body.children().each( function( idx, row ) {
				table_data[ idx ] = $( row ).find( 'textarea' )
					.map( function() {
						return $(this).val();
					} )
					.get();
			} );
			table_data = JSON.stringify( table_data );

			// @TODO: maybe for options saving: https://stackoverflow.com/questions/1184624/convert-form-data-to-javascript-object-with-jquery
			// or each()-loop through all checkboxes/textfields/selects
			table_options = {
				// Table Options
				table_head: tp.table.head,
				table_foot: tp.table.foot,
				alternating_row_colors: $id( 'option-alternating-row-colors' ).prop( 'checked' ),
				row_hover: $id( 'option-row-hover' ).prop( 'checked' ),
				print_name: $id( 'option-print-name' ).prop( 'checked' ),
				print_description: $id( 'option-print-description' ).prop( 'checked' ),
				print_name_position: $id( 'option-print-name-position' ).val(),
				print_description_position: $id( 'option-print-description-position' ).val(),
				extra_css_classes: $id( 'option-extra-css-classes' ).val(),
				// DataTables JS features
				use_datatables: $id( 'option-use-datatables' ).prop( 'checked' ),
				datatables_sort: $id( 'option-datatables-sort' ).prop( 'checked' ),
				datatables_filter: $id( 'option-datatables-filter' ).prop( 'checked' ),
				datatables_paginate: $id( 'option-datatables-paginate' ).prop( 'checked' ),
				datatables_lengthchange: $id( 'option-datatables-lengthchange' ).prop( 'checked' ),
				datatables_paginate_entries: $id( 'option-datatables-paginate_entries' ).val(),
				datatables_info: $id( 'option-datatables-info' ).prop( 'checked' ),
				datatables_scrollx: $id( 'option-datatables-scrollx' ).prop( 'checked' ),
				datatables_custom_commands: $id( 'option-datatables-custom-commands' ).val()
			};
			table_options = JSON.stringify( table_options );

			table_visibility.rows = $table_body.find( 'input[type="hidden"]' )
				.map( function() {
					if ( '1' === $(this).val() ) {
						return 1;
					}
					table_number.hidden_rows += 1;
					return 0;
				} )
				.get();
			table_visibility.columns = $id( 'edit-form-foot' ).find( 'input[type="hidden"]' )
				.map( function() {
					if ( '1' === $(this).val() ) {
						return 1;
					}
					table_number.hidden_columns += 1;
					return 0;
				} )
				.get();
			table_visibility = JSON.stringify( table_visibility );

			// request_data =
			return {
				action: wp_action,
				_ajax_nonce : $( wp_nonce ).val(),
				tablepress: {
					id: tp.table.id,
					new_id: tp.table.new_id,
					name: $id( 'table-name' ).val(),
					description: $id( 'table-description' ).val(),
					number: table_number,
					data: table_data,
					options: table_options,
					visibility: table_visibility
				}
			};
		},
		preview: {
			trigger: function( /* event */ ) {
				if ( ! tp.made_changes ) {
					tp.table.preview.show( $(this).attr( 'href' ) + '&TB_iframe=true' );
					return false;
				}

				// validation checks
				if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
					alert( tablepress_strings.num_pagination_entries_invalid );
					$id( 'option-datatables-paginate_entries' ).focus().select();
					return;
				}
				if ( ( /[^A-Za-z0-9- _]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
					alert( tablepress_strings.extra_css_classes_invalid );
					$id( 'option-extra-css-classes' ).focus().select();
					return;
				}

				$(this).closest( 'p' ).append( '<span class="animation-preview spinner is-active" title="' + tablepress_strings.preparing_preview + '"/>' );
				$( 'body' ).addClass( 'wait' );
				$id( 'table-preview' ).empty(); // clear preview

				$.ajax({
					'type': 'POST',
					'url': ajaxurl,
					'data': tp.table.prepare_ajax_request( 'tablepress_preview_table', '#nonce-preview-table' ),
					'success': tp.table.preview.ajax_success,
					'error': tp.table.preview.ajax_error,
					'dataType': 'json'
				} );

				return false;
			},
			ajax_success: function( data, status /*, jqXHR */ ) {
				if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
					tp.table.preview.error( 'AJAX call successful, but unclear status.' );
				} else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) || ( true !== data.success ) ) {
					tp.table.preview.error( 'AJAX call successful, but unclear data.' );
				} else {
					tp.table.preview.success( data );
				}
			},
			ajax_error: function( jqXHR, status, error_thrown ) {
				tp.table.preview.error( 'AJAX call failed: ' + status + ' - ' + error_thrown );
			},
			success: function( data ) {
				$id( 'table-preview' ).empty();
				$( '<iframe id="table-preview-iframe" />' ).load( function() {
					var $iframe = $(this).contents();
					$iframe.find( 'head' ).append( data.head_html );
					$iframe.find( 'body' ).append( data.body_html );
				} ).appendTo( '#table-preview' );
				$( '.animation-preview' ).remove();
				$( 'body' ).removeClass( 'wait' );
				tp.table.preview.show( '#TB_inline?inlineId=preview-container' );
			},
			error: function( message ) {
				$( '.animation-preview' ).closest( 'p' )
					.after( '<div class="ajax-alert preview-error error"><p>' + tablepress_strings.preview_error + ': ' + message + '</p></div>' );
				$( '.animation-preview' ).remove();
				$( '.preview-error' ).delay( 6000 ).fadeOut( 2000, function() { $(this).remove(); } );
				$( 'body' ).removeClass( 'wait' );
			},
			show: function( url ) {
				var width = $( window ).width() - 120,
					height = $( window ).height() - 120;
				if ( $( '#wpadminbar' ).length ) {
					height -= parseInt( $( '#wpadminbar' ).css( 'height' ), 10 );
				}
				tb_show( $( '.show-preview-button' ).first().text(), url + '&height=' + height + '&width=' + width, false );
			}
		}
	};

	tp.rows = {
		create: function( num_rows ) {
			var i, j,
				column_idxs,
				new_rows = '';

			for ( i = 0; i < num_rows; i++ ) {
				new_rows += tp.table.body_cells_pre;
				for ( j = 0; j < tp.table.columns; j++ ) {
					new_rows += tp.table.body_cell;
				}
				new_rows += tp.table.body_cells_post;
			}

			column_idxs = $id( 'edit-form-foot' ).find( '.column-hidden' )
				.map( function() { return $(this).index(); } ).get();
			return $( new_rows ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.addClass( 'column-hidden' );
			} );
		},
		append: function( /* event */ ) {
			var num_rows = $id( 'rows-append-number' ).val();

			if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_rows ) ) {
				alert( tablepress_strings.append_num_rows_invalid );
				$id( 'rows-append-number' ).focus().select();
				return;
			}

			$id( 'edit-form-body' ).append( tp.rows.create( num_rows ) );

			tp.rows.stripe();
			tp.reindex();
		},
		insert: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.before( tp.rows.create( 1 ) );

			tp.rows.stripe();
			tp.reindex();
		},
		duplicate: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.each( function( idx, row ) {
				var $row = $( row ),
					$textareas = $row.find( 'textarea' ),
					$duplicated_row = $row.clone();
				$duplicated_row.find( 'textarea' ).removeAttr( 'id' ).each( function( idx, cell ) {
					$( cell ).val( $textareas.eq( idx ).val() ); // setting val() is necessary, as clone() doesn't copy the current value, see jQuery bugs 5524, 2285, 3016
				} );
				$row.after( $duplicated_row );
			} );

			tp.rows.stripe();
			tp.reindex();
		},
		hide: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.addClass( 'row-hidden' ).find( '.visibility' ).val( '0' );

			tp.rows.stripe();
			tp.table.set_table_changed();
		},
		unhide: function( event ) {
			var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
				.prop( 'checked', event.shiftKey ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			$selected_rows.removeClass( 'row-hidden' ).find( '.visibility' ).val( '1' );

			tp.rows.stripe();
			tp.table.set_table_changed();
		},
		remove: function( /* event */ ) {
			var confirm_message,
				$selected_rows = $id( 'edit-form-body' ).find( 'input:checked' ).closest( 'tr' );

			if ( 0 === $selected_rows.length ) {
				alert( tablepress_strings.no_rows_selected );
				return;
			}

			if ( tp.table.rows === $selected_rows.length ) {
				alert( tablepress_strings.no_remove_all_rows );
				return;
			}

			if ( 1 === $selected_rows.length ) {
				confirm_message = tablepress_strings.ays_remove_rows_singular;
			} else {
				confirm_message = tablepress_strings.ays_remove_rows_plural;
			}
			if ( ! confirm( confirm_message ) ) {
				return;
			}

			$selected_rows.remove();

			tp.rows.stripe();
			tp.reindex();
		},
		move: {
			start: function( event, ui ) {
				$( ui.placeholder ).removeClass( 'row-hidden' ).css( 'visibility', 'visible' )
					.html( '<td colspan="' + ( tp.table.columns + tp.table.no_data_columns_pre + tp.table.no_data_columns_post ) + '"><div/></td>' );
				$( ui.helper ).removeClass( 'odd head-row foot-row' );
			},
			change: function( event, ui ) {
				tp.rows.stripe( ui.helper );
			},
			stop: function( /* event, ui */ ) {
				tp.rows.stripe();
			}
		},
		sort: function() {
			var column_idx = $(this).parent().index(),
				direction = ( $(this).hasClass( 'sort-asc' ) ) ? 1 : -1,
				$table_body = $('#edit-form-body'),
				$head_rows = $table_body.find( '.head-row' ).prevAll().addBack(),
				$foot_rows = $table_body.find( '.foot-row' ).nextAll().addBack(),
				rows = $table_body.children().not( $head_rows ).not( $foot_rows ).get(),
				/*
				 * Natural Sort algorithm for Javascript - Version 0.8.1 - Released under MIT license
				 * Author: Jim Palmer (based on chunking idea from Dave Koelle)
				 * See: https://github.com/overset/javascript-natural-sort and http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm-with-unicode-support/
				 */
				natural_sort = function( a, b ) {
					var re = /(^([+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|^0x[\da-fA-F]+$|\d+)/g,
						sre = /^\s+|\s+$/g,   // trim pre-post whitespace
						snre = /\s+/g,        // normalize all whitespace to single ' ' character
						dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
						hre = /^0x[0-9a-f]+$/i,
						ore = /^0/,
						// strip whitespace
						x = a.replace(sre, '') || '',
						y = b.replace(sre, '') || '',
						// chunk/tokenize
						xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
						yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
						// numeric, hex or date detection
						xD = parseInt(x.match(hre), 16) || (xN.length !== 1 && Date.parse(x)),
						yD = parseInt(y.match(hre), 16) || xD && y.match(dre) && Date.parse(y) || null,
						normChunk = function(s, l) {
							// normalize spaces; find floats not starting with '0', string or 0 if not defined (Clint Priest)
							return (!s.match(ore) || l === 1) && parseFloat(s) || s.replace(snre, ' ').replace(sre, '') || 0;
						},
						oFxNcL, oFyNcL;
					// first try and sort Hex codes or Dates
					if (yD) {
						if (xD < yD) { return -1; }
						else if (xD > yD) { return 1; }
					}
					// natural sorting through split numeric strings and default strings
					for(var cLoc = 0, xNl = xN.length, yNl = yN.length, numS = Math.max(xNl, yNl); cLoc < numS; cLoc++) {
						oFxNcL = normChunk(xN[cLoc] || '', xNl);
						oFyNcL = normChunk(yN[cLoc] || '', yNl);
						// handle numeric vs string comparison - number < string - (Kyle Adams)
						if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
							return isNaN(oFxNcL) ? 1 : -1;
						}
						// if unicode use locale comparison
						if (/[^\x00-\x80]/.test(oFxNcL + oFyNcL) && oFxNcL.localeCompare) {
							var comp = oFxNcL.localeCompare(oFyNcL);
							return comp / Math.abs(comp);
						}
						if (oFxNcL < oFyNcL) { return -1; }
						else if (oFxNcL > oFyNcL) { return 1; }
					}
				};

			$.each( rows, function( row_idx, row ) {
				row.sort_key = ( '' + $( row ).children().eq( column_idx ).find( 'textarea' ).val() ).toLowerCase(); // convert to string, and lower case for case insensitive sorting
			} );

			rows.sort( function( a, b ) {
				return direction * natural_sort( a.sort_key, b.sort_key );
			} );

			// might not be necessary:
			$.each( rows, function( row_idx, row ) {
				row.sort_key = null;
			} );

			$table_body.append( $head_rows );
			$table_body.append( rows );
			$table_body.append( $foot_rows );

			tp.rows.stripe();
			tp.reindex();
		},
		stripe: function( helper ) {
			if ( 'undefined' === typeof helper ) {
				helper = null;
			}
			helper = $( helper );
			var $rows = $id( 'edit-form-body' ).children().removeClass( 'odd head-row foot-row' ).not( helper );
			$rows.filter( ':even' ).addClass( 'odd' );
			$rows = $rows.not( '.row-hidden' );
			if ( helper.hasClass( 'row-hidden' ) ) {
				$rows = $rows.not( '.ui-sortable-placeholder' );
			}
			if ( tp.table.head ) {
				$rows.first().addClass( 'head-row' );
			}
			if ( tp.table.foot ) {
				$rows.last().addClass( 'foot-row' );
			}
		}
	};

	tp.columns = {
		append: function( /* event */ ) {
			var i,
				num_columns = $id( 'columns-append-number' ).val(),
				new_head_cells = '', new_body_cells = '', new_foot_cells = '';

			if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_columns ) ) {
				alert( tablepress_strings.append_num_columns_invalid );
				$id( 'columns-append-number' ).focus().select();
				return;
			}

			for ( i = 0; i < num_columns; i++ ) {
				new_body_cells += tp.table.body_cell;
				new_head_cells += tp.table.head_cell;
				new_foot_cells += tp.table.foot_cell;
			}

			$id( 'edit-form-body' ).children().each( function( row_idx, row ) {
				$( row ).children().slice( - tp.table.no_data_columns_post )
					.before( new_body_cells );
			} );
			$id( 'edit-form-head' ).children().slice( - tp.table.no_data_columns_post )
				.before( new_head_cells );
			$id( 'edit-form-foot' ).children().slice( - tp.table.no_data_columns_post )
				.before( new_foot_cells );

			tp.reindex();
		},
		insert: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.before( tp.table.body_cell );
			} );
			$id( 'edit-form-head' ).children()
				.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
				.before( tp.table.head_cell );
			$selected_columns.before( tp.table.foot_cell );

			tp.reindex();
		},
		duplicate: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form' ).find( 'tr' ).each( function( row_idx, row ) {
				$( row ).children().each( function( idx, cell ) {
					if ( -1 !== $.inArray( idx, column_idxs ) ) {
						var $cell = $( cell ),
							$duplicated_cell = $cell.clone();
							$duplicated_cell.find( 'textarea' ).removeAttr( 'id' ).val( $cell.find( 'textarea' ).val() ); // setting val() is necessary, as clone() doesn't copy the current value, see jQuery bugs 5524, 2285, 3016
						$cell.after( $duplicated_cell );
					}
				} );
			} );

			tp.reindex();
		},
		hide: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.addClass( 'column-hidden' );
			} );
			$selected_columns.addClass( 'column-hidden' ).find( '.visibility' ).val( '0' );

			tp.table.set_table_changed();
		},
		unhide: function( event ) {
			var column_idxs,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
					.prop( 'checked', event.shiftKey ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.removeClass( 'column-hidden' );
			} );
			$selected_columns.removeClass( 'column-hidden' ).find( '.visibility' ).val( '1' );

			tp.table.set_table_changed();
		},
		remove: function( /* event */ ) {
			var column_idxs,
				confirm_message,
				$selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' ).closest( 'th' );

			if ( 0 === $selected_columns.length ) {
				alert( tablepress_strings.no_columns_selected );
				return;
			}

			if ( tp.table.columns === $selected_columns.length ) {
				alert( tablepress_strings.no_remove_all_columns );
				return;
			}

			if ( 1 === $selected_columns.length ) {
				confirm_message = tablepress_strings.ays_remove_columns_singular;
			} else {
				confirm_message = tablepress_strings.ays_remove_columns_plural;
			}
			if ( ! confirm( confirm_message ) ) {
				return;
			}

			column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
			$id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
				$( row ).children()
					.filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
					.remove();
			} );
			$selected_columns.remove();

			tp.reindex();
		},
		move: {
			source_idx: -1,
			target_idx: -1,
			$rows: null,
			$row_children: null,
			$cell: null,
			$cells: null,
			$placeholder: null,
			$helper: null,
			start: function( event, ui ) {
				var $item = $( ui.item ),
					column_width;

				tp.columns.move.source_idx = $item.index();

				tp.columns.move.$rows = $id( 'edit-form-body' ).children().add( '#edit-form-foot' );

				tp.columns.move.$cells = tp.columns.move.$rows
					.find( ':nth-child(' + ( tp.columns.move.source_idx + 1 ) + ')' )
					.each( function() {
						tp.columns.move.$cell = $(this);
						$( '<td class="move-placeholder"><div/></td>' ).insertBefore( tp.columns.move.$cell );
						tp.columns.move.$cell.insertAfter( tp.columns.move.$cell.nextAll().last() )
							.clone().addClass( 'move-hover' ).insertAfter( tp.columns.move.$cell )
							.find( 'textarea' ).val( tp.columns.move.$cell.find( 'textarea' ).val() );
							// last line works around problem with clone() of textareas, see jQuery bugs 5524, 2285, 3016
					} )
					.hide();

				tp.columns.move.$helper = tp.columns.move.$rows.find( '.move-hover' );
				/* // seems not to be working for rows, so disable it for columns
					.each( function() {
						tp.columns.move.$cell = $(this);
						tp.columns.move.$cell.css( 'top', ( tp.columns.move.$cell.position().top - 3 ) + 'px' );
					} );
				*/

				column_width = tp.columns.move.$helper.eq(1).width(); // eq(0) is table foot
				tp.columns.move.$helper.eq(0).width( column_width );
				tp.columns.move.$placeholder = tp.columns.move.$rows.find( '.move-placeholder' );
				tp.columns.move.$placeholder.find( 'div' ).width( column_width );
			},
			change: function( event, ui ) {
				tp.columns.move.target_idx = $( ui.placeholder ).index();

				if ( ( tp.columns.move.target_idx - tp.columns.move.source_idx ) === 1 ) {
					tp.columns.move.target_idx += 1;
				} else {
					if ( tp.columns.move.target_idx === tp.columns.move.source_idx ) {
						tp.columns.move.target_idx -= 1;
					}
				}

				tp.columns.move.$placeholder.each( function() {
					tp.columns.move.$cell = $(this);
					tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().children().eq( tp.columns.move.target_idx ) );
				} );

				if ( tp.columns.move.target_idx > tp.columns.move.source_idx ) {
					tp.columns.move.target_idx -= 1;
				}

				tp.columns.move.source_idx = tp.columns.move.target_idx;
			},
			sort: function( event, ui ) {
				tp.columns.move.$helper.css( 'left', ui.position.left );
			},
			stop: function( /* event, ui */ ) {
				tp.columns.move.$helper.remove();
				tp.columns.move.$cells
					.each( function() {
						tp.columns.move.$cell = $(this);
						tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().find( '.move-placeholder' ) );
					} )
					.show();
				tp.columns.move.$placeholder.remove();

				tp.columns.move.source_idx = tp.columns.move.target_idx = -1;
				tp.columns.move.$rows = tp.columns.move.$row_children = tp.columns.move.$cell = tp.columns.move.$cells = tp.columns.move.$placeholder = tp.columns.move.$helper = null;

				tp.reindex();
			}
		},
		number_to_letter: function( number ) {
			var column = '';
			while ( number > 0 ) {
				column = String.fromCharCode( 65 + ( ( number-1) % 26 ) ) + column;
				number = Math.floor( (number-1) / 26 );
			}
			return column;
		}/*,
		letter_to_number: function( column ) {
			column = column.toUpperCase();
			var count = column.length,
				number = 0,
				i;
			for ( i = 0; i < count; i++ ) {
				number += ( column.charCodeAt( count-1-i ) - 64 ) * Math.pow( 26, i );
			}
			return number;
		}*/
	};

	tp.cells = {
		$focus: $( null ),
		$textarea: null,
		autogrow: function( /* event */ ) {
			tp.cells.$focus.removeClass( 'focus' );
			tp.cells.$focus = $(this).closest( 'tr' ).addClass( 'focus' );
		},
		advanced_editor: {
			prompt_shown: false,
			keyopen: function( event ) {
				if ( ! event.shiftKey ) {
					return;
				}

				var $advanced_editor = $id( 'advanced-editor-content' );
				tp.cells.$textarea = $(this).blur();
				$advanced_editor.val( tp.cells.$textarea.val() );
				$id( 'advanced-editor' ).wpdialog( 'open' );
				$advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
				$advanced_editor.focus();
			},
			buttonopen: function() {
				if ( ! tp.cells.advanced_editor.prompt_shown ) {
					if ( ! confirm( tablepress_strings.advanced_editor_open ) ) {
						return;
					}
				}

				tp.cells.advanced_editor.prompt_shown = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var $advanced_editor = $id( 'advanced-editor-content' );
					tp.cells.$textarea = $(this).blur();
					$advanced_editor.val( tp.cells.$textarea.val() );
					$id( 'advanced-editor' ).wpdialog( 'open' );
					$advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
					$advanced_editor.focus();
				} );
			},
			save: function() {
				var $ve_content = $id( 'advanced-editor-content' ).blur().val();
				if ( tp.cells.$textarea.val() !== $ve_content ) {
					tp.cells.$textarea.val( $ve_content );
					// position cursor at the end
					tp.cells.$textarea.get(0).selectionStart = tp.cells.$textarea.get(0).selectionEnd = tp.cells.$textarea.val().length;
					tp.table.set_table_changed();
				}
				tp.cells.$textarea.focus();
				tp.cells.advanced_editor.close();
			},
			close: function() {
				$id( 'advanced-editor' ).wpdialog( 'close' );
				return false;
			}
		},
		checkboxes: {
			last_clicked: { '#edit-form-body' : false, '#edit-form-foot' : false },
			multi_select: function ( event ) {
				if ( 'undefined' === event.shiftKey ) {
					return true;
				}

				if ( event.shiftKey ) {
					if ( ! tp.cells.checkboxes.last_clicked[ event.data.parent ] ) {
						return true;
					}

					var $checkboxes = $( event.data.parent ).find( ':checkbox' ),
						first_cb = $checkboxes.index( tp.cells.checkboxes.last_clicked[ event.data.parent ] ),
						last_cb = $checkboxes.index( this );
					if ( first_cb !== last_cb ) {
						$checkboxes.slice( Math.min( first_cb, last_cb ), Math.max( first_cb, last_cb ) ).prop( 'checked', $(this).prop( 'checked' ) );
					}
				}
				tp.cells.checkboxes.last_clicked[ event.data.parent ] = this;
				return true;
			}
		}
	};

	tp.content = {
		link: {
			prompt_shown: false,
			add: function( /* event */ ) {
				if ( ! tp.content.link.prompt_shown ) {
					if ( ! confirm( tablepress_strings.link_add ) ) {
						return;
					}
				}

				tp.content.link.prompt_shown = true;
				// mousedown instead of click to allow selection of text
				// mousedown will set the desired target textarea, and mouseup anywhere will show the link box
				// other approaches can lead to the wrong textarea being selected
				$id( 'edit-form-body' ).one( 'mousedown', 'textarea', function() {
					var editor_id = this.id;
					$( document ).one( 'mouseup', function() {
						if ( typeof wpLink !== 'undefined' ) {
							wpLink.open( editor_id );
							tp.table.set_table_changed();
						}
					} );
				} );
			}
		},
		image: {
			prompt_shown: false,
			add: function( /* event */ ) {
				if ( ! tp.content.image.prompt_shown ) {
					if ( ! confirm( tablepress_strings.image_add ) ) {
						return;
					}
				}

				tp.content.image.prompt_shown = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var editor = this.id,
						options = {
							frame: 'post',
							state: 'insert',
							title: wp.media.view.l10n.addMedia,
							multiple: true
						};

					// Move caret to the end, to prevent inserting right between existing text, as that's ugly in small cells (though possible in the Advanced Editor and Insert Link dialog).
					this.selectionStart = this.selectionEnd = this.value.length;

					// Remove focus from the textarea to prevent Opera from showing the outline of the textarea above the modal.
					// See: WP Core #22445
					$(this).blur();

					wp.media.editor.open( editor, options );
					tp.table.set_table_changed();
				} );
			}
		},
		span: {
			prompt_shown: false,
			add: function( span ) {
				var span_add_msg = ( '#rowspan#' === span ) ? tablepress_strings.rowspan_add : tablepress_strings.colspan_add;

				// init object, due to string keys
				if ( false === tp.content.span.prompt_shown ) {
					tp.content.span.prompt_shown = {};
					tp.content.span.prompt_shown['#rowspan#'] = tp.content.span.prompt_shown['#colspan#'] = false;
				}

				// Automatically deactivate DataTables, if cells are combined
				if ( $id( 'option-use-datatables' ).prop( 'checked' ) ) {
					if ( confirm( tablepress_strings.span_add_datatables_warning ) ) {
						$id( 'option-use-datatables' ).prop( 'checked', false ).change();
					} else {
						return;
					}
				}

				if ( ! tp.content.span.prompt_shown[ span ] ) {
					if ( ! confirm( span_add_msg ) ) {
						return;
					}
				}

				tp.content.span.prompt_shown[ span ] = true;
				$id( 'edit-form-body' ).one( 'click', 'textarea', function() {
					var $textarea = $(this),
						col_idx = $textarea.parent().index(),
						row_idx = $textarea.closest( 'tr' ).index();
					if ( '#rowspan#' === span ) {
						if ( 0 === row_idx ) {
							alert( tablepress_strings.no_rowspan_first_row );
							return;
						}
						if ( tp.table.head && 1 === row_idx ) {
							alert( tablepress_strings.no_rowspan_table_head );
							return;
						}
						if ( tp.table.foot && ( tp.table.rows - 1 ) === row_idx ) {
							alert( tablepress_strings.no_rowspan_table_foot );
							return;
						}
					} else if ( ( '#colspan#' === span ) && ( tp.table.no_data_columns_pre === col_idx ) ) {
						alert( tablepress_strings.no_colspan_first_col );
						return;
					}
					$textarea.val( span );
					tp.table.set_table_changed();
				} );
			}
		}
	};

	tp.check = {
		table_id: function( event ) {
			if ( ( 37 === event.which ) || ( 39 === event.which ) ) {
				return;
			}
			var $input = $(this);
			$input.val( $input.val().replace( /[^0-9a-zA-Z-_]/g, '' ) );
		},
		changes_saved: function() {
			if ( tp.made_changes ) {
				return tablepress_strings.unsaved_changes_unload;
			}
		}
	};

	tp.reindex = function() {
		var $row,
			$rows = $id( 'edit-form-body' ).children(),
			$cell, known_references = {};

		tp.table.rows = $rows.length;
		if ( tp.table.rows > 0 ) {
			tp.table.columns = $rows.first().children().length - tp.table.no_data_columns_pre - tp.table.no_data_columns_post;
		} else {
			tp.table.columns = 0;
		}

		$rows
		.each( function( row_idx, row ) {
			$row = $( row );
			$row.find( 'textarea' )
				.val( function( column_idx, value ) {
					// If the cell is not a formula, there's nothing to do here
					if ( ( '' === value ) || ( '=' !== value.charAt(0) ) ) {
						return value;
					}

					return value.replace( /([A-Z]+[0-9]+)(?::([A-Z]+[0-9]+))?/g, function( full_match, first_cell, second_cell ) {
						// first_cell must always exist, while second_cell only exists in ranges like A4:B7
						// we will use full_match as our result variable, so that we don't need an extra one

						if ( ! known_references.hasOwnProperty( first_cell ) ) {
							$cell = $id( 'cell-' + first_cell );
							if ( $cell.length ) {
								known_references[ first_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
							} else {
								known_references[ first_cell ] = first_cell;
							}
						}
						full_match = known_references[ first_cell ];

						if ( ( 'undefined' !== typeof second_cell ) && ( '' !== second_cell ) ) { // Chrome and IE pass an undefined variable, while Firefox passes an empty string
							if ( ! known_references.hasOwnProperty( second_cell ) ) {
								$cell = $id( 'cell-' + second_cell );
								if ( $cell.length ) {
									known_references[ second_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
								} else {
									known_references[ second_cell ] = second_cell;
								}
							}
							full_match += ':' + known_references[ second_cell ];
						}

						return full_match;
					} );
				} )
				.attr( 'name', function( column_idx /*, old_name */ ) {
					return 'table[data][' + row_idx + '][' + column_idx + ']';
				} );

			$row.find( '.move-handle' ).html( row_idx + 1 );
		} )
		.each( function( row_idx, row ) { // need a second loop here to not break logic in previous loop, that queries textareas by their old ID
			$( row ).find( 'textarea' ).attr( 'id', function( column_idx /*, old_id */ ) {
				return 'cell-' + tp.columns.number_to_letter( column_idx + 1 ) + ( row_idx + 1 );
			} );
		});
		$id( 'edit-form-head' ).find( '.move-handle' )
			.html( function( idx ) { return tp.columns.number_to_letter( idx + 1 ); } );

		$id( 'number-rows' ).val( tp.table.rows );
		$id( 'number-columns' ).val( tp.table.columns );

		tp.table.set_table_changed();
	};

	tp.save_changes = {
		trigger: function( event ) {
			// validation checks
			if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
				alert( tablepress_strings.num_pagination_entries_invalid );
				$id( 'option-datatables-paginate_entries' ).focus().select();
				return;
			}
			if ( ( /[^A-Za-z0-9- _]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
				alert( tablepress_strings.extra_css_classes_invalid );
				$id( 'option-extra-css-classes' ).focus().select();
				return;
			}

			if ( event.shiftKey ) {
				tp.made_changes = false; // to prevent onunload warning
				$id( 'tablepress-page' ).find( 'form' ).submit();
				return;
			}

			$(this).closest( 'p' ).append( '<span class="animation-saving spinner is-active" title="' + tablepress_strings.saving_changes + '"/>' );
			$( '.save-changes-button' ).prop( 'disabled', true );
			$( 'body' ).addClass( 'wait' );

			$.ajax({
				'type': 'POST',
				'url': ajaxurl,
				'data': tp.table.prepare_ajax_request( 'tablepress_save_table', '#nonce-edit-table' ),
				'success': tp.save_changes.ajax_success,
				'error': tp.save_changes.ajax_error,
				'dataType': 'json'
			} );
		},
		ajax_success: function( data, status /*, jqXHR */ ) {
			if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
				tp.save_changes.error( 'AJAX call successful, but unclear status. Try again while holding down the &#8220;Shift&#8221; key.' );
			} else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) ) {
				tp.save_changes.error( 'AJAX call successful, but unclear data. Try again while holding down the &#8220;Shift&#8221; key.' );
			} else if ( true !== data.success ) {
				var debug_html = '';
				// Print debug information, if we are in debug mode
				if ( ( 'undefined' !== typeof data.error_details ) && ( tablepress_options.print_debug_output ) ) {
					debug_html = '</p><p>These errors were encountered:</p><pre>' + data.error_details + '</pre><p>'; // Some HTML magic because this is wrapped in <p> when printed
				}
				tp.save_changes.error( 'AJAX call successful, internal saving process failed. Try again while holding down the &#8220;Shift&#8221; key.' + debug_html );
			} else {
				tp.save_changes.success( data );
			}
		},
		ajax_error: function( jqXHR, status, error_thrown ) {
			tp.save_changes.error( 'AJAX call failed: ' + status + ' - ' + error_thrown + '. Try again while holding down the &#8220;Shift&#8221; key.' );
		},
		success: function( data ) {
			// saving was successful, so the original ID has changed to the (maybe) new ID -> we need to adjust all occurrences
			if ( tp.table.id !== data.table_id ) {
				// update URL (for HTML5 browsers only), but only if ID really changed, to not get dummy entries in the browser history
				if ( ( 'pushState' in window.history ) && null !== window.history.pushState ) {
					window.history.pushState( '', '', window.location.href.replace( /table_id=[0-9a-zA-Z-_]+/gi, 'table_id=' + data.table_id ) );
				}
			}
			// update CSS class for data field form
			$id( 'edit-form' ).removeClass( 'tablepress-edit-screen-id-' + tp.table.id ).addClass( 'tablepress-edit-screen-id-' + data.table_id );
			// update table ID in input fields (type text and hidden)
			tp.table.id = tp.table.new_id = data.table_id;
			$id( 'table-id' ).val( tp.table.id );
			$id( 'table-new-id' ).val( tp.table.new_id );
			// update the Shortcode text field
			$( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' );
			// update the nonces
			$id( 'nonce-edit-table' ).val( data.new_edit_nonce );
			$id( 'nonce-preview-table' ).val( data.new_preview_nonce );
			// update URLs in Preview links
			var $show_preview_buttons = $( '.show-preview-button' );
			if ( $show_preview_buttons.length ) { // check necessary, because Preview button might not be visible
				$show_preview_buttons.attr( 'href',
					$show_preview_buttons.first().attr( 'href' )
						.replace( /item=[a-zA-Z0-9_-]+/g, 'item=' + data.table_id )
						.replace( /&_wpnonce=[a-z0-9]+/ig, '&_wpnonce=' + data.new_preview_nonce )
				);
			}
			// update last modified date and user nickname
			$id( 'last-modified' ).text( data.last_modified );
			$id( 'last-editor' ).text( data.last_editor );
			tp.table.unset_table_changed();
			tp.save_changes.after_saving_dialog( 'success', tablepress_strings[ data.message ] );
		},
		error: function( message ) {
			tp.save_changes.after_saving_dialog( 'error', message );
		},
		after_saving_dialog: function( type, message ) {
			if ( 'undefined' === typeof message ) {
				message = '';
			} else {
				message = ': ' + message;
			}
			var delay,
				div_class = 'save-changes-' + type;
			if ( 'success' === type ) {
				div_class += ' notice notice-success';
				delay = 3000;
			} else {
				div_class += ' notice notice-error';
				delay = 6000;
			}
			$( '.animation-saving' ).closest( 'p' )
				.after( '<div class="ajax-alert ' + div_class + '"><p>' + tablepress_strings['save_changes_' + type] + message + '</p></div>' );
			$( '.animation-saving' ).remove();
			$( '.save-changes-' + type ).delay( delay ).fadeOut( 2000, function() { $(this).remove(); } );
			$( '.save-changes-button' ).prop( 'disabled', false );
			$( 'body' ).removeClass( 'wait' );
		}
	};

	tp.init = function() {
		var callbacks = {
			'click': {
				'#rows-insert':			tp.rows.insert,
				'#columns-insert':		tp.columns.insert,
				'#rows-duplicate':		tp.rows.duplicate,
				'#columns-duplicate':	tp.columns.duplicate,
				'#rows-remove':			tp.rows.remove,
				'#columns-remove':		tp.columns.remove,
				'#rows-hide':			tp.rows.hide,
				'#columns-hide':		tp.columns.hide,
				'#rows-unhide':			tp.rows.unhide,
				'#columns-unhide':		tp.columns.unhide,
				'#rows-append':			tp.rows.append,
				'#columns-append':		tp.columns.append,
				'#link-add':			tp.content.link.add,
				'#image-add':			tp.content.image.add,
				'#span-add-rowspan':	function() { tp.content.span.add( '#rowspan#' ); },
				'#span-add-colspan':	function() { tp.content.span.add( '#colspan#' ); },
				'.show-preview-button': tp.table.preview.trigger,
				'.save-changes-button': tp.save_changes.trigger,
				'.show-help-box':		function() {
					$(this).next().wpdialog( {
						title: $(this).attr( 'title' ),
						height: 470,
						width: 320,
						modal: true,
						dialogClass: 'wp-dialog',
						resizable: false
					} );
				}
			},
			'keyup': {
				'#table-new-id':		tp.check.table_id
			},
			'change': {
				'#option-table-head':			tp.table.change_table_head,
				'#option-table-foot':			tp.table.change_table_foot,
				'#option-use-datatables':		tp.table.change_datatables,
				'#option-datatables-paginate':	tp.table.change_datatables_pagination
			},
			'blur': {
				'#table-new-id':		tp.table.change_id	// onchange would not recognize changed values from tp.check.table_id
			}
		},
		$table = $id( 'edit-form-body' );

		$.each( callbacks, function( event, event_callbacks ) {
			$.each( event_callbacks, function( selector, callback ) {
				$( selector ).on( event, callback );
			} );
		} );

		$( window ).on( 'beforeunload', tp.check.changes_saved );

		// do this before the next lines, to not trigger set_table_changed()
		$id( 'option-table-head' ).change(); // init changed/disabled states of DataTables JS features checkboxes
		$id( 'option-print-name' ).change( tp.table.change_print_name_description ).change(); // init dropdowns for name and description position
		$id( 'option-print-description' ).change( tp.table.change_print_name_description ).change();

		// just once is enough, will be reset after saving
		$table.one( 'change', 'textarea', tp.table.set_table_changed );
		$( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );

		if ( tablepress_options.cells_advanced_editor ) {
			$table.on( 'click', 'textarea', tp.cells.advanced_editor.keyopen );
			$id( 'advanced-editor-open' ).on( 'click', tp.cells.advanced_editor.buttonopen );
			$id( 'advanced-editor-confirm' ).on( 'click', tp.cells.advanced_editor.save );
			$id( 'advanced-editor-cancel' ).on( 'click', tp.cells.advanced_editor.close );
			$id( 'advanced-editor' ).wpdialog( {
				autoOpen: false,
				title: $id( 'advanced-editor-open' ).val(),
				width: 600,
				modal: true,
				dialogClass: 'wp-dialog',
				resizable: false
			} );
			// Fix issue with input fields not being usable (they are immediately losing focus without this) in the wpLink dialog when called through the "Advanced Editor"
			$id( 'wp-link' ).on( 'focus', 'input', function( event ) {
				event.stopPropagation();
			} );
		} else {
			$id( 'advanced-editor-open' ).hide();
		}

		// Fix issue with input fields not being usable (they are immediately losing focus without this) in the sidebar of the new Media Manager
		$( 'body' ).on( 'focus', '.media-modal .media-frame-content input, .media-modal .media-frame-content textarea', function( event ) {
			event.stopPropagation();
		} );

		if ( tablepress_options.cells_auto_grow ) {
			$table.on( 'focus', 'textarea', tp.cells.autogrow );
		}

		$id( 'edit-form-body' ).on( 'click', 'input:checkbox', { parent: '#edit-form-body' }, tp.cells.checkboxes.multi_select );
		$id( 'edit-form-foot' ).on( 'click', 'input:checkbox', { parent: '#edit-form-foot' }, tp.cells.checkboxes.multi_select );

		$id( 'edit-form-head' ).on( 'click', '.sort-control', tp.rows.sort );

		// on form submit: Enable disabled fields, so that they are transmitted in the POST request
		$id( 'tablepress-page' ).find( 'form' ).on( 'submit', function() {
			$(this).find( '.tablepress-postbox-table' ).find( 'input, select' ).prop( 'disabled', false );
		} );

		$table.sortable( {
			axis: 'y',
			containment: $id( 'edit-form' ), // to get better behavior when dragging before/after the first/last row
			forceHelperSize: true, // necessary?
			handle: '.move-handle',
			start: tp.rows.move.start,
			change: tp.rows.move.change,
			stop: tp.rows.move.stop,
			update: tp.reindex
		} ); // disableSelection() prohibits selection of text in textareas via keyboard

		$id( 'edit-form-head' ).sortable( {
			axis: 'x',
			items: '.head',
			containment: 'parent',
			forceHelperSize: true, // necessary?
			helper: 'clone',
			handle: '.move-handle',
			start: tp.columns.move.start,
			stop: tp.columns.move.stop,
			change: tp.columns.move.change,
			sort: tp.columns.move.sort
		} ).disableSelection();
	};

	// Run TablePress initialization.
	tp.init();

} );