jquery-tablesorter 1.17.4 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- /*! TableSorter (FORK) v2.22.5 *//*
1
+ /*! TableSorter (FORK) v2.23.0 *//*
2
2
  * Client-side table sorting with ease!
3
3
  * @requires jQuery v1.2.6+
4
4
  *
@@ -26,7 +26,7 @@
26
26
 
27
27
  var ts = this;
28
28
 
29
- ts.version = '2.22.5';
29
+ ts.version = '2.23.0';
30
30
 
31
31
  ts.parsers = [];
32
32
  ts.widgets = [];
@@ -155,13 +155,13 @@
155
155
  ts.instanceMethods = {};
156
156
 
157
157
  // $.isEmptyObject from jQuery v1.4
158
- function isEmptyObject(obj) {
158
+ ts.isEmptyObject = function( obj ) {
159
159
  /*jshint forin: false */
160
- for (var name in obj) {
160
+ for ( var name in obj ) {
161
161
  return false;
162
162
  }
163
163
  return true;
164
- }
164
+ };
165
165
 
166
166
  ts.getElementText = function(c, node, cellIndex) {
167
167
  if (!node) { return ''; }
@@ -304,7 +304,7 @@
304
304
  j += (list.parsers.length) ? len : 1;
305
305
  }
306
306
  if ( c.debug ) {
307
- if ( !isEmptyObject( debug ) ) {
307
+ if ( !ts.isEmptyObject( debug ) ) {
308
308
  console[ console.table ? 'table' : 'log' ]( debug );
309
309
  } else {
310
310
  console.warn( ' No parsers detected!' );
@@ -317,7 +317,7 @@
317
317
  }
318
318
 
319
319
  /* utils */
320
- function buildCache(table, $tbodies) {
320
+ function buildCache(table, callback, $tbodies) {
321
321
  var cc, t, v, i, j, k, $tb, $row, cols, cacheTime,
322
322
  totalRows, rowData, prevRowData, colMax,
323
323
  c = table.config,
@@ -410,54 +410,8 @@
410
410
  if ( c.debug ) {
411
411
  console.log( 'Building cache for ' + totalRows + ' rows' + ts.benchmark( cacheTime ) );
412
412
  }
413
- }
414
-
415
- // init flag (true) used by pager plugin to prevent widget application
416
- function appendToTable(table, init) {
417
- var c = table.config,
418
- wo = c.widgetOptions,
419
- $tbodies = c.$tbodies,
420
- rows = [],
421
- cc = c.cache,
422
- n, totalRows, $bk, $tb,
423
- i, k, appendTime;
424
- // empty table - fixes #206/#346
425
- if (isEmptyObject(cc)) {
426
- // run pager appender in case the table was just emptied
427
- return c.appender ? c.appender(table, rows) :
428
- table.isUpdating ? c.$table.trigger('updateComplete', table) : ''; // Fixes #532
429
- }
430
- if (c.debug) {
431
- appendTime = new Date();
432
- }
433
- for (k = 0; k < $tbodies.length; k++) {
434
- $bk = $tbodies.eq(k);
435
- if ($bk.length) {
436
- // get tbody
437
- $tb = ts.processTbody(table, $bk, true);
438
- n = cc[k].normalized;
439
- totalRows = n.length;
440
- for (i = 0; i < totalRows; i++) {
441
- rows.push(n[i][c.columns].$row);
442
- // removeRows used by the pager plugin; don't render if using ajax - fixes #411
443
- if (!c.appender || (c.pager && (!c.pager.removeRows || !wo.pager_removeRows) && !c.pager.ajax)) {
444
- $tb.append(n[i][c.columns].$row);
445
- }
446
- }
447
- // restore tbody
448
- ts.processTbody(table, $tb, false);
449
- }
450
- }
451
- if (c.appender) {
452
- c.appender(table, rows);
453
- }
454
- if (c.debug) {
455
- console.log( 'Rebuilt table' + ts.benchmark(appendTime) );
456
- }
457
- // apply table widgets; but not before ajax completes
458
- if (!init && !c.appender) { ts.applyWidget(table); }
459
- if (table.isUpdating) {
460
- c.$table.trigger('updateComplete', table);
413
+ if ( $.isFunction( callback ) ) {
414
+ callback( table );
461
415
  }
462
416
  }
463
417
 
@@ -466,9 +420,8 @@
466
420
  return (/^d/i.test(v) || v === 1);
467
421
  }
468
422
 
469
- function buildHeaders(table) {
470
- var ch, $t, h, i, t, lock, time, indx,
471
- c = table.config;
423
+ function buildHeaders( c ) {
424
+ var ch, $t, h, i, t, lock, time, indx;
472
425
  c.headerList = [];
473
426
  c.headerContent = [];
474
427
  if (c.debug) {
@@ -479,12 +432,12 @@
479
432
  // add icon if cssIcon option exists
480
433
  i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : '';
481
434
  // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683
482
- c.$headers = $( $.map( $(table).find(c.selectorHeaders), function(elem, index) {
435
+ c.$headers = $( $.map( c.$table.find(c.selectorHeaders), function(elem, index) {
483
436
  $t = $(elem);
484
437
  // ignore cell (don't add it to c.$headers) if row has ignoreRow class
485
438
  if ($t.parent().hasClass(c.cssIgnoreRow)) { return; }
486
439
  // make sure to get header cell & not column indexed cell
487
- ch = ts.getColumnData( table, c.headers, index, true );
440
+ ch = ts.getColumnData( c.table, c.headers, index, true );
488
441
  // save original header content
489
442
  c.headerContent[index] = $t.html();
490
443
  // if headerTemplate is empty, don't reformat the header cell
@@ -526,12 +479,12 @@
526
479
  // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6
527
480
  c.$headerIndexed[indx] = $t.not('.sorter-false').length ? $t.not('.sorter-false').filter(':last') : $t.filter(':last');
528
481
  }
529
- $(table).find(c.selectorHeaders).attr({
482
+ c.$table.find(c.selectorHeaders).attr({
530
483
  scope: 'col',
531
484
  role : 'columnheader'
532
485
  });
533
486
  // enable/disable sorting
534
- updateHeader(table);
487
+ updateHeader(c.table);
535
488
  if (c.debug) {
536
489
  console.log( 'Built headers:' + ts.benchmark( time ) );
537
490
  console.log( c.$headers );
@@ -788,7 +741,7 @@
788
741
  // set css for headers
789
742
  setHeadersCss(table);
790
743
  multisort(table);
791
- appendToTable(table);
744
+ ts.appendCache( c );
792
745
  $table.trigger('sortEnd', table);
793
746
  }, 1);
794
747
  }
@@ -803,7 +756,7 @@
803
756
  sortList = c.sortList,
804
757
  l = sortList.length,
805
758
  bl = c.$tbodies.length;
806
- if (c.serverSideSorting || isEmptyObject(c.cache)) { // empty table - fixes #206/#346
759
+ if (c.serverSideSorting || ts.isEmptyObject(c.cache)) { // empty table - fixes #206/#346
807
760
  return;
808
761
  }
809
762
  if (c.debug) { sortTime = new Date(); }
@@ -894,188 +847,84 @@
894
847
  }
895
848
  }
896
849
 
897
- function bindMethods(table){
850
+ function bindMethods( table ){
898
851
  var c = table.config,
899
852
  $table = c.$table,
900
- events = ('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache ' +
901
- 'updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave ').split(' ')
902
- .join(c.namespace + ' ');
853
+ events = ( 'sortReset update updateRows updateAll updateHeaders addRows updateCell updateComplete ' +
854
+ 'sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup ' +
855
+ 'mouseleave ' ).split( ' ' )
856
+ .join( c.namespace + ' ' );
903
857
  // apply easy methods that trigger bound events
904
858
  $table
905
- .unbind( events.replace(/\s+/g, ' ') )
906
- .bind('sortReset' + c.namespace, function(e, callback){
859
+ .unbind( events.replace( /\s+/g, ' ' ) )
860
+ .bind( 'sortReset' + c.namespace, function( e, callback ) {
907
861
  e.stopPropagation();
908
- c.sortList = [];
909
- setHeadersCss(table);
910
- multisort(table);
911
- appendToTable(table);
912
- if ($.isFunction(callback)) {
913
- callback(table);
914
- }
862
+ // using this.config to ensure functions are getting a non-cached version of the config
863
+ ts.sortReset( this.config, callback );
915
864
  })
916
- .bind('updateAll' + c.namespace, function(e, resort, callback){
865
+ .bind( 'updateAll' + c.namespace, function( e, resort, callback ) {
917
866
  e.stopPropagation();
918
- table.isUpdating = true;
919
- ts.refreshWidgets(table, true, true);
920
- buildHeaders(table);
921
- ts.bindEvents(table, c.$headers, true);
922
- bindMethods(table);
923
- commonUpdate(table, resort, callback);
867
+ ts.updateAll( this.config, resort, callback );
924
868
  })
925
- .bind('update' + c.namespace + ' updateRows' + c.namespace, function(e, resort, callback) {
869
+ .bind( 'update' + c.namespace + ' updateRows' + c.namespace, function( e, resort, callback ) {
926
870
  e.stopPropagation();
927
- table.isUpdating = true;
928
- // update sorting (if enabled/disabled)
929
- updateHeader(table);
930
- commonUpdate(table, resort, callback);
871
+ ts.update( this.config, resort, callback );
931
872
  })
932
- .bind('updateCell' + c.namespace, function(e, cell, resort, callback) {
873
+ .bind( 'updateHeaders' + c.namespace, function( e, callback ) {
933
874
  e.stopPropagation();
934
- table.isUpdating = true;
935
- $table.find(c.selectorRemove).remove();
936
- // get position from the dom
937
- var t, row, icell, cache,
938
- $tb = c.$tbodies,
939
- $cell = $(cell),
940
- // update cache - format: function(s, table, cell, cellIndex)
941
- // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr');
942
- tbdy = $tb.index( $.fn.closest ? $cell.closest('tbody') : $cell.parents('tbody').filter(':first') ),
943
- tbcache = c.cache[ tbdy ],
944
- $row = $.fn.closest ? $cell.closest('tr') : $cell.parents('tr').filter(':first');
945
- cell = $cell[0]; // in case cell is a jQuery object
946
- // tbody may not exist if update is initialized while tbody is removed for processing
947
- if ($tb.length && tbdy >= 0) {
948
- row = $tb.eq( tbdy ).find( 'tr' ).index( $row );
949
- cache = tbcache.normalized[ row ];
950
- icell = $cell.index();
951
- t = ts.getParsedText( c, cell, icell );
952
- cache[ icell ] = t;
953
- cache[ c.columns ].$row = $row;
954
- if ( (c.parsers[icell].type || '').toLowerCase() === 'numeric' ) {
955
- // update column max value (ignore sign)
956
- tbcache.colMax[icell] = Math.max(Math.abs(t) || 0, tbcache.colMax[icell] || 0);
957
- }
958
- t = resort !== 'undefined' ? resort : c.resort;
959
- if (t !== false) {
960
- // widgets will be reapplied
961
- checkResort(c, t, callback);
962
- } else {
963
- // don't reapply widgets is resort is false, just in case it causes
964
- // problems with element focus
965
- if ($.isFunction(callback)) {
966
- callback(table);
967
- }
968
- c.$table.trigger('updateComplete', c.table);
969
- }
970
- }
875
+ ts.updateHeaders( this.config, callback );
971
876
  })
972
- .bind('addRows' + c.namespace, function(e, $row, resort, callback) {
877
+ .bind( 'updateCell' + c.namespace, function(e, cell, resort, callback ) {
973
878
  e.stopPropagation();
974
- table.isUpdating = true;
975
- if (isEmptyObject(c.cache)) {
976
- // empty table, do an update instead - fixes #450
977
- updateHeader(table);
978
- commonUpdate(table, resort, callback);
979
- } else {
980
- $row = $($row).attr('role', 'row'); // make sure we're using a jQuery object
981
- var i, j, l, rowData, cells,
982
- rows = $row.filter('tr').length,
983
- tbdy = c.$tbodies.index( $row.parents('tbody').filter(':first') );
984
- // fixes adding rows to an empty table - see issue #179
985
- if (!(c.parsers && c.parsers.length)) {
986
- buildParserCache(c);
987
- }
988
- // add each row
989
- for (i = 0; i < rows; i++) {
990
- l = $row[i].cells.length;
991
- cells = [];
992
- rowData = {
993
- child: [],
994
- $row : $row.eq(i),
995
- order: c.cache[tbdy].normalized.length
996
- };
997
- // add each cell
998
- for (j = 0; j < l; j++) {
999
- cells[j] = ts.getParsedText( c, $row[i].cells[j], j );
1000
- if ((c.parsers[j].type || '').toLowerCase() === 'numeric') {
1001
- // update column max value (ignore sign)
1002
- c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0);
1003
- }
1004
- }
1005
- // add the row data to the end
1006
- cells.push(rowData);
1007
- // update cache
1008
- c.cache[tbdy].normalized.push(cells);
1009
- }
1010
- // resort using current settings
1011
- checkResort(c, resort, callback);
1012
- }
879
+ ts.updateCell( this.config, cell, resort, callback );
1013
880
  })
1014
- .bind('updateComplete' + c.namespace, function(){
881
+ .bind( 'addRows' + c.namespace, function(e, $row, resort, callback) {
882
+ e.stopPropagation();
883
+ ts.addRows( this.config, $row, resort, callback );
884
+ })
885
+ .bind( 'updateComplete' + c.namespace, function() {
1015
886
  table.isUpdating = false;
1016
887
  })
1017
- .bind('sorton' + c.namespace, function(e, list, callback, init) {
1018
- var c = table.config;
888
+ .bind( 'sorton' + c.namespace, function( e, list, callback, init ) {
1019
889
  e.stopPropagation();
1020
- $table.trigger('sortStart', this);
1021
- // update header count index
1022
- updateHeaderSortCount(table, list);
1023
- // set css for headers
1024
- setHeadersCss(table);
1025
- // fixes #346
1026
- if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); }
1027
- $table.trigger('sortBegin', this);
1028
- // sort the table and append it to the dom
1029
- multisort(table);
1030
- appendToTable(table, init);
1031
- $table.trigger('sortEnd', this);
1032
- ts.applyWidget(table);
1033
- if ($.isFunction(callback)) {
1034
- callback(table);
1035
- }
890
+ ts.sortOn( this.config, list, callback, init );
1036
891
  })
1037
- .bind('appendCache' + c.namespace, function(e, callback, init) {
892
+ .bind( 'appendCache' + c.namespace, function( e, callback, init ) {
1038
893
  e.stopPropagation();
1039
- appendToTable(table, init);
1040
- if ($.isFunction(callback)) {
1041
- callback(table);
894
+ ts.appendCache( this.config, init );
895
+ if ( $.isFunction( callback ) ) {
896
+ callback( table );
1042
897
  }
1043
898
  })
1044
899
  // $tbodies variable is used by the tbody sorting widget
1045
- .bind('updateCache' + c.namespace, function(e, callback, $tbodies){
1046
- // rebuild parsers
1047
- if (!(c.parsers && c.parsers.length)) {
1048
- buildParserCache(c, $tbodies);
1049
- }
1050
- // rebuild the cache map
1051
- buildCache(table, $tbodies);
1052
- if ($.isFunction(callback)) {
1053
- callback(table);
1054
- }
900
+ .bind( 'updateCache' + c.namespace, function( e, callback, $tbodies ){
901
+ e.stopPropagation();
902
+ ts.updateCache( this.config, callback, $tbodies );
1055
903
  })
1056
- .bind('applyWidgetId' + c.namespace, function(e, id) {
904
+ .bind( 'applyWidgetId' + c.namespace, function( e, id ) {
1057
905
  e.stopPropagation();
1058
- ts.getWidgetById(id).format(table, c, c.widgetOptions);
906
+ ts.getWidgetById( id ).format( table, this.config, this.config.widgetOptions );
1059
907
  })
1060
- .bind('applyWidgets' + c.namespace, function(e, init) {
908
+ .bind( 'applyWidgets' + c.namespace, function( e, init ) {
1061
909
  e.stopPropagation();
1062
910
  // apply widgets
1063
- ts.applyWidget(table, init);
911
+ ts.applyWidget( table, init );
1064
912
  })
1065
- .bind('refreshWidgets' + c.namespace, function(e, all, dontapply){
913
+ .bind( 'refreshWidgets' + c.namespace, function( e, all, dontapply ) {
1066
914
  e.stopPropagation();
1067
- ts.refreshWidgets(table, all, dontapply);
915
+ ts.refreshWidgets( table, all, dontapply );
1068
916
  })
1069
- .bind('destroy' + c.namespace, function(e, c, cb){
917
+ .bind( 'destroy' + c.namespace, function( e, removeClasses, callback ) {
1070
918
  e.stopPropagation();
1071
- ts.destroy(table, c, cb);
919
+ ts.destroy( table, removeClasses, callback );
1072
920
  })
1073
- .bind('resetToLoadState' + c.namespace, function(){
921
+ .bind( 'resetToLoadState' + c.namespace, function( e ) {
922
+ e.stopPropagation();
1074
923
  // remove all widgets
1075
- ts.removeWidget(table, true, false);
924
+ ts.removeWidget( table, true, false );
1076
925
  // restore original settings; this clears out current settings, but does not clear
1077
926
  // values saved to storage.
1078
- c = $.extend(true, ts.defaults, c.originalSettings);
927
+ c = $.extend( true, ts.defaults, c.originalSettings );
1079
928
  table.hasInitialized = false;
1080
929
  // setup the entire table again
1081
930
  ts.setup( table, c );
@@ -1171,7 +1020,7 @@
1171
1020
  // change textExtraction via data-attribute
1172
1021
  c.textExtraction = c.$table.attr('data-text-extraction') || c.textExtraction || 'basic';
1173
1022
  // build headers
1174
- buildHeaders(table);
1023
+ buildHeaders( c );
1175
1024
  // fixate columns if the users supplies the fixedWidth option
1176
1025
  // do this after theme has been applied
1177
1026
  ts.fixColumnWidth(table);
@@ -1391,7 +1240,7 @@
1391
1240
  .find(c.selectorSort).add( $headers.filter(c.selectorSort) )
1392
1241
  .unbind(t)
1393
1242
  .bind(t, function(e, external) {
1394
- var cell, temp,
1243
+ var $cell, cell, temp,
1395
1244
  $target = $(e.target),
1396
1245
  // wrap event type in spaces, so the match doesn't trigger on inner words
1397
1246
  type = ' ' + e.type + ' ';
@@ -1423,12 +1272,13 @@
1423
1272
  $target.parents('button').length > 0 ) {
1424
1273
  return !c.cancelSelection;
1425
1274
  }
1426
- if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); }
1275
+ if (c.delayInit && ts.isEmptyObject(c.cache)) { buildCache(table); }
1427
1276
  // jQuery v1.2.6 doesn't have closest()
1428
- cell = $.fn.closest ? $(this).closest('th, td')[0] : /TH|TD/.test(this.nodeName) ? this : $(this).parents('th, td')[0];
1277
+ $cell = $.fn.closest ? $(this).closest('th, td') : /TH|TD/.test(this.nodeName) ? $(this) : $(this).parents('th, td');
1429
1278
  // reference original table headers and find the same cell
1430
- cell = c.$headers[ $headers.index( cell ) ];
1431
- if (!cell.sortDisabled) {
1279
+ // don't use $headers or IE8 throws an error - see #987
1280
+ cell = c.$headers[ $cell.prevAll().length ];
1281
+ if (cell && !cell.sortDisabled) {
1432
1282
  initSort(table, cell, e);
1433
1283
  }
1434
1284
  });
@@ -1444,6 +1294,219 @@
1444
1294
  }
1445
1295
  };
1446
1296
 
1297
+ ts.sortReset = function( c, callback ) {
1298
+ var table = c.table;
1299
+ c.sortList = [];
1300
+ setHeadersCss( table );
1301
+ multisort( table );
1302
+ ts.appendCache( c );
1303
+ if ( $.isFunction( callback ) ) {
1304
+ callback( table );
1305
+ }
1306
+ };
1307
+
1308
+ ts.updateAll = function( c, resort, callback ) {
1309
+ var table = c.table;
1310
+ table.isUpdating = true;
1311
+ ts.refreshWidgets( table, true, true );
1312
+ buildHeaders( c );
1313
+ ts.bindEvents( table, c.$headers, true );
1314
+ bindMethods( table);
1315
+ commonUpdate( table, resort, callback );
1316
+ };
1317
+
1318
+ ts.update = function( c, resort, callback ) {
1319
+ var table = c.table;
1320
+ table.isUpdating = true;
1321
+ // update sorting (if enabled/disabled)
1322
+ updateHeader( table );
1323
+ commonUpdate( table, resort, callback );
1324
+ };
1325
+
1326
+ // simple header update - see #989
1327
+ ts.updateHeaders = function( c, callback ) {
1328
+ c.table.isUpdating = true;
1329
+ buildHeaders( c );
1330
+ ts.bindEvents( c.table, c.$headers, true );
1331
+ resortComplete( c, callback );
1332
+ };
1333
+
1334
+ ts.updateCell = function( c, cell, resort, callback ) {
1335
+ c.table.isUpdating = true;
1336
+ c.$table.find( c.selectorRemove ).remove();
1337
+ // get position from the dom
1338
+ var t, row, icell, cache,
1339
+ table = c.table,
1340
+ $tb = c.$tbodies,
1341
+ $cell = $(cell),
1342
+ // update cache - format: function(s, table, cell, cellIndex)
1343
+ // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr');
1344
+ tbdy = $tb.index( $.fn.closest ? $cell.closest( 'tbody' ) : $cell.parents( 'tbody' ).filter( ':first' ) ),
1345
+ tbcache = c.cache[ tbdy ],
1346
+ $row = $.fn.closest ? $cell.closest( 'tr' ) : $cell.parents( 'tr' ).filter( ':first' );
1347
+ cell = $cell[ 0 ]; // in case cell is a jQuery object
1348
+ // tbody may not exist if update is initialized while tbody is removed for processing
1349
+ if ( $tb.length && tbdy >= 0 ) {
1350
+ row = $tb.eq( tbdy ).find( 'tr' ).index( $row );
1351
+ cache = tbcache.normalized[ row ];
1352
+ icell = $cell.index();
1353
+ t = ts.getParsedText( c, cell, icell );
1354
+ cache[ icell ] = t;
1355
+ cache[ c.columns ].$row = $row;
1356
+ if ( ( c.parsers[ icell ].type || '' ).toLowerCase() === 'numeric' ) {
1357
+ // update column max value (ignore sign)
1358
+ tbcache.colMax[ icell ] = Math.max( Math.abs( t ) || 0, tbcache.colMax[ icell ] || 0 );
1359
+ }
1360
+ t = resort !== 'undefined' ? resort : c.resort;
1361
+ if ( t !== false ) {
1362
+ // widgets will be reapplied
1363
+ checkResort( c, t, callback );
1364
+ } else {
1365
+ // don't reapply widgets is resort is false, just in case it causes
1366
+ // problems with element focus
1367
+ resortComplete( c, callback );
1368
+ }
1369
+ }
1370
+ };
1371
+
1372
+ ts.addRows = function( c, $row, resort, callback ) {
1373
+ var i, j, l, rowData, cells, rows, tbdy,
1374
+ // allow passing a row string if only one non-info tbody exists in the table
1375
+ valid = typeof $row === 'string' && c.$tbodies.length === 1 && /<tr/.test( $row || '' ),
1376
+ table = c.table;
1377
+ if ( valid ) {
1378
+ $row = $( $row );
1379
+ c.$tbodies.append( $row );
1380
+ } else if ( !$row ||
1381
+ // row is a jQuery object?
1382
+ !( $row instanceof jQuery ) ||
1383
+ // row contained in the table?
1384
+ ( $.fn.closest ? $row.closest( 'table' )[ 0 ] : $row.parents( 'table' )[ 0 ] ) !== c.table ) {
1385
+ if ( c.debug ) {
1386
+ console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' +
1387
+ 'been added to the table, or (2) row HTML string to be added to a table with only one tbody' );
1388
+ }
1389
+ return false;
1390
+ }
1391
+ table.isUpdating = true;
1392
+ if ( ts.isEmptyObject( c.cache ) ) {
1393
+ // empty table, do an update instead - fixes #450
1394
+ updateHeader( table );
1395
+ commonUpdate( table, resort, callback );
1396
+ } else {
1397
+ rows = $row.filter( 'tr' ).attr( 'role', 'row' ).length;
1398
+ tbdy = c.$tbodies.index( $row.parents( 'tbody' ).filter( ':first' ) );
1399
+ // fixes adding rows to an empty table - see issue #179
1400
+ if ( !( c.parsers && c.parsers.length ) ) {
1401
+ buildParserCache( c );
1402
+ }
1403
+ // add each row
1404
+ for ( i = 0; i < rows; i++ ) {
1405
+ l = $row[ i ].cells.length;
1406
+ cells = [];
1407
+ rowData = {
1408
+ child: [],
1409
+ $row : $row.eq( i ),
1410
+ order: c.cache[ tbdy ].normalized.length
1411
+ };
1412
+ // add each cell
1413
+ for ( j = 0; j < l; j++ ) {
1414
+ cells[ j ] = ts.getParsedText( c, $row[ i ].cells[ j ], j );
1415
+ if ( ( c.parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) {
1416
+ // update column max value (ignore sign)
1417
+ c.cache[ tbdy ].colMax[ j ] = Math.max( Math.abs( cells[ j ] ) || 0, c.cache[ tbdy ].colMax[ j ] || 0 );
1418
+ }
1419
+ }
1420
+ // add the row data to the end
1421
+ cells.push( rowData );
1422
+ // update cache
1423
+ c.cache[ tbdy ].normalized.push( cells );
1424
+ }
1425
+ // resort using current settings
1426
+ checkResort( c, resort, callback );
1427
+ }
1428
+ };
1429
+
1430
+ ts.updateCache = function( c, callback, $tbodies ) {
1431
+ // rebuild parsers
1432
+ if ( !( c.parsers && c.parsers.length ) ) {
1433
+ buildParserCache( c, $tbodies );
1434
+ }
1435
+ // rebuild the cache map
1436
+ buildCache( c.table, callback, $tbodies );
1437
+ };
1438
+
1439
+ // init flag (true) used by pager plugin to prevent widget application
1440
+ // renamed from appendToTable
1441
+ ts.appendCache = function( c, init ) {
1442
+ var n, totalRows, $bk, $tb, i, k, appendTime,
1443
+ table = c.table,
1444
+ wo = c.widgetOptions,
1445
+ $tbodies = c.$tbodies,
1446
+ rows = [],
1447
+ cc = c.cache;
1448
+ // empty table - fixes #206/#346
1449
+ if ( ts.isEmptyObject( cc ) ) {
1450
+ // run pager appender in case the table was just emptied
1451
+ return c.appender ? c.appender( table, rows ) :
1452
+ table.isUpdating ? c.$table.trigger( 'updateComplete', table ) : ''; // Fixes #532
1453
+ }
1454
+ if ( c.debug ) {
1455
+ appendTime = new Date();
1456
+ }
1457
+ for ( k = 0; k < $tbodies.length; k++ ) {
1458
+ $bk = $tbodies.eq( k );
1459
+ if ( $bk.length ) {
1460
+ // get tbody
1461
+ $tb = ts.processTbody( table, $bk, true );
1462
+ n = cc[ k ].normalized;
1463
+ totalRows = n.length;
1464
+ for ( i = 0; i < totalRows; i++ ) {
1465
+ rows.push( n[ i ][ c.columns ].$row );
1466
+ // removeRows used by the pager plugin; don't render if using ajax - fixes #411
1467
+ if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) {
1468
+ $tb.append( n[ i ][ c.columns ].$row );
1469
+ }
1470
+ }
1471
+ // restore tbody
1472
+ ts.processTbody( table, $tb, false );
1473
+ }
1474
+ }
1475
+ if ( c.appender ) {
1476
+ c.appender( table, rows );
1477
+ }
1478
+ if ( c.debug ) {
1479
+ console.log( 'Rebuilt table' + ts.benchmark( appendTime ) );
1480
+ }
1481
+ // apply table widgets; but not before ajax completes
1482
+ if ( !init && !c.appender ) { ts.applyWidget( table ); }
1483
+ if ( table.isUpdating ) {
1484
+ c.$table.trigger( 'updateComplete', table );
1485
+ }
1486
+ };
1487
+
1488
+ ts.sortOn = function( c, list, callback, init ) {
1489
+ var table = c.table;
1490
+ c.$table.trigger( 'sortStart', table );
1491
+ // update header count index
1492
+ updateHeaderSortCount( table, list );
1493
+ // set css for headers
1494
+ setHeadersCss( table );
1495
+ // fixes #346
1496
+ if ( c.delayInit && ts.isEmptyObject( c.cache ) ) {
1497
+ buildCache( table );
1498
+ }
1499
+ c.$table.trigger( 'sortBegin', table );
1500
+ // sort the table and append it to the dom
1501
+ multisort( table );
1502
+ ts.appendCache( c, init );
1503
+ c.$table.trigger( 'sortEnd', table );
1504
+ ts.applyWidget( table );
1505
+ if ( $.isFunction( callback ) ) {
1506
+ callback( table );
1507
+ }
1508
+ };
1509
+
1447
1510
  // restore headers
1448
1511
  ts.restoreHeaders = function(table){
1449
1512
  var index, $cell,
@@ -1482,8 +1545,9 @@
1482
1545
  // remove widget added rows, just in case
1483
1546
  $h.find('tr').not($r).remove();
1484
1547
  // disable tablesorter
1485
- events = 'sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache ' +
1486
- 'applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd resetToLoadState '.split(' ')
1548
+ events = 'sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton ' +
1549
+ 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress ' +
1550
+ 'sortBegin sortEnd resetToLoadState '.split(' ')
1487
1551
  .join(c.namespace + ' ');
1488
1552
  $t
1489
1553
  .removeData('tablesorter')
@@ -1905,7 +1969,7 @@
1905
1969
  allColumns = column === 'all',
1906
1970
  data = { raw : [], parsed: [], $cell: [] },
1907
1971
  c = table.config;
1908
- if ( isEmptyObject( c ) ) {
1972
+ if ( ts.isEmptyObject( c ) ) {
1909
1973
  if ( c.debug ) {
1910
1974
  console.warn( 'No cache found - aborting getColumnText function!' );
1911
1975
  }
@@ -2057,7 +2121,8 @@
2057
2121
  },
2058
2122
  format: function(s, table) {
2059
2123
  var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table);
2060
- return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s;
2124
+ return s && typeof n === 'number' ? n :
2125
+ s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s;
2061
2126
  },
2062
2127
  type: 'numeric'
2063
2128
  });
@@ -2065,11 +2130,14 @@
2065
2130
  ts.addParser({
2066
2131
  id: 'currency',
2067
2132
  is: function(s) {
2068
- return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[+\-,. ]/g, '')); // £$€¤¥¢
2133
+ s = (s || '').replace(/[+\-,. ]/g, '');
2134
+ // test for £$€¤¥¢
2135
+ return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test(s);
2069
2136
  },
2070
2137
  format: function(s, table) {
2071
2138
  var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table);
2072
- return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s;
2139
+ return s && typeof n === 'number' ? n :
2140
+ s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s;
2073
2141
  },
2074
2142
  type: 'numeric'
2075
2143
  });
@@ -2127,7 +2195,8 @@
2127
2195
  is: function(s) {
2128
2196
  // two digit years are not allowed cross-browser
2129
2197
  // Jan 01, 2013 12:34:56 PM or 01 Jan 2013
2130
- return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s);
2198
+ return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) ||
2199
+ (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s);
2131
2200
  },
2132
2201
  format: function(s, table) {
2133
2202
  var date = s ? new Date( s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s;
@@ -2139,16 +2208,20 @@
2139
2208
  ts.addParser({
2140
2209
  id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd'
2141
2210
  is: function(s) {
2211
+ s = (s || '').replace(/\s+/g, ' ').replace(/[\-.,]/g, '/');
2142
2212
  // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included
2143
- return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'));
2213
+ return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test(s);
2144
2214
  },
2145
2215
  format: function(s, table, cell, cellIndex) {
2146
2216
  if (s) {
2147
2217
  var date, d,
2148
2218
  c = table.config,
2149
2219
  ci = c.$headerIndexed[ cellIndex ],
2150
- format = ci.length && ci[0].dateFormat || ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || c.dateFormat;
2151
- d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); // escaped - because JSHint in Firefox was showing it as an error
2220
+ format = ci.length && ci[0].dateFormat ||
2221
+ ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') ||
2222
+ c.dateFormat;
2223
+ // escaped "-" because JSHint in Firefox was showing it as an error
2224
+ d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/');
2152
2225
  if (format === 'mmddyyyy') {
2153
2226
  d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$1/$2');
2154
2227
  } else if (format === 'ddmmyyyy') {