| 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 “Shift” 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 “Shift” 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 “Shift” 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 “Shift” 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 |
|