PluginProbe
TablePress – Tables in WordPress made easy / 1.14
TablePress – Tables in WordPress made easy v1.14
3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.4.4 All 44 releases
tablepress / admin / js / edit.js

edit.js in TablePress – Tables in WordPress made easy 1.14, at admin/js/edit.js

1,285 lines 47.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * JavaScript code for the "Edit" screen
3 *
4 * @package TablePress
5 * @subpackage Views JavaScript
6 * @author Tobias Bäthge
7 * @since 1.0.0
8 */
9
10 /* global alert, confirm, tp, tablepress_strings, tablepress_options, ajaxurl, wpLink, tb_show, wp, JSON */
11
12 // Ensure the global `tp` object exists.
13 window.tp = window.tp || {};
14
15 jQuery( function( $ ) {
16
17 'use strict';
18
19 /* Wrapper to find elements in the page faster with JS-native functions */
20 var $id = function( element_id ) {
21 return $( document.getElementById( element_id ) );
22 };
23
24 /**
25 * TablePress object, mostly with functionality for the "Edit" screen
26 *
27 * @since 1.0.0
28 */
29 tp.made_changes = false;
30
31 tp.table = {
32 id: $id( 'table-id' ).val(),
33 new_id: $id( 'table-new-id' ).val(),
34 rows: parseInt( $id( 'number-rows' ).val(), 10 ),
35 columns: parseInt( $id( 'number-columns' ).val(), 10 ),
36 head: $id( 'option-table-head' ).prop( 'checked' ),
37 foot: $id( 'option-table-foot' ).prop( 'checked' ),
38 no_data_columns_pre: 2,
39 no_data_columns_post: 1,
40 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>',
41 body_cells_post: '<td><span class="move-handle"></span></td></tr>',
42 body_cell: '<td><textarea rows="1"></textarea></td>',
43 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>',
44 foot_cell: '<th><input type="checkbox" /><input type="hidden" class="visibility" name="table[visibility][columns][]" value="1" /></th>',
45 set_table_changed: function() {
46 tp.made_changes = true;
47 },
48 unset_table_changed: function() {
49 tp.made_changes = false;
50 $id( 'edit-form-body' ).one( 'change', 'textarea', tp.table.set_table_changed );
51 // @TODO: maybe use .tablepress-postbox-table here and further below
52 $( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );
53 },
54 change_id: function( /* event */ ) {
55 // empty table IDs are not allowed
56 if ( '' === $id( 'table-new-id' ).val().toString().trim() ) {
57 alert( tablepress_strings.table_id_not_empty );
58 $id( 'table-new-id' ).val( tp.table.new_id ).trigger( 'focus' ).trigger( 'select' );
59 return;
60 }
61 // the '0' table ID is not allowed
62 if ( '0' === $id( 'table-new-id' ).val().toString().trim() ) {
63 alert( tablepress_strings.table_id_not_zero );
64 $id( 'table-new-id' ).val( tp.table.new_id ).trigger( 'focus' ).trigger( 'select' );
65 return;
66 }
67
68 if ( this.value === tp.table.new_id ) {
69 return;
70 }
71
72 if ( confirm( tablepress_strings.ays_change_table_id ) ) {
73 tp.table.new_id = this.value;
74 $( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' ).trigger( 'click' ); // click() to focus and select
75 tp.table.set_table_changed();
76 } else {
77 $(this).val( tp.table.new_id );
78 }
79 },
80 change_table_head: function( /* event */ ) {
81 tp.table.head = $(this).prop( 'checked' );
82 $id( 'option-use-datatables' ).prop( 'disabled', ! tp.table.head ).trigger( 'change' );
83 $id( 'notice-datatables-head-row' ).toggle( ! tp.table.head );
84 tp.rows.stripe();
85 },
86 change_table_foot: function( /* event */ ) {
87 tp.table.foot = $(this).prop( 'checked' );
88 tp.rows.stripe();
89 },
90 change_print_name_description: function( /* event */ ) {
91 $id( this.id + '-position' ).prop( 'disabled', ! $(this).prop( 'checked' ) );
92 },
93 change_datatables: function() {
94 var $datatables_checkbox = $id( 'option-use-datatables' ),
95 checkboxes_disabled = ! ( $datatables_checkbox.prop( 'checked' ) && ! $datatables_checkbox.prop( 'disabled' ) );
96 $datatables_checkbox.closest( 'tbody' ).find( 'input' ).not( $datatables_checkbox ).prop( 'disabled', checkboxes_disabled );
97 tp.table.change_datatables_pagination();
98 },
99 change_datatables_pagination: function() {
100 var $pagination_checkbox = $id( 'option-datatables-paginate' ),
101 pagination_enabled = ( $pagination_checkbox.prop( 'checked' ) && ! $pagination_checkbox.prop( 'disabled' ) );
102 $id( 'option-datatables-lengthchange' ).prop( 'disabled', ! pagination_enabled );
103 $id( 'option-datatables-paginate_entries' ).prop( 'disabled', ! pagination_enabled );
104 },
105 prepare_ajax_request: function( wp_action, wp_nonce ) {
106 var $table_body = $id( 'edit-form-body' ),
107 table_data = [],
108 table_options,
109 table_number = { rows: tp.table.rows, columns: tp.table.columns, hidden_rows: 0, hidden_columns: 0 },
110 table_visibility = { rows: [], columns: [] };
111
112 $table_body.children().each( function( idx, row ) {
113 table_data[ idx ] = $( row ).find( 'textarea' )
114 .map( function() {
115 return this.value;
116 } )
117 .get();
118 } );
119 table_data = JSON.stringify( table_data );
120
121 // @TODO: maybe for options saving: https://stackoverflow.com/questions/1184624/convert-form-data-to-javascript-object-with-jquery
122 // or each()-loop through all checkboxes/textfields/selects
123 table_options = {
124 // Table Options
125 table_head: tp.table.head,
126 table_foot: tp.table.foot,
127 alternating_row_colors: $id( 'option-alternating-row-colors' ).prop( 'checked' ),
128 row_hover: $id( 'option-row-hover' ).prop( 'checked' ),
129 print_name: $id( 'option-print-name' ).prop( 'checked' ),
130 print_description: $id( 'option-print-description' ).prop( 'checked' ),
131 print_name_position: $id( 'option-print-name-position' ).val(),
132 print_description_position: $id( 'option-print-description-position' ).val(),
133 extra_css_classes: $id( 'option-extra-css-classes' ).val(),
134 // DataTables JS features
135 use_datatables: $id( 'option-use-datatables' ).prop( 'checked' ),
136 datatables_sort: $id( 'option-datatables-sort' ).prop( 'checked' ),
137 datatables_filter: $id( 'option-datatables-filter' ).prop( 'checked' ),
138 datatables_paginate: $id( 'option-datatables-paginate' ).prop( 'checked' ),
139 datatables_lengthchange: $id( 'option-datatables-lengthchange' ).prop( 'checked' ),
140 datatables_paginate_entries: $id( 'option-datatables-paginate_entries' ).val(),
141 datatables_info: $id( 'option-datatables-info' ).prop( 'checked' ),
142 datatables_scrollx: $id( 'option-datatables-scrollx' ).prop( 'checked' ),
143 datatables_custom_commands: $id( 'option-datatables-custom-commands' ).val()
144 };
145 table_options = JSON.stringify( table_options );
146
147 table_visibility.rows = $table_body.find( 'input[type="hidden"]' )
148 .map( function() {
149 if ( '1' === $(this).val() ) {
150 return 1;
151 }
152 table_number.hidden_rows += 1;
153 return 0;
154 } )
155 .get();
156 table_visibility.columns = $id( 'edit-form-foot' ).find( 'input[type="hidden"]' )
157 .map( function() {
158 if ( '1' === $(this).val() ) {
159 return 1;
160 }
161 table_number.hidden_columns += 1;
162 return 0;
163 } )
164 .get();
165 table_visibility = JSON.stringify( table_visibility );
166
167 // request_data =
168 return {
169 action: wp_action,
170 _ajax_nonce : $( wp_nonce ).val(),
171 tablepress: {
172 id: tp.table.id,
173 new_id: tp.table.new_id,
174 name: $id( 'table-name' ).val(),
175 description: $id( 'table-description' ).val(),
176 number: table_number,
177 data: table_data,
178 options: table_options,
179 visibility: table_visibility
180 }
181 };
182 },
183 preview: {
184 trigger: function( /* event */ ) {
185 if ( ! tp.made_changes ) {
186 tp.table.preview.show( $(this).attr( 'href' ) + '&TB_iframe=true' );
187 return false;
188 }
189
190 // validation checks
191 if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
192 alert( tablepress_strings.num_pagination_entries_invalid );
193 $id( 'option-datatables-paginate_entries' ).trigger( 'focus' ).trigger( 'select' );
194 return;
195 }
196 if ( ( /[^A-Za-z0-9- _:]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
197 alert( tablepress_strings.extra_css_classes_invalid );
198 $id( 'option-extra-css-classes' ).trigger( 'focus' ).trigger( 'select' );
199 return;
200 }
201
202 $(this).closest( 'p' ).append( '<span class="animation-preview spinner is-active" title="' + tablepress_strings.preparing_preview + '"/>' );
203 $( 'body' ).addClass( 'wait' );
204 $id( 'table-preview' ).empty(); // clear preview
205
206 $.ajax({
207 'type': 'POST',
208 'url': ajaxurl,
209 'data': tp.table.prepare_ajax_request( 'tablepress_preview_table', '#nonce-preview-table' ),
210 'success': tp.table.preview.ajax_success,
211 'error': tp.table.preview.ajax_error,
212 'dataType': 'json'
213 } );
214
215 return false;
216 },
217 ajax_success: function( data, status /*, jqXHR */ ) {
218 if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
219 tp.table.preview.error( 'AJAX call successful, but unclear status.' );
220 } else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) || ( true !== data.success ) ) {
221 tp.table.preview.error( 'AJAX call successful, but unclear data.' );
222 } else {
223 tp.table.preview.success( data );
224 }
225 },
226 ajax_error: function( jqXHR, status, error_thrown ) {
227 tp.table.preview.error( 'AJAX call failed: ' + status + ' - ' + error_thrown );
228 },
229 success: function( data ) {
230 $id( 'table-preview' ).empty();
231 $( '<iframe id="table-preview-iframe" />' ).load( function() {
232 var $iframe = $(this).contents();
233 $iframe.find( 'head' ).append( data.head_html );
234 $iframe.find( 'body' ).append( data.body_html );
235 } ).appendTo( '#table-preview' );
236 $( '.animation-preview' ).remove();
237 $( 'body' ).removeClass( 'wait' );
238 tp.table.preview.show( '#TB_inline?inlineId=preview-container' );
239 },
240 error: function( message ) {
241 $( '.animation-preview' ).closest( 'p' )
242 .after( '<div class="ajax-alert preview-error error"><p>' + tablepress_strings.preview_error + ': ' + message + '</p></div>' );
243 $( '.animation-preview' ).remove();
244 $( '.preview-error' ).delay( 6000 ).fadeOut( 2000, function() { $(this).remove(); } );
245 $( 'body' ).removeClass( 'wait' );
246 },
247 show: function( url ) {
248 var width = $( window ).width() - 120,
249 height = $( window ).height() - 120;
250 if ( $( '#wpadminbar' ).length ) {
251 height -= parseInt( $( '#wpadminbar' ).css( 'height' ), 10 );
252 }
253 tb_show( $( '.show-preview-button' ).first().text(), url + '&height=' + height + '&width=' + width, false );
254 }
255 }
256 };
257
258 tp.rows = {
259 create: function( num_rows ) {
260 var i, j,
261 column_idxs,
262 new_rows = '';
263
264 for ( i = 0; i < num_rows; i++ ) {
265 new_rows += tp.table.body_cells_pre;
266 for ( j = 0; j < tp.table.columns; j++ ) {
267 new_rows += tp.table.body_cell;
268 }
269 new_rows += tp.table.body_cells_post;
270 }
271
272 column_idxs = $id( 'edit-form-foot' ).find( '.column-hidden' )
273 .map( function() { return $(this).index(); } ).get();
274 return $( new_rows ).each( function( row_idx, row ) {
275 $( row ).children()
276 .filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
277 .addClass( 'column-hidden' );
278 } );
279 },
280 append: function( /* event */ ) {
281 var num_rows = $id( 'rows-append-number' ).val();
282
283 if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_rows ) ) {
284 alert( tablepress_strings.append_num_rows_invalid );
285 $id( 'rows-append-number' ).trigger( 'focus' ).trigger( 'select' );
286 return;
287 }
288
289 $id( 'edit-form-body' ).append( tp.rows.create( num_rows ) );
290
291 tp.rows.stripe();
292 tp.reindex();
293 },
294 insert: function( event ) {
295 var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
296 .prop( 'checked', event.shiftKey ).closest( 'tr' );
297
298 if ( 0 === $selected_rows.length ) {
299 alert( tablepress_strings.no_rows_selected );
300 return;
301 }
302
303 $selected_rows.before( tp.rows.create( 1 ) );
304
305 tp.rows.stripe();
306 tp.reindex();
307 },
308 duplicate: function( event ) {
309 var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
310 .prop( 'checked', event.shiftKey ).closest( 'tr' );
311
312 if ( 0 === $selected_rows.length ) {
313 alert( tablepress_strings.no_rows_selected );
314 return;
315 }
316
317 $selected_rows.each( function( idx, row ) {
318 var $row = $( row ),
319 $textareas = $row.find( 'textarea' ),
320 $duplicated_row = $row.clone();
321 $duplicated_row.find( 'textarea' ).removeAttr( 'id' ).each( function( idx, cell ) {
322 $( cell ).val( $textareas.eq( idx ).val() ); // setting val() is necessary, as clone() doesn't copy the current value, see jQuery bugs 5524, 2285, 3016
323 } );
324 $row.after( $duplicated_row );
325 } );
326
327 tp.rows.stripe();
328 tp.reindex();
329 },
330 hide: function( event ) {
331 var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
332 .prop( 'checked', event.shiftKey ).closest( 'tr' );
333
334 if ( 0 === $selected_rows.length ) {
335 alert( tablepress_strings.no_rows_selected );
336 return;
337 }
338
339 $selected_rows.addClass( 'row-hidden' ).find( '.visibility' ).val( '0' );
340
341 tp.rows.stripe();
342 tp.table.set_table_changed();
343 },
344 unhide: function( event ) {
345 var $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' )
346 .prop( 'checked', event.shiftKey ).closest( 'tr' );
347
348 if ( 0 === $selected_rows.length ) {
349 alert( tablepress_strings.no_rows_selected );
350 return;
351 }
352
353 $selected_rows.removeClass( 'row-hidden' ).find( '.visibility' ).val( '1' );
354
355 tp.rows.stripe();
356 tp.table.set_table_changed();
357 },
358 remove: function( /* event */ ) {
359 var confirm_message,
360 $selected_rows = $id( 'edit-form-body' ).find( 'input:checked' ).closest( 'tr' );
361
362 if ( 0 === $selected_rows.length ) {
363 alert( tablepress_strings.no_rows_selected );
364 return;
365 }
366
367 if ( tp.table.rows === $selected_rows.length ) {
368 alert( tablepress_strings.no_remove_all_rows );
369 return;
370 }
371
372 if ( 1 === $selected_rows.length ) {
373 confirm_message = tablepress_strings.ays_remove_rows_singular;
374 } else {
375 confirm_message = tablepress_strings.ays_remove_rows_plural;
376 }
377 if ( ! confirm( confirm_message ) ) {
378 return;
379 }
380
381 $selected_rows.remove();
382
383 tp.rows.stripe();
384 tp.reindex();
385 },
386 move: {
387 start: function( event, ui ) {
388 $( ui.placeholder ).removeClass( 'row-hidden' ).css( 'visibility', 'visible' )
389 .html( '<td colspan="' + ( tp.table.columns + tp.table.no_data_columns_pre + tp.table.no_data_columns_post ) + '"><div/></td>' );
390 $( ui.helper ).removeClass( 'odd head-row foot-row' );
391 },
392 change: function( event, ui ) {
393 tp.rows.stripe( ui.helper );
394 },
395 stop: function( /* event, ui */ ) {
396 tp.rows.stripe();
397 }
398 },
399 sort: function() {
400 var column_idx = $(this).parent().index(),
401 direction = ( $(this).hasClass( 'sort-asc' ) ) ? 1 : -1,
402 $table_body = $('#edit-form-body'),
403 $head_rows = $table_body.find( '.head-row' ).prevAll().addBack(),
404 $foot_rows = $table_body.find( '.foot-row' ).nextAll().addBack(),
405 rows = $table_body.children().not( $head_rows ).not( $foot_rows ).get(),
406 /*
407 * Natural Sort algorithm for Javascript - Version 0.8.1 - Released under MIT license
408 * Author: Jim Palmer (based on chunking idea from Dave Koelle)
409 * See: https://github.com/overset/javascript-natural-sort and http://www.overset.com/2008/09/01/javascript-natural-sort-algorithm-with-unicode-support/
410 */
411 natural_sort = function( a, b ) {
412 var re = /(^([+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|^0x[\da-fA-F]+$|\d+)/g,
413 sre = /^\s+|\s+$/g, // trim pre-post whitespace
414 snre = /\s+/g, // normalize all whitespace to single ' ' character
415 dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
416 hre = /^0x[0-9a-f]+$/i,
417 ore = /^0/,
418 // strip whitespace
419 x = a.replace(sre, '') || '',
420 y = b.replace(sre, '') || '',
421 // chunk/tokenize
422 xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
423 yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
424 // numeric, hex or date detection
425 xD = parseInt(x.match(hre), 16) || (xN.length !== 1 && Date.parse(x)),
426 yD = parseInt(y.match(hre), 16) || xD && y.match(dre) && Date.parse(y) || null,
427 normChunk = function(s, l) {
428 // normalize spaces; find floats not starting with '0', string or 0 if not defined (Clint Priest)
429 return (!s.match(ore) || l === 1) && parseFloat(s) || s.replace(snre, ' ').replace(sre, '') || 0;
430 },
431 oFxNcL, oFyNcL;
432 // first try and sort Hex codes or Dates
433 if (yD) {
434 if (xD < yD) { return -1; }
435 else if (xD > yD) { return 1; }
436 }
437 // natural sorting through split numeric strings and default strings
438 for(var cLoc = 0, xNl = xN.length, yNl = yN.length, numS = Math.max(xNl, yNl); cLoc < numS; cLoc++) {
439 oFxNcL = normChunk(xN[cLoc] || '', xNl);
440 oFyNcL = normChunk(yN[cLoc] || '', yNl);
441 // handle numeric vs string comparison - number < string - (Kyle Adams)
442 if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
443 return isNaN(oFxNcL) ? 1 : -1;
444 }
445 // if unicode use locale comparison
446 if (/[^\x00-\x80]/.test(oFxNcL + oFyNcL) && oFxNcL.localeCompare) {
447 var comp = oFxNcL.localeCompare(oFyNcL);
448 return comp / Math.abs(comp);
449 }
450 if (oFxNcL < oFyNcL) { return -1; }
451 else if (oFxNcL > oFyNcL) { return 1; }
452 }
453 };
454
455 $.each( rows, function( row_idx, row ) {
456 row.sort_key = ( '' + $( row ).children().eq( column_idx ).find( 'textarea' ).val() ).toLowerCase(); // convert to string, and lower case for case insensitive sorting
457 } );
458
459 rows.sort( function( a, b ) {
460 return direction * natural_sort( a.sort_key, b.sort_key );
461 } );
462
463 // might not be necessary:
464 $.each( rows, function( row_idx, row ) {
465 row.sort_key = null;
466 } );
467
468 $table_body.append( $head_rows );
469 $table_body.append( rows );
470 $table_body.append( $foot_rows );
471
472 tp.rows.stripe();
473 tp.reindex();
474 },
475 stripe: function( helper ) {
476 if ( 'undefined' === typeof helper ) {
477 helper = null;
478 }
479 helper = $( helper );
480 var $rows = $id( 'edit-form-body' ).children().removeClass( 'odd head-row foot-row' ).not( helper );
481 $rows.even().addClass( 'odd' );
482 $rows = $rows.not( '.row-hidden' );
483 if ( helper.hasClass( 'row-hidden' ) ) {
484 $rows = $rows.not( '.ui-sortable-placeholder' );
485 }
486 if ( tp.table.head ) {
487 $rows.first().addClass( 'head-row' );
488 }
489 if ( tp.table.foot ) {
490 $rows.last().addClass( 'foot-row' );
491 }
492 }
493 };
494
495 tp.columns = {
496 append: function( /* event */ ) {
497 var i,
498 num_columns = $id( 'columns-append-number' ).val(),
499 new_head_cells = '', new_body_cells = '', new_foot_cells = '';
500
501 if ( ! ( /^[1-9][0-9]{0,4}$/ ).test( num_columns ) ) {
502 alert( tablepress_strings.append_num_columns_invalid );
503 $id( 'columns-append-number' ).trigger( 'focus' ).trigger( 'select' );
504 return;
505 }
506
507 for ( i = 0; i < num_columns; i++ ) {
508 new_body_cells += tp.table.body_cell;
509 new_head_cells += tp.table.head_cell;
510 new_foot_cells += tp.table.foot_cell;
511 }
512
513 $id( 'edit-form-body' ).children().each( function( row_idx, row ) {
514 $( row ).children().slice( - tp.table.no_data_columns_post )
515 .before( new_body_cells );
516 } );
517 $id( 'edit-form-head' ).children().slice( - tp.table.no_data_columns_post )
518 .before( new_head_cells );
519 $id( 'edit-form-foot' ).children().slice( - tp.table.no_data_columns_post )
520 .before( new_foot_cells );
521
522 tp.reindex();
523 },
524 insert: function( event ) {
525 var column_idxs,
526 $selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
527 .prop( 'checked', event.shiftKey ).closest( 'th' );
528
529 if ( 0 === $selected_columns.length ) {
530 alert( tablepress_strings.no_columns_selected );
531 return;
532 }
533
534 column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
535 $id( 'edit-form-body' ).children().each( function( row_idx, row ) {
536 $( row ).children()
537 .filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
538 .before( tp.table.body_cell );
539 } );
540 $id( 'edit-form-head' ).children()
541 .filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
542 .before( tp.table.head_cell );
543 $selected_columns.before( tp.table.foot_cell );
544
545 tp.reindex();
546 },
547 duplicate: function( event ) {
548 var column_idxs,
549 $selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
550 .prop( 'checked', event.shiftKey ).closest( 'th' );
551
552 if ( 0 === $selected_columns.length ) {
553 alert( tablepress_strings.no_columns_selected );
554 return;
555 }
556
557 column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
558 $id( 'edit-form' ).find( 'tr' ).each( function( row_idx, row ) {
559 $( row ).children().each( function( idx, cell ) {
560 if ( -1 !== $.inArray( idx, column_idxs ) ) {
561 var $cell = $( cell ),
562 $duplicated_cell = $cell.clone();
563 $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
564 $cell.after( $duplicated_cell );
565 }
566 } );
567 } );
568
569 tp.reindex();
570 },
571 hide: function( event ) {
572 var column_idxs,
573 $selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
574 .prop( 'checked', event.shiftKey ).closest( 'th' );
575
576 if ( 0 === $selected_columns.length ) {
577 alert( tablepress_strings.no_columns_selected );
578 return;
579 }
580
581 column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
582 $id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
583 $( row ).children()
584 .filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
585 .addClass( 'column-hidden' );
586 } );
587 $selected_columns.addClass( 'column-hidden' ).find( '.visibility' ).val( '0' );
588
589 tp.table.set_table_changed();
590 },
591 unhide: function( event ) {
592 var column_idxs,
593 $selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' )
594 .prop( 'checked', event.shiftKey ).closest( 'th' );
595
596 if ( 0 === $selected_columns.length ) {
597 alert( tablepress_strings.no_columns_selected );
598 return;
599 }
600
601 column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
602 $id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
603 $( row ).children()
604 .filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
605 .removeClass( 'column-hidden' );
606 } );
607 $selected_columns.removeClass( 'column-hidden' ).find( '.visibility' ).val( '1' );
608
609 tp.table.set_table_changed();
610 },
611 remove: function( /* event */ ) {
612 var column_idxs,
613 confirm_message,
614 $selected_columns = $id( 'edit-form-foot' ).find( 'input:checked' ).closest( 'th' );
615
616 if ( 0 === $selected_columns.length ) {
617 alert( tablepress_strings.no_columns_selected );
618 return;
619 }
620
621 if ( tp.table.columns === $selected_columns.length ) {
622 alert( tablepress_strings.no_remove_all_columns );
623 return;
624 }
625
626 if ( 1 === $selected_columns.length ) {
627 confirm_message = tablepress_strings.ays_remove_columns_singular;
628 } else {
629 confirm_message = tablepress_strings.ays_remove_columns_plural;
630 }
631 if ( ! confirm( confirm_message ) ) {
632 return;
633 }
634
635 column_idxs = $selected_columns.map( function() { return $(this).index(); } ).get();
636 $id( 'edit-form-body' ).children().add( '#edit-form-head' ).each( function( row_idx, row ) {
637 $( row ).children()
638 .filter( function( idx ) { return ( -1 !== $.inArray( idx, column_idxs ) ); } )
639 .remove();
640 } );
641 $selected_columns.remove();
642
643 tp.reindex();
644 },
645 move: {
646 source_idx: -1,
647 target_idx: -1,
648 $rows: null,
649 $row_children: null,
650 $cell: null,
651 $cells: null,
652 $placeholder: null,
653 $helper: null,
654 start: function( event, ui ) {
655 var $item = $( ui.item ),
656 column_width;
657
658 tp.columns.move.source_idx = $item.index();
659
660 tp.columns.move.$rows = $id( 'edit-form-body' ).children().add( '#edit-form-foot' );
661
662 tp.columns.move.$cells = tp.columns.move.$rows
663 .children( ':nth-child(' + ( tp.columns.move.source_idx + 1 ) + ')' )
664 .each( function() {
665 tp.columns.move.$cell = $(this);
666 $( '<td class="move-placeholder"><div/></td>' ).insertBefore( tp.columns.move.$cell );
667 tp.columns.move.$cell.insertAfter( tp.columns.move.$cell.nextAll().last() )
668 .clone().addClass( 'move-hover' ).insertAfter( tp.columns.move.$cell )
669 .find( 'textarea' ).val( tp.columns.move.$cell.find( 'textarea' ).val() );
670 // last line works around problem with clone() of textareas, see jQuery bugs 5524, 2285, 3016
671 } )
672 .hide();
673
674 tp.columns.move.$helper = tp.columns.move.$rows.find( '.move-hover' );
675 /* // seems not to be working for rows, so disable it for columns
676 .each( function() {
677 tp.columns.move.$cell = $(this);
678 tp.columns.move.$cell.css( 'top', ( tp.columns.move.$cell.position().top - 3 ) + 'px' );
679 } );
680 */
681
682 column_width = tp.columns.move.$helper.eq(1).width(); // eq(0) is table foot
683 tp.columns.move.$helper.eq(0).width( column_width );
684 tp.columns.move.$placeholder = tp.columns.move.$rows.find( '.move-placeholder' );
685 tp.columns.move.$placeholder.find( 'div' ).width( column_width );
686 },
687 change: function( event, ui ) {
688 tp.columns.move.target_idx = $( ui.placeholder ).index();
689
690 if ( ( tp.columns.move.target_idx - tp.columns.move.source_idx ) === 1 ) {
691 tp.columns.move.target_idx += 1;
692 } else {
693 if ( tp.columns.move.target_idx === tp.columns.move.source_idx ) {
694 tp.columns.move.target_idx -= 1;
695 }
696 }
697
698 tp.columns.move.$placeholder.each( function() {
699 tp.columns.move.$cell = $(this);
700 tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().children().eq( tp.columns.move.target_idx ) );
701 } );
702
703 if ( tp.columns.move.target_idx > tp.columns.move.source_idx ) {
704 tp.columns.move.target_idx -= 1;
705 }
706
707 tp.columns.move.source_idx = tp.columns.move.target_idx;
708 },
709 sort: function( event, ui ) {
710 tp.columns.move.$helper.css( 'left', ui.position.left );
711 },
712 stop: function( /* event, ui */ ) {
713 tp.columns.move.$helper.remove();
714 tp.columns.move.$cells
715 .each( function() {
716 tp.columns.move.$cell = $(this);
717 tp.columns.move.$cell.insertBefore( tp.columns.move.$cell.parent().find( '.move-placeholder' ) );
718 } )
719 .show();
720 tp.columns.move.$placeholder.remove();
721
722 tp.columns.move.source_idx = tp.columns.move.target_idx = -1;
723 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;
724
725 tp.reindex();
726 }
727 },
728 number_to_letter: function( number ) {
729 var column = '';
730 while ( number > 0 ) {
731 column = String.fromCharCode( 65 + ( ( number-1) % 26 ) ) + column;
732 number = Math.floor( (number-1) / 26 );
733 }
734 return column;
735 }/*,
736 letter_to_number: function( column ) {
737 column = column.toUpperCase();
738 var count = column.length,
739 number = 0,
740 i;
741 for ( i = 0; i < count; i++ ) {
742 number += ( column.charCodeAt( count-1-i ) - 64 ) * Math.pow( 26, i );
743 }
744 return number;
745 }*/
746 };
747
748 tp.cells = {
749 $focus: $( null ),
750 $textarea: null,
751 autogrow: function( /* event */ ) {
752 tp.cells.$focus.removeClass( 'focus' );
753 tp.cells.$focus = $(this).closest( 'tr' ).addClass( 'focus' );
754 },
755 advanced_editor: {
756 prompt_shown: false,
757 keyopen: function( event ) {
758 if ( ! event.shiftKey ) {
759 return;
760 }
761
762 var $advanced_editor = $id( 'advanced-editor-content' );
763 tp.cells.$textarea = $(this).trigger( 'blur' );
764 $advanced_editor.val( tp.cells.$textarea.val() );
765 $id( 'advanced-editor' ).wpdialog( 'open' );
766 $advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
767 $advanced_editor.trigger( 'focus' );
768 },
769 buttonopen: function() {
770 if ( ! tp.cells.advanced_editor.prompt_shown ) {
771 if ( ! confirm( tablepress_strings.advanced_editor_open ) ) {
772 return;
773 }
774 }
775
776 tp.cells.advanced_editor.prompt_shown = true;
777 $id( 'edit-form-body' ).one( 'click', 'textarea', function() {
778 var $advanced_editor = $id( 'advanced-editor-content' );
779 tp.cells.$textarea = $(this).trigger( 'blur' );
780 $advanced_editor.val( tp.cells.$textarea.val() );
781 $id( 'advanced-editor' ).wpdialog( 'open' );
782 $advanced_editor.get(0).selectionStart = $advanced_editor.get(0).selectionEnd = $advanced_editor.val().length;
783 $advanced_editor.trigger( 'focus' );
784 } );
785 },
786 save: function() {
787 var $ve_content = $id( 'advanced-editor-content' ).trigger( 'blur' ).val();
788 if ( tp.cells.$textarea.val() !== $ve_content ) {
789 tp.cells.$textarea.val( $ve_content );
790 // position cursor at the end
791 tp.cells.$textarea.get(0).selectionStart = tp.cells.$textarea.get(0).selectionEnd = tp.cells.$textarea.val().length;
792 tp.table.set_table_changed();
793 }
794 tp.cells.$textarea.trigger( 'focus' );
795 tp.cells.advanced_editor.close();
796 },
797 close: function() {
798 $id( 'advanced-editor' ).wpdialog( 'close' );
799 return false;
800 }
801 },
802 checkboxes: {
803 last_clicked: { '#edit-form-body' : false, '#edit-form-foot' : false },
804 multi_select: function ( event ) {
805 if ( 'undefined' === event.shiftKey ) {
806 return true;
807 }
808
809 if ( event.shiftKey ) {
810 if ( ! tp.cells.checkboxes.last_clicked[ event.data.parent ] ) {
811 return true;
812 }
813
814 var $checkboxes = $( event.data.parent ).find( ':checkbox' ),
815 first_cb = $checkboxes.index( tp.cells.checkboxes.last_clicked[ event.data.parent ] ),
816 last_cb = $checkboxes.index( this );
817 if ( first_cb !== last_cb ) {
818 $checkboxes.slice( Math.min( first_cb, last_cb ), Math.max( first_cb, last_cb ) ).prop( 'checked', $(this).prop( 'checked' ) );
819 }
820 }
821 tp.cells.checkboxes.last_clicked[ event.data.parent ] = this;
822 return true;
823 }
824 }
825 };
826
827 tp.content = {
828 link: {
829 prompt_shown: false,
830 add: function( /* event */ ) {
831 if ( ! tp.content.link.prompt_shown ) {
832 if ( ! confirm( tablepress_strings.link_add ) ) {
833 return;
834 }
835 }
836
837 tp.content.link.prompt_shown = true;
838 // mousedown instead of click to allow selection of text
839 // mousedown will set the desired target textarea, and mouseup anywhere will show the link box
840 // other approaches can lead to the wrong textarea being selected
841 $id( 'edit-form-body' ).one( 'mousedown', 'textarea', function() {
842 var editor_id = this.id;
843 $( document ).one( 'mouseup', function() {
844 if ( typeof wpLink !== 'undefined' ) {
845 wpLink.open( editor_id );
846 tp.table.set_table_changed();
847 }
848 } );
849 } );
850 }
851 },
852 image: {
853 prompt_shown: false,
854 add: function( /* event */ ) {
855 if ( ! tp.content.image.prompt_shown ) {
856 if ( ! confirm( tablepress_strings.image_add ) ) {
857 return;
858 }
859 }
860
861 tp.content.image.prompt_shown = true;
862 $id( 'edit-form-body' ).one( 'click', 'textarea', function() {
863 var editor = this.id,
864 options = {
865 frame: 'post',
866 state: 'insert',
867 title: wp.media.view.l10n.addMedia,
868 multiple: true
869 };
870
871 // 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).
872 this.selectionStart = this.selectionEnd = this.value.length;
873
874 // Remove focus from the textarea to prevent Opera from showing the outline of the textarea above the modal.
875 // See: WP Core #22445
876 $(this).trigger( 'blur' );
877
878 wp.media.editor.open( editor, options );
879 tp.table.set_table_changed();
880 } );
881 }
882 },
883 span: {
884 prompt_shown: false,
885 add: function( span ) {
886 var span_add_msg = ( '#rowspan#' === span ) ? tablepress_strings.rowspan_add : tablepress_strings.colspan_add;
887
888 // init object, due to string keys
889 if ( false === tp.content.span.prompt_shown ) {
890 tp.content.span.prompt_shown = {};
891 tp.content.span.prompt_shown['#rowspan#'] = tp.content.span.prompt_shown['#colspan#'] = false;
892 }
893
894 // Automatically deactivate DataTables, if cells are combined
895 if ( $id( 'option-use-datatables' ).prop( 'checked' ) ) {
896 if ( confirm( tablepress_strings.span_add_datatables_warning ) ) {
897 $id( 'option-use-datatables' ).prop( 'checked', false ).trigger( 'change' );
898 } else {
899 return;
900 }
901 }
902
903 if ( ! tp.content.span.prompt_shown[ span ] ) {
904 if ( ! confirm( span_add_msg ) ) {
905 return;
906 }
907 }
908
909 tp.content.span.prompt_shown[ span ] = true;
910 $id( 'edit-form-body' ).one( 'click', 'textarea', function() {
911 var $textarea = $(this),
912 col_idx = $textarea.parent().index(),
913 row_idx = $textarea.closest( 'tr' ).index();
914 if ( '#rowspan#' === span ) {
915 if ( 0 === row_idx ) {
916 alert( tablepress_strings.no_rowspan_first_row );
917 return;
918 }
919 if ( tp.table.head && 1 === row_idx ) {
920 alert( tablepress_strings.no_rowspan_table_head );
921 return;
922 }
923 if ( tp.table.foot && ( tp.table.rows - 1 ) === row_idx ) {
924 alert( tablepress_strings.no_rowspan_table_foot );
925 return;
926 }
927 } else if ( ( '#colspan#' === span ) && ( tp.table.no_data_columns_pre === col_idx ) ) {
928 alert( tablepress_strings.no_colspan_first_col );
929 return;
930 }
931 $textarea.val( span );
932 tp.table.set_table_changed();
933 } );
934 }
935 }
936 };
937
938 tp.check = {
939 table_id: function( event ) {
940 if ( ( 37 === event.which ) || ( 39 === event.which ) ) {
941 return;
942 }
943 var $input = $(this);
944 $input.val( $input.val().replace( /[^0-9a-zA-Z-_]/g, '' ) );
945 },
946 changes_saved: function() {
947 if ( tp.made_changes ) {
948 return tablepress_strings.unsaved_changes_unload;
949 }
950 }
951 };
952
953 tp.reindex = function() {
954 var $row,
955 $rows = $id( 'edit-form-body' ).children(),
956 $cell, known_references = {};
957
958 tp.table.rows = $rows.length;
959 if ( tp.table.rows > 0 ) {
960 tp.table.columns = $rows.first().children().length - tp.table.no_data_columns_pre - tp.table.no_data_columns_post;
961 } else {
962 tp.table.columns = 0;
963 }
964
965 $rows
966 .each( function( row_idx, row ) {
967 $row = $( row );
968 $row.find( 'textarea' )
969 .val( function( column_idx, value ) {
970 // If the cell is not a formula, there's nothing to do here.
971 if ( ( '' === value ) || ( '=' !== value.charAt(0) ) ) {
972 return value;
973 }
974
975 // Support putting formulas in strings, like =Total: {A3+A4}.
976 var expressions = value.match( /{.+?}/g );
977 if ( null === expressions ) {
978 // Fill array so that it has the same structure as if it came from match().
979 expressions = [ value ];
980 }
981
982 // Find the replacement value (with updated cell references) for each expression and replace the old one with it.
983 expressions.forEach( function( expression ) {
984 var new_expression = expression.replace( /([A-Z]+[0-9]+)(?::([A-Z]+[0-9]+))?/g, function( full_match, first_cell, second_cell ) {
985 // first_cell must always exist, while second_cell only exists in ranges like A4:B7
986 // we will use full_match as our result variable, so that we don't need an extra one
987
988 if ( ! known_references.hasOwnProperty( first_cell ) ) {
989 $cell = $id( 'cell-' + first_cell );
990 if ( $cell.length ) {
991 known_references[ first_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
992 } else {
993 known_references[ first_cell ] = first_cell;
994 }
995 }
996 full_match = known_references[ first_cell ];
997
998 if ( ( 'undefined' !== typeof second_cell ) && ( '' !== second_cell ) ) { // Chrome and IE pass an undefined variable, while Firefox passes an empty string
999 if ( ! known_references.hasOwnProperty( second_cell ) ) {
1000 $cell = $id( 'cell-' + second_cell );
1001 if ( $cell.length ) {
1002 known_references[ second_cell ] = tp.columns.number_to_letter( $cell.parent().index() - tp.table.no_data_columns_pre + 1 ) + ( $cell.closest( 'tr' ).index() + 1 );
1003 } else {
1004 known_references[ second_cell ] = second_cell;
1005 }
1006 }
1007 full_match += ':' + known_references[ second_cell ];
1008 }
1009
1010 return full_match;
1011 } );
1012 value = value.replace( expression, new_expression );
1013 } );
1014
1015 return value;
1016 } )
1017 .attr( 'name', function( column_idx /*, old_name */ ) {
1018 return 'table[data][' + row_idx + '][' + column_idx + ']';
1019 } );
1020
1021 $row.find( '.move-handle' ).html( row_idx + 1 );
1022 } )
1023 .each( function( row_idx, row ) { // need a second loop here to not break logic in previous loop, that queries textareas by their old ID
1024 $( row ).find( 'textarea' ).attr( 'id', function( column_idx /*, old_id */ ) {
1025 return 'cell-' + tp.columns.number_to_letter( column_idx + 1 ) + ( row_idx + 1 );
1026 } );
1027 });
1028 $id( 'edit-form-head' ).find( '.move-handle' )
1029 .html( function( idx ) { return tp.columns.number_to_letter( idx + 1 ); } );
1030
1031 $id( 'number-rows' ).val( tp.table.rows );
1032 $id( 'number-columns' ).val( tp.table.columns );
1033
1034 tp.table.set_table_changed();
1035 };
1036
1037 tp.save_changes = {
1038 trigger: function( event ) {
1039 // validation checks
1040 if ( $id( 'option-datatables-paginate' ).prop( 'checked' ) && ! ( /^[1-9][0-9]{0,4}$/ ).test( $id( 'option-datatables-paginate_entries' ).val() ) ) {
1041 alert( tablepress_strings.num_pagination_entries_invalid );
1042 $id( 'option-datatables-paginate_entries' ).trigger( 'focus' ).trigger( 'select' );
1043 return;
1044 }
1045 if ( ( /[^A-Za-z0-9- _:]/ ).test( $id( 'option-extra-css-classes' ).val() ) ) {
1046 alert( tablepress_strings.extra_css_classes_invalid );
1047 $id( 'option-extra-css-classes' ).trigger( 'focus' ).trigger( 'select' );
1048 return;
1049 }
1050
1051 if ( event.shiftKey ) {
1052 tp.made_changes = false; // to prevent onunload warning
1053 $id( 'tablepress-page' ).find( 'form' ).trigger( 'submit' );
1054 return;
1055 }
1056
1057 $(this).closest( 'p' ).append( '<span class="animation-saving spinner is-active" title="' + tablepress_strings.saving_changes + '"/>' );
1058 $( '.save-changes-button' ).prop( 'disabled', true );
1059 $( 'body' ).addClass( 'wait' );
1060
1061 $.ajax({
1062 'type': 'POST',
1063 'url': ajaxurl,
1064 'data': tp.table.prepare_ajax_request( 'tablepress_save_table', '#nonce-edit-table' ),
1065 'success': tp.save_changes.ajax_success,
1066 'error': tp.save_changes.ajax_error,
1067 'dataType': 'json'
1068 } );
1069 },
1070 ajax_success: function( data, status /*, jqXHR */ ) {
1071 if ( ( 'undefined' === typeof status ) || ( 'success' !== status ) ) {
1072 tp.save_changes.error( 'AJAX call successful, but unclear status. Try again while holding down the &#8220;Shift&#8221; key.' );
1073 } else if ( ( 'undefined' === typeof data ) || ( null === data ) || ( '-1' === data ) || ( 'undefined' === typeof data.success ) ) {
1074 tp.save_changes.error( 'AJAX call successful, but unclear data. Try again while holding down the &#8220;Shift&#8221; key.' );
1075 } else if ( true !== data.success ) {
1076 var debug_html = '';
1077 // Print debug information, if we are in debug mode
1078 if ( ( 'undefined' !== typeof data.error_details ) && ( tablepress_options.print_debug_output ) ) {
1079 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
1080 }
1081 tp.save_changes.error( 'AJAX call successful, internal saving process failed. Try again while holding down the &#8220;Shift&#8221; key.' + debug_html );
1082 } else {
1083 tp.save_changes.success( data );
1084 }
1085 },
1086 ajax_error: function( jqXHR, status, error_thrown ) {
1087 tp.save_changes.error( 'AJAX call failed: ' + status + ' - ' + error_thrown + '. Try again while holding down the &#8220;Shift&#8221; key.' );
1088 },
1089 success: function( data ) {
1090 // saving was successful, so the original ID has changed to the (maybe) new ID -> we need to adjust all occurrences
1091 if ( tp.table.id !== data.table_id ) {
1092 // update URL (for HTML5 browsers only), but only if ID really changed, to not get dummy entries in the browser history
1093 if ( ( 'pushState' in window.history ) && null !== window.history.pushState ) {
1094 window.history.pushState( '', '', window.location.href.replace( /table_id=[0-9a-zA-Z-_]+/gi, 'table_id=' + data.table_id ) );
1095 }
1096 }
1097 // update CSS class for data field form
1098 $id( 'edit-form' ).removeClass( 'tablepress-edit-screen-id-' + tp.table.id ).addClass( 'tablepress-edit-screen-id-' + data.table_id );
1099 // update table ID in input fields (type text and hidden)
1100 tp.table.id = tp.table.new_id = data.table_id;
1101 $id( 'table-id' ).val( tp.table.id );
1102 $id( 'table-new-id' ).val( tp.table.new_id );
1103 // update the Shortcode text field
1104 $( '.table-shortcode' ).val( '[' + tablepress_options.shortcode + ' id=' + tp.table.new_id + ' /]' );
1105 // update the nonces
1106 $id( 'nonce-edit-table' ).val( data.new_edit_nonce );
1107 $id( 'nonce-preview-table' ).val( data.new_preview_nonce );
1108 // update URLs in Preview links
1109 var $show_preview_buttons = $( '.show-preview-button' );
1110 if ( $show_preview_buttons.length ) { // check necessary, because Preview button might not be visible
1111 $show_preview_buttons.attr( 'href',
1112 $show_preview_buttons.first().attr( 'href' )
1113 .replace( /item=[a-zA-Z0-9_-]+/g, 'item=' + data.table_id )
1114 .replace( /&_wpnonce=[a-z0-9]+/ig, '&_wpnonce=' + data.new_preview_nonce )
1115 );
1116 }
1117 // update last modified date and user nickname
1118 $id( 'last-modified' ).text( data.last_modified );
1119 $id( 'last-editor' ).text( data.last_editor );
1120 tp.table.unset_table_changed();
1121 tp.save_changes.after_saving_dialog( 'success', tablepress_strings[ data.message ] );
1122 },
1123 error: function( message ) {
1124 tp.save_changes.after_saving_dialog( 'error', message );
1125 },
1126 after_saving_dialog: function( type, message ) {
1127 if ( 'undefined' === typeof message ) {
1128 message = '';
1129 } else {
1130 message = ': ' + message;
1131 }
1132 var delay,
1133 div_class = 'save-changes-' + type;
1134 if ( 'success' === type ) {
1135 div_class += ' notice notice-success';
1136 delay = 3000;
1137 } else {
1138 div_class += ' notice notice-error';
1139 delay = 6000;
1140 }
1141 $( '.animation-saving' ).closest( 'p' )
1142 .after( '<div class="ajax-alert ' + div_class + '"><p>' + tablepress_strings['save_changes_' + type] + message + '</p></div>' );
1143 $( '.animation-saving' ).remove();
1144 $( '.save-changes-' + type ).delay( delay ).fadeOut( 2000, function() { $(this).remove(); } );
1145 $( '.save-changes-button' ).prop( 'disabled', false );
1146 $( 'body' ).removeClass( 'wait' );
1147 }
1148 };
1149
1150 tp.init = function() {
1151 var callbacks = {
1152 'click': {
1153 '#rows-insert': tp.rows.insert,
1154 '#columns-insert': tp.columns.insert,
1155 '#rows-duplicate': tp.rows.duplicate,
1156 '#columns-duplicate': tp.columns.duplicate,
1157 '#rows-remove': tp.rows.remove,
1158 '#columns-remove': tp.columns.remove,
1159 '#rows-hide': tp.rows.hide,
1160 '#columns-hide': tp.columns.hide,
1161 '#rows-unhide': tp.rows.unhide,
1162 '#columns-unhide': tp.columns.unhide,
1163 '#rows-append': tp.rows.append,
1164 '#columns-append': tp.columns.append,
1165 '#link-add': tp.content.link.add,
1166 '#image-add': tp.content.image.add,
1167 '#span-add-rowspan': function() { tp.content.span.add( '#rowspan#' ); },
1168 '#span-add-colspan': function() { tp.content.span.add( '#colspan#' ); },
1169 '.show-preview-button': tp.table.preview.trigger,
1170 '.save-changes-button': tp.save_changes.trigger,
1171 '.show-help-box': function() {
1172 var helpbox_id = $(this).data( 'help-box' );
1173 $( helpbox_id ).wpdialog( {
1174 title: $(this).attr( 'title' ),
1175 height: 470,
1176 width: 320,
1177 modal: true,
1178 dialogClass: 'wp-dialog',
1179 closeOnEscape: true
1180 } );
1181 }
1182 },
1183 'keyup': {
1184 '#table-new-id': tp.check.table_id
1185 },
1186 'change': {
1187 '#option-table-head': tp.table.change_table_head,
1188 '#option-table-foot': tp.table.change_table_foot,
1189 '#option-use-datatables': tp.table.change_datatables,
1190 '#option-datatables-paginate': tp.table.change_datatables_pagination
1191 },
1192 'blur': {
1193 '#table-new-id': tp.table.change_id // onchange would not recognize changed values from tp.check.table_id
1194 }
1195 },
1196 $table = $id( 'edit-form-body' );
1197
1198 $.each( callbacks, function( event, event_callbacks ) {
1199 $.each( event_callbacks, function( selector, callback ) {
1200 $( selector ).on( event, callback );
1201 } );
1202 } );
1203
1204 $( window ).on( 'beforeunload', tp.check.changes_saved );
1205
1206 // do this before the next lines, to not trigger set_table_changed()
1207 $id( 'option-table-head' ).trigger( 'change' ); // init changed/disabled states of DataTables JS features checkboxes
1208 $id( 'option-print-name' ).on( 'change', tp.table.change_print_name_description ).trigger( 'change' ); // init dropdowns for name and description position
1209 $id( 'option-print-description' ).on( 'change', tp.table.change_print_name_description ).trigger( 'change' );
1210
1211 // just once is enough, will be reset after saving
1212 $table.one( 'change', 'textarea', tp.table.set_table_changed );
1213 $( '#tablepress_edit-table-information, #tablepress_edit-table-options, #tablepress_edit-datatables-features' ).one( 'change', 'input, textarea, select', tp.table.set_table_changed );
1214
1215 if ( tablepress_options.cells_advanced_editor ) {
1216 $table.on( 'click', 'textarea', tp.cells.advanced_editor.keyopen );
1217 $id( 'advanced-editor-open' ).on( 'click', tp.cells.advanced_editor.buttonopen );
1218 $id( 'advanced-editor-confirm' ).on( 'click', tp.cells.advanced_editor.save );
1219 $id( 'advanced-editor-cancel' ).on( 'click', tp.cells.advanced_editor.close );
1220 $id( 'advanced-editor' ).wpdialog( {
1221 autoOpen: false,
1222 title: $id( 'advanced-editor-open' ).val(),
1223 width: 600,
1224 modal: true,
1225 dialogClass: 'wp-dialog',
1226 resizable: false,
1227 closeOnEscape: true
1228 } );
1229 // 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"
1230 $id( 'wp-link' ).on( 'focus', 'input', function( event ) {
1231 event.stopPropagation();
1232 } );
1233 } else {
1234 $id( 'advanced-editor-open' ).hide();
1235 }
1236
1237 // Fix issue with input fields not being usable (they are immediately losing focus without this) in the sidebar of the new Media Manager
1238 $( 'body' ).on( 'focus', '.media-modal .media-frame-content input, .media-modal .media-frame-content textarea', function( event ) {
1239 event.stopPropagation();
1240 } );
1241
1242 if ( tablepress_options.cells_auto_grow ) {
1243 $table.on( 'focus', 'textarea', tp.cells.autogrow );
1244 }
1245
1246 $id( 'edit-form-body' ).on( 'click', 'input:checkbox', { parent: '#edit-form-body' }, tp.cells.checkboxes.multi_select );
1247 $id( 'edit-form-foot' ).on( 'click', 'input:checkbox', { parent: '#edit-form-foot' }, tp.cells.checkboxes.multi_select );
1248
1249 $id( 'edit-form-head' ).on( 'click', '.sort-control', tp.rows.sort );
1250
1251 // on form submit: Enable disabled fields, so that they are transmitted in the POST request
1252 $id( 'tablepress-page' ).find( 'form' ).on( 'submit', function() {
1253 $(this).find( '.tablepress-postbox-table' ).find( 'input, select' ).prop( 'disabled', false );
1254 } );
1255
1256 $table.sortable( {
1257 axis: 'y',
1258 containment: $id( 'edit-form' ), // to get better behavior when dragging before/after the first/last row
1259 forceHelperSize: true, // necessary?
1260 handle: '.move-handle',
1261 start: tp.rows.move.start,
1262 change: tp.rows.move.change,
1263 stop: tp.rows.move.stop,
1264 update: tp.reindex
1265 } ); // disableSelection() prohibits selection of text in textareas via keyboard
1266
1267 $id( 'edit-form-head' ).sortable( {
1268 axis: 'x',
1269 items: '.head',
1270 containment: 'parent',
1271 forceHelperSize: true, // necessary?
1272 helper: 'clone',
1273 handle: '.move-handle',
1274 start: tp.columns.move.start,
1275 stop: tp.columns.move.stop,
1276 change: tp.columns.move.change,
1277 sort: tp.columns.move.sort
1278 } ).disableSelection();
1279 };
1280
1281 // Run TablePress initialization.
1282 tp.init();
1283
1284 } );
1285