jquery-tablesorter 1.17.1 → 1.17.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (21) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/jquery-tablesorter/version.rb +1 -1
  4. data/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +6 -2
  5. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +353 -228
  6. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +41 -37
  7. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +313 -192
  8. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +2 -1
  9. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js +63 -48
  10. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js +1 -1
  11. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +17 -12
  12. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +10 -5
  13. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +114 -71
  14. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +95 -66
  15. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +3 -2
  16. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js +1 -1
  17. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +120 -59
  18. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +510 -242
  19. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js +228 -0
  20. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +76 -59
  21. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eb7ba6c4dbd8076b0e52b6f0abccb9ee298dc366
4
- data.tar.gz: 887a64f685ec1c39e48c7aa7819f7892b793fab4
3
+ metadata.gz: 853a88593e71f448072c8a7917b8f1365d45e98b
4
+ data.tar.gz: c021dba2083ef3ee1efe3b0039ec1de19232f5df
5
5
  SHA512:
6
- metadata.gz: d0ba74694869a7ff4b46a7c3f271ce77d376b2ffc53a0b553878c5fa58a473b9c942eae7f406a3a2526ec963ba18435bf14829e1c18d4e3de913eeec74495599
7
- data.tar.gz: 458ac71eacbf0b9ac0a8bd625acaa533a19d2fd80c1ee72cf90427419d7903b3074b757df37c01ff6978b4cab77108853f16db74bc35abc8a26286a3107f53b4
6
+ metadata.gz: e2863d563ad0304efcdf39676e4b54411da4f1da60929229f09991fa5e0cefb85f3b1e1fe49bc4c10ce02558e059d3946e26cbb001205a0f0209c799aafb1afe
7
+ data.tar.gz: 9f3b893120f50751ac24432d432ea54d74fb0501c084fd931c672fc32b07911739005f63089270d7b3746acc72f9a0f5f9934130597b17cf50c0bd3759ca37f4
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Simple integration of jquery-tablesorter into the asset pipeline.
6
6
 
7
- Current tablesorter version: 2.22.1 (5/17/2015), [documentation]
7
+ Current tablesorter version: 2.22.3 (6/30/2015), [documentation]
8
8
 
9
9
  Any issue associated with the js/css files, please report to [Mottie's fork].
10
10
 
@@ -1,3 +1,3 @@
1
1
  module JqueryTablesorter
2
- VERSION = '1.17.1'
2
+ VERSION = '1.17.2'
3
3
  end
@@ -408,7 +408,7 @@
408
408
  c.totalRows = p.totalRows = result.total;
409
409
  c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total;
410
410
  th = result.headers;
411
- d = result.rows;
411
+ d = result.rows || [];
412
412
  } else {
413
413
  // allow [ total, rows, headers ] or [ rows, total, headers ]
414
414
  t = isNaN(result[0]) && !isNaN(result[1]);
@@ -417,7 +417,8 @@
417
417
  p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count;
418
418
  // can't set filtered rows when returning an array
419
419
  c.totalRows = c.filteredRows = p.filteredRows = p.totalRows;
420
- d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data
420
+ // set row data to empty array if nothing found - see http://stackoverflow.com/q/30875583/145346
421
+ d = p.totalRows === 0 ? [] : result[t ? 0 : 1] || []; // row data
421
422
  th = result[2]; // headers
422
423
  }
423
424
  l = d && d.length;
@@ -1046,6 +1047,9 @@
1046
1047
  updatePageDisplay(table, p, false);
1047
1048
  }
1048
1049
  }
1050
+
1051
+ // make the hasWidget function think that the pager widget is being used
1052
+ c.widgetInit.pager = true;
1049
1053
  });
1050
1054
  };
1051
1055
 
@@ -4,7 +4,7 @@
4
4
  ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██
5
5
  █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀
6
6
  */
7
- /*! tablesorter (FORK) - updated 05-18-2015 (v2.22.1)*/
7
+ /*! tablesorter (FORK) - updated 06-30-2015 (v2.22.2)*/
8
8
  /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */
9
9
  (function(factory) {
10
10
  if (typeof define === 'function' && define.amd) {
@@ -16,7 +16,7 @@
16
16
  }
17
17
  }(function($) {
18
18
 
19
- /*! TableSorter (FORK) v2.22.1 *//*
19
+ /*! TableSorter (FORK) v2.22.3 *//*
20
20
  * Client-side table sorting with ease!
21
21
  * @requires jQuery v1.2.6+
22
22
  *
@@ -44,7 +44,7 @@
44
44
 
45
45
  var ts = this;
46
46
 
47
- ts.version = '2.22.1';
47
+ ts.version = '2.22.3';
48
48
 
49
49
  ts.parsers = [];
50
50
  ts.widgets = [];
@@ -209,10 +209,10 @@
209
209
  if (typeof(t) === 'string') {
210
210
  // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow!
211
211
  // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/
212
- return $.trim(
213
- ( t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) ||
214
- $node.text()
215
- );
212
+ if ( t === 'basic' && typeof ( te = $node.attr(c.textAttribute) ) !== 'undefined' ) {
213
+ return $.trim( te );
214
+ }
215
+ return $.trim( node.textContent || $node.text() );
216
216
  } else {
217
217
  if (typeof(t) === 'function') {
218
218
  return $.trim( t($node[0], c.table, cellIndex) );
@@ -224,9 +224,8 @@
224
224
  return $.trim( $node[0].textContent || $node.text() );
225
225
  };
226
226
 
227
- function detectParserForColumn(table, rows, rowIndex, cellIndex) {
227
+ function detectParserForColumn(c, rows, rowIndex, cellIndex) {
228
228
  var cur, $node,
229
- c = table.config,
230
229
  i = ts.parsers.length,
231
230
  node = false,
232
231
  nodeValue = '',
@@ -237,7 +236,7 @@
237
236
  node = rows[rowIndex].cells[cellIndex];
238
237
  nodeValue = ts.getElementText(c, node, cellIndex);
239
238
  $node = $(node);
240
- if (table.config.debug) {
239
+ if (c.debug) {
241
240
  log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"');
242
241
  }
243
242
  } else {
@@ -247,7 +246,7 @@
247
246
  while (--i >= 0) {
248
247
  cur = ts.parsers[i];
249
248
  // ignore the default text parser because it will always be true
250
- if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, table, node, $node)) {
249
+ if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, c.table, node, $node)) {
251
250
  return cur;
252
251
  }
253
252
  }
@@ -256,7 +255,7 @@
256
255
  }
257
256
 
258
257
  // centralized function to extract/parse cell contents
259
- function getParsedText( c, cell, colIndex, txt ) {
258
+ ts.getParsedText = function( c, cell, colIndex, txt ) {
260
259
  if ( typeof txt === 'undefined' ) {
261
260
  txt = ts.getElementText( c, cell, colIndex );
262
261
  }
@@ -279,16 +278,17 @@
279
278
  }
280
279
  }
281
280
  return val;
282
- }
281
+ };
283
282
 
284
- function buildParserCache(table) {
285
- var c = table.config,
286
- // update table bodies in case we start with an empty table
287
- tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'),
288
- rows, list, l, i, h, ch, np, p, e, time,
283
+ function buildParserCache( c, $tbodies ) {
284
+ var rows, list, l, i, h, ch, np, p, e, time, tb, len,
285
+ table = c.table,
289
286
  j = 0,
290
- parsersDebug = '',
291
- len = tb.length;
287
+ parsersDebug = '';
288
+ // update table bodies in case we start with an empty table
289
+ c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')');
290
+ tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies;
291
+ len = tb.length;
292
292
  if ( len === 0) {
293
293
  return c.debug ? log('Warning: *Empty table!* Not building a parser cache') : '';
294
294
  } else if (c.debug) {
@@ -323,7 +323,7 @@
323
323
  e = false;
324
324
  }
325
325
  if (!p) {
326
- p = detectParserForColumn(table, rows, -1, i);
326
+ p = detectParserForColumn(c, rows, -1, i);
327
327
  }
328
328
  if (c.debug) {
329
329
  parsersDebug += 'column:' + i + '; extractor:' + e.id + '; parser:' + p.id + '; string:' + c.strings[i] + '; empty: ' + c.empties[i] + '\n';
@@ -343,12 +343,14 @@
343
343
  }
344
344
 
345
345
  /* utils */
346
- function buildCache(table) {
347
- var cc, t, v, i, j, k, $row, cols, cacheTime,
346
+ function buildCache(table, $tbodies) {
347
+ var cc, t, v, i, j, k, $tb, $row, cols, cacheTime,
348
348
  totalRows, rowData, prevRowData, colMax,
349
349
  c = table.config,
350
- $tb = c.$tbodies,
351
350
  parsers = c.parsers;
351
+ // update tbody variable
352
+ c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')');
353
+ $tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies,
352
354
  c.cache = {};
353
355
  c.totalRows = 0;
354
356
  // if no parsers found, return - it's an empty table.
@@ -396,7 +398,7 @@
396
398
  prevRowData.child[ t ] = [];
397
399
  // child row content does not account for colspans/rowspans; so indexing may be off
398
400
  for ( j = 0; j < c.columns; j++ ) {
399
- prevRowData.child[ t ][ j ] = getParsedText( c, v[ j ], j );
401
+ prevRowData.child[ t ][ j ] = ts.getParsedText( c, v[ j ], j );
400
402
  }
401
403
  // go to the next for loop
402
404
  continue;
@@ -412,7 +414,7 @@
412
414
  }
413
415
  t = ts.getElementText( c, $row[ 0 ].cells[j], j );
414
416
  rowData.raw.push( t ); // save original row text
415
- v = getParsedText( c, $row[ 0 ].cells[ j ], j, t );
417
+ v = ts.getParsedText( c, $row[ 0 ].cells[ j ], j, t );
416
418
  cols.push( v );
417
419
  if ( ( parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) {
418
420
  // determine column max value (ignore sign)
@@ -565,7 +567,7 @@
565
567
  // remove rows/elements before update
566
568
  c.$table.find(c.selectorRemove).remove();
567
569
  // rebuild parsers
568
- buildParserCache(table);
570
+ buildParserCache(c);
569
571
  // rebuild the cache map
570
572
  buildCache(table);
571
573
  checkResort(c, resort, callback);
@@ -855,7 +857,7 @@
855
857
  num = (c.strings[col]) ? c.string[c.strings[col]] || 0 : 0;
856
858
  }
857
859
  // fall back to built-in numeric sort
858
- // var sort = $.tablesorter['sort' + s](table, a[c], b[c], c, colMax[c], dir);
860
+ // var sort = $.tablesorter['sort' + s]( a[c], b[c], dir, colMax[c], table);
859
861
  sort = c.numberSorter ? c.numberSorter(a[col], b[col], dir, colMax[col], table) :
860
862
  ts[ 'sortNumeric' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], num, colMax[col], col, table);
861
863
  } else {
@@ -968,7 +970,7 @@
968
970
  row = $tb.eq( tbdy ).find( 'tr' ).index( $row );
969
971
  cache = tbcache.normalized[ row ];
970
972
  icell = $cell.index();
971
- t = getParsedText( c, cell, icell );
973
+ t = ts.getParsedText( c, cell, icell );
972
974
  cache[ icell ] = t;
973
975
  cache[ c.columns ].$row = $row;
974
976
  if ( (c.parsers[icell].type || '').toLowerCase() === 'numeric' ) {
@@ -1003,7 +1005,7 @@
1003
1005
  tbdy = c.$tbodies.index( $row.parents('tbody').filter(':first') );
1004
1006
  // fixes adding rows to an empty table - see issue #179
1005
1007
  if (!(c.parsers && c.parsers.length)) {
1006
- buildParserCache(table);
1008
+ buildParserCache(c);
1007
1009
  }
1008
1010
  // add each row
1009
1011
  for (i = 0; i < rows; i++) {
@@ -1016,7 +1018,7 @@
1016
1018
  };
1017
1019
  // add each cell
1018
1020
  for (j = 0; j < l; j++) {
1019
- cells[j] = getParsedText( c, $row[i].cells[j], j );
1021
+ cells[j] = ts.getParsedText( c, $row[i].cells[j], j );
1020
1022
  if ((c.parsers[j].type || '').toLowerCase() === 'numeric') {
1021
1023
  // update column max value (ignore sign)
1022
1024
  c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0);
@@ -1061,13 +1063,14 @@
1061
1063
  callback(table);
1062
1064
  }
1063
1065
  })
1064
- .bind('updateCache' + c.namespace, function(e, callback){
1066
+ // $tbodies variable is used by the tbody sorting widget
1067
+ .bind('updateCache' + c.namespace, function(e, callback, $tbodies){
1065
1068
  // rebuild parsers
1066
1069
  if (!(c.parsers && c.parsers.length)) {
1067
- buildParserCache(table);
1070
+ buildParserCache(c, $tbodies);
1068
1071
  }
1069
1072
  // rebuild the cache map
1070
- buildCache(table);
1073
+ buildCache(table, $tbodies);
1071
1074
  if ($.isFunction(callback)) {
1072
1075
  callback(table);
1073
1076
  }
@@ -1187,7 +1190,7 @@
1187
1190
  // add widget options before parsing (e.g. grouping widget has parser settings)
1188
1191
  ts.applyWidgetOptions(table, c);
1189
1192
  // try to auto detect column type, and store in tables config
1190
- buildParserCache(table);
1193
+ buildParserCache(c);
1191
1194
  // start total row count at zero
1192
1195
  c.totalRows = 0;
1193
1196
  // build the cache for the tbody cells
@@ -1413,10 +1416,11 @@
1413
1416
  }
1414
1417
  // ignore mouseup if mousedown wasn't on the same target
1415
1418
  if ( type.match(' ' + c.pointerUp + ' ') && downTarget !== e.target && external !== true ) { return; }
1416
- // set timer on mousedown
1419
+ // set target on mousedown
1417
1420
  if ( type.match(' ' + c.pointerDown + ' ') ) {
1418
1421
  downTarget = e.target;
1419
- // needed or jQuery v1.3.2 or older throws an "Uncaught TypeError: handler.apply is not a function" error
1422
+ // preventDefault needed or jQuery v1.3.2 and older throws an
1423
+ // "Uncaught TypeError: handler.apply is not a function" error
1420
1424
  temp = $target.jquery.split( '.' );
1421
1425
  if ( temp[0] === '1' && temp[1] < 4 ) { e.preventDefault(); }
1422
1426
  return;
@@ -2155,7 +2159,7 @@
2155
2159
  format: function(table, c, wo) {
2156
2160
  var $tv, $tr, row, even, time, k, i, len,
2157
2161
  child = new RegExp(c.cssChildRow, 'i'),
2158
- b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody' ) );
2162
+ b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody:not(.' + c.cssInfoBlock + ')' ) );
2159
2163
  if (c.debug) {
2160
2164
  time = new Date();
2161
2165
  }
@@ -2650,6 +2654,66 @@ ts.filter = {
2650
2654
  // data.index = column index; table = table element ( DOM )
2651
2655
  // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class )
2652
2656
  types: {
2657
+ or : function( c, data, vars ) {
2658
+ if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) {
2659
+ var indx, filterMatched, txt, query, regex,
2660
+ // duplicate data but split filter
2661
+ data2 = $.extend( {}, data ),
2662
+ index = data.index,
2663
+ parsed = data.parsed[ index ],
2664
+ filter = data.filter.split( ts.filter.regex.orSplit ),
2665
+ iFilter = data.iFilter.split( ts.filter.regex.orSplit ),
2666
+ len = filter.length;
2667
+ for ( indx = 0; indx < len; indx++ ) {
2668
+ data2.nestedFilters = true;
2669
+ data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' );
2670
+ data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
2671
+ query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')';
2672
+ regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' );
2673
+ // filterMatched = data2.filter === '' && indx > 0 ? true
2674
+ // look for an exact match with the 'or' unless the 'filter-match' class is found
2675
+ filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars );
2676
+ if ( filterMatched ) {
2677
+ return filterMatched;
2678
+ }
2679
+ }
2680
+ // may be null from processing types
2681
+ return filterMatched || false;
2682
+ }
2683
+ return null;
2684
+ },
2685
+ // Look for an AND or && operator ( logical and )
2686
+ and : function( c, data, vars ) {
2687
+ if ( ts.filter.regex.andTest.test( data.filter ) ) {
2688
+ var indx, filterMatched, result, txt, query, regex,
2689
+ // duplicate data but split filter
2690
+ data2 = $.extend( {}, data ),
2691
+ index = data.index,
2692
+ parsed = data.parsed[ index ],
2693
+ filter = data.filter.split( ts.filter.regex.andSplit ),
2694
+ iFilter = data.iFilter.split( ts.filter.regex.andSplit ),
2695
+ len = filter.length;
2696
+ for ( indx = 0; indx < len; indx++ ) {
2697
+ data2.nestedFilters = true;
2698
+ data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' );
2699
+ data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
2700
+ query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' )
2701
+ // replace wild cards since /(a*)/i will match anything
2702
+ .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' );
2703
+ regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' );
2704
+ // look for an exact match with the 'and' unless the 'filter-match' class is found
2705
+ result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) );
2706
+ if ( indx === 0 ) {
2707
+ filterMatched = result;
2708
+ } else {
2709
+ filterMatched = filterMatched && result;
2710
+ }
2711
+ }
2712
+ // may be null from processing types
2713
+ return filterMatched || false;
2714
+ }
2715
+ return null;
2716
+ },
2653
2717
  // Look for regex
2654
2718
  regex: function( c, data ) {
2655
2719
  if ( ts.filter.regex.regex.test( data.filter ) ) {
@@ -2737,23 +2801,6 @@ ts.filter = {
2737
2801
  }
2738
2802
  return null;
2739
2803
  },
2740
- // Look for an AND or && operator ( logical and )
2741
- and : function( c, data ) {
2742
- if ( ts.filter.regex.andTest.test( data.filter ) ) {
2743
- var index = data.index,
2744
- parsed = data.parsed[index],
2745
- query = data.iFilter.split( ts.filter.regex.andSplit ),
2746
- result = data.iExact.search( $.trim( ts.filter.parseFilter( c, query[0], index, parsed ) ) ) >= 0,
2747
- indx = query.length - 1;
2748
- while ( result && indx ) {
2749
- result = result &&
2750
- data.iExact.search( $.trim( ts.filter.parseFilter( c, query[indx], index, parsed ) ) ) >= 0;
2751
- indx--;
2752
- }
2753
- return result;
2754
- }
2755
- return null;
2756
- },
2757
2804
  // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu!
2758
2805
  range : function( c, data ) {
2759
2806
  if ( ts.filter.regex.toTest.test( data.iFilter ) ) {
@@ -2790,24 +2837,20 @@ ts.filter = {
2790
2837
  },
2791
2838
  // Look for wild card: ? = single, * = multiple, or | = logical OR
2792
2839
  wild : function( c, data ) {
2793
- if ( /[\?\*\|]/.test( data.iFilter ) || ts.filter.regex.orReplace.test( data.filter ) ) {
2840
+ if ( /[\?\*\|]/.test( data.iFilter ) ) {
2794
2841
  var index = data.index,
2795
2842
  parsed = data.parsed[ index ],
2796
- txt = data.iFilter.replace( ts.filter.regex.orReplace, '|' ),
2797
- query = '' + ( ts.filter.parseFilter( c, txt, index, parsed ) || '' );
2843
+ query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' );
2798
2844
  // look for an exact match with the 'or' unless the 'filter-match' class is found
2799
- if ( !c.$headerIndexed[ index ].hasClass( 'filter-match' ) && /\|/.test( query ) ) {
2800
- // show all results while using filter match. Fixes #727
2801
- if ( query[ query.length - 1 ] === '|' ) {
2802
- query += '*';
2803
- }
2804
- query = data.anyMatch && $.isArray( data.rowArray ) ?
2805
- '(' + query + ')' :
2806
- '^(' + query + ')$';
2845
+ if ( !/\?\*/.test( query ) && data.nestedFilters ) {
2846
+ query = data.isMatch ? query : '^(' + query + ')$';
2807
2847
  }
2808
2848
  // parsing the filter may not work properly when using wildcards =/
2809
- return new RegExp( query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ) )
2810
- .test( data.iExact );
2849
+ return new RegExp(
2850
+ query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ),
2851
+ c.widgetOptions.filter_ignoreCase ? 'i' : ''
2852
+ )
2853
+ .test( data.exact );
2811
2854
  }
2812
2855
  return null;
2813
2856
  },
@@ -2861,7 +2904,7 @@ ts.filter = {
2861
2904
  toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi' ),
2862
2905
  andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ),
2863
2906
  andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ),
2864
- orReplace : new RegExp( '\\s+(' + ts.language.or + ')\\s+', 'gi' ),
2907
+ orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ),
2865
2908
  iQuery : new RegExp( val, 'i' ),
2866
2909
  igQuery : new RegExp( val, 'ig' )
2867
2910
  });
@@ -3081,7 +3124,6 @@ ts.filter = {
3081
3124
  }
3082
3125
  }
3083
3126
  },
3084
-
3085
3127
  setDefaults: function( table, c, wo ) {
3086
3128
  var isArray, saved, indx, col, $filters,
3087
3129
  // get current ( default ) filters
@@ -3308,14 +3350,13 @@ ts.filter = {
3308
3350
  }
3309
3351
  },
3310
3352
  hideFilters: function( table, c ) {
3311
- var $filterRow, $filterRow2, timer;
3312
- $( table )
3353
+ var timer;
3354
+ c.$table
3313
3355
  .find( '.' + tscss.filterRow )
3314
- .addClass( tscss.filterRowHide )
3315
3356
  .bind( 'mouseenter mouseleave', function( e ) {
3316
3357
  // save event object - http://bugs.jquery.com/ticket/12140
3317
- var event = e;
3318
- $filterRow = $( this );
3358
+ var event = e,
3359
+ $filterRow = $( this );
3319
3360
  clearTimeout( timer );
3320
3361
  timer = setTimeout( function() {
3321
3362
  if ( /enter|over/.test( event.type ) ) {
@@ -3333,13 +3374,14 @@ ts.filter = {
3333
3374
  }, 200 );
3334
3375
  })
3335
3376
  .find( 'input, select' ).bind( 'focus blur', function( e ) {
3336
- $filterRow2 = $( this ).closest( 'tr' );
3377
+ var event = e,
3378
+ $row = $( this ).closest( 'tr' );
3337
3379
  clearTimeout( timer );
3338
- var event = e;
3339
3380
  timer = setTimeout( function() {
3381
+ clearTimeout( timer );
3340
3382
  // don't hide row if any filter has a value
3341
3383
  if ( ts.getFilters( c.$table ).join( '' ) === '' ) {
3342
- $filterRow2.toggleClass( tscss.filterRowHide, event.type === 'focus' );
3384
+ $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' );
3343
3385
  }
3344
3386
  }, 200 );
3345
3387
  });
@@ -3373,7 +3415,7 @@ ts.filter = {
3373
3415
  return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' );
3374
3416
  });
3375
3417
  }
3376
- return $();
3418
+ return $input || $();
3377
3419
  },
3378
3420
  multipleColumns: function( c, $input ) {
3379
3421
  // look for multiple columns '1-3,4-6,8' in data-column
@@ -3426,8 +3468,22 @@ ts.filter = {
3426
3468
  }
3427
3469
  return columns;
3428
3470
  },
3471
+ processTypes: function( c, data, vars ) {
3472
+ var ffxn,
3473
+ filterMatched = null,
3474
+ matches = null;
3475
+ for ( ffxn in ts.filter.types ) {
3476
+ if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) {
3477
+ matches = ts.filter.types[ffxn]( c, data, vars );
3478
+ if ( matches !== null ) {
3479
+ filterMatched = matches;
3480
+ }
3481
+ }
3482
+ }
3483
+ return filterMatched;
3484
+ },
3429
3485
  processRow: function( c, data, vars ) {
3430
- var $cell, columnIndex, hasSelect, matches, result, val, filterMatched, excludeMatch,
3486
+ var columnIndex, hasSelect, result, val, filterMatched,
3431
3487
  fxn, ffxn, txt,
3432
3488
  regex = ts.filter.regex,
3433
3489
  wo = c.widgetOptions,
@@ -3438,6 +3494,7 @@ ts.filter = {
3438
3494
  // look for multiple columns '1-3,4-6,8'
3439
3495
  columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch );
3440
3496
  data.anyMatch = true;
3497
+ data.isMatch = true;
3441
3498
  data.rowArray = data.$cells.map( function( i ) {
3442
3499
  if ( $.inArray( i, columnIndex ) > -1 ) {
3443
3500
  if ( data.parsed[ i ] ) {
@@ -3457,16 +3514,10 @@ ts.filter = {
3457
3514
  data.exact = data.rowArray.join( ' ' );
3458
3515
  data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact;
3459
3516
  data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' );
3460
- filterMatched = null;
3461
- matches = null;
3462
- for ( ffxn in ts.filter.types ) {
3463
- if ( $.inArray( ffxn, vars.noAnyMatch ) < 0 && matches === null ) {
3464
- matches = ts.filter.types[ffxn]( c, data );
3465
- if ( matches !== null ) {
3466
- filterMatched = matches;
3467
- }
3468
- }
3469
- }
3517
+
3518
+ vars.excludeMatch = vars.noAnyMatch;
3519
+ filterMatched = ts.filter.processTypes( c, data, vars );
3520
+
3470
3521
  if ( filterMatched !== null ) {
3471
3522
  showRow = filterMatched;
3472
3523
  } else {
@@ -3493,7 +3544,7 @@ ts.filter = {
3493
3544
  data.index = columnIndex;
3494
3545
 
3495
3546
  // filter types to exclude, per column
3496
- excludeMatch = vars.excludeFilter[ columnIndex ];
3547
+ vars.excludeMatch = vars.excludeFilter[ columnIndex ];
3497
3548
 
3498
3549
  // ignore if filter is empty or disabled
3499
3550
  if ( data.filter ) {
@@ -3507,6 +3558,9 @@ ts.filter = {
3507
3558
  }
3508
3559
  data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ?
3509
3560
  data.exact.toLowerCase() : data.exact;
3561
+
3562
+ data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' );
3563
+
3510
3564
  result = showRow; // if showRow is true, show that row
3511
3565
 
3512
3566
  // in case select filter option has a different value vs text 'a - z|A through Z'
@@ -3531,13 +3585,12 @@ ts.filter = {
3531
3585
  // data.filter = case sensitive
3532
3586
  data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter;
3533
3587
  fxn = vars.functions[ columnIndex ];
3534
- $cell = c.$headerIndexed[ columnIndex ];
3535
- hasSelect = $cell.hasClass( 'filter-select' );
3588
+ hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' );
3536
3589
  filterMatched = null;
3537
3590
  if ( fxn || ( hasSelect && val ) ) {
3538
3591
  if ( fxn === true || hasSelect ) {
3539
3592
  // default selector uses exact match unless 'filter-match' class is found
3540
- filterMatched = $cell.hasClass( 'filter-match' ) ?
3593
+ filterMatched = data.isMatch ?
3541
3594
  data.iExact.search( data.iFilter ) >= 0 :
3542
3595
  data.filter === data.exact;
3543
3596
  } else if ( typeof fxn === 'function' ) {
@@ -3554,15 +3607,7 @@ ts.filter = {
3554
3607
  if ( filterMatched === null ) {
3555
3608
  // cycle through the different filters
3556
3609
  // filters return a boolean or null if nothing matches
3557
- matches = null;
3558
- for ( ffxn in ts.filter.types ) {
3559
- if ( $.inArray( ffxn, excludeMatch ) < 0 && matches === null ) {
3560
- matches = ts.filter.types[ ffxn ]( c, data );
3561
- if ( matches !== null ) {
3562
- filterMatched = matches;
3563
- }
3564
- }
3565
- }
3610
+ filterMatched = ts.filter.processTypes( c, data, vars );
3566
3611
  if ( filterMatched !== null ) {
3567
3612
  result = filterMatched;
3568
3613
  // Look for match, and add child row data for matching
@@ -3596,7 +3641,7 @@ ts.filter = {
3596
3641
  anyMatch: false,
3597
3642
  filters: filters,
3598
3643
  // regex filter type cache
3599
- filter_regexCache : [],
3644
+ filter_regexCache : []
3600
3645
  },
3601
3646
  vars = {
3602
3647
  // anyMatch really screws up with these types of filters
@@ -3817,7 +3862,7 @@ ts.filter = {
3817
3862
  },
3818
3863
  getOptionSource: function( table, column, onlyAvail ) {
3819
3864
  table = $( table )[0];
3820
- var cts, indx, len,
3865
+ var cts, txt, indx, len,
3821
3866
  c = table.config,
3822
3867
  wo = c.widgetOptions,
3823
3868
  parsed = [],
@@ -3862,11 +3907,13 @@ ts.filter = {
3862
3907
  len = arry.length;
3863
3908
  // parse select option values
3864
3909
  for ( indx = 0; indx < len; indx++ ) {
3910
+ txt = arry[ indx ];
3865
3911
  // parse array data using set column parser; this DOES NOT pass the original
3866
3912
  // table cell to the parser format function
3867
3913
  parsed.push({
3868
- t : arry[ indx ],
3869
- p : c.parsers && c.parsers[ column ].format( arry[ indx ], table, [], column )
3914
+ t : txt,
3915
+ // check parser length - fixes #934
3916
+ p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt
3870
3917
  });
3871
3918
  }
3872
3919
 
@@ -4056,8 +4103,8 @@ ts.getFilters = function( table, getRaw, setFilters, skipFirst ) {
4056
4103
  $column = ts.filter.getLatestSearch( $column );
4057
4104
  if ( $.isArray( setFilters ) ) {
4058
4105
  // skip first ( latest input ) to maintain cursor position while typing
4059
- if ( skipFirst ) {
4060
- $column.slice( 1 );
4106
+ if ( skipFirst && $column.length > 1 ) {
4107
+ $column = $column.slice( 1 );
4061
4108
  }
4062
4109
  if ( i === c.columns ) {
4063
4110
  // prevent data-column='all' from filling data-column='0,1' ( etc )
@@ -4129,32 +4176,34 @@ $.extend(ts.css, {
4129
4176
  // Add a resize event to table headers
4130
4177
  ts.addHeaderResizeEvent = function(table, disable, settings) {
4131
4178
  table = $(table)[0]; // make sure we're using a dom element
4132
- var headers,
4133
- defaults = {
4179
+ if ( !table.config ) { return; }
4180
+ var defaults = {
4134
4181
  timer : 250
4135
4182
  },
4136
4183
  options = $.extend({}, defaults, settings),
4137
4184
  c = table.config,
4138
4185
  wo = c.widgetOptions,
4139
- checkSizes = function(triggerEvent) {
4186
+ checkSizes = function( triggerEvent ) {
4187
+ var index, headers, $header, sizes, width, height,
4188
+ len = c.$headers.length;
4140
4189
  wo.resize_flag = true;
4141
4190
  headers = [];
4142
- c.$headers.each(function() {
4143
- var $header = $(this),
4144
- sizes = $header.data('savedSizes') || [0,0], // fixes #394
4145
- width = this.offsetWidth,
4146
- height = this.offsetHeight;
4147
- if (width !== sizes[0] || height !== sizes[1]) {
4148
- $header.data('savedSizes', [ width, height ]);
4149
- headers.push(this);
4191
+ for ( index = 0; index < len; index++ ) {
4192
+ $header = c.$headers.eq( index );
4193
+ sizes = $header.data( 'savedSizes' ) || [ 0,0 ]; // fixes #394
4194
+ width = $header[0].offsetWidth;
4195
+ height = $header[0].offsetHeight;
4196
+ if ( width !== sizes[0] || height !== sizes[1] ) {
4197
+ $header.data( 'savedSizes', [ width, height ] );
4198
+ headers.push( $header[0] );
4150
4199
  }
4151
- });
4152
- if (headers.length && triggerEvent !== false) {
4153
- c.$table.trigger('resize', [ headers ]);
4200
+ }
4201
+ if ( headers.length && triggerEvent !== false ) {
4202
+ c.$table.trigger( 'resize', [ headers ] );
4154
4203
  }
4155
4204
  wo.resize_flag = false;
4156
4205
  };
4157
- checkSizes(false);
4206
+ checkSizes( false );
4158
4207
  clearInterval(wo.resize_timer);
4159
4208
  if (disable) {
4160
4209
  wo.resize_flag = false;
@@ -4190,7 +4239,8 @@ ts.addWidget({
4190
4239
  if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) {
4191
4240
  return;
4192
4241
  }
4193
- var $table = c.$table,
4242
+ var index, len, $t,
4243
+ $table = c.$table,
4194
4244
  // add position: relative to attach element, hopefully it won't cause trouble.
4195
4245
  $attach = $(wo.stickyHeaders_attachTo),
4196
4246
  namespace = c.namespace + 'stickyheaders ',
@@ -4225,17 +4275,19 @@ ts.addWidget({
4225
4275
  laststate = '',
4226
4276
  spacing = 0,
4227
4277
  setWidth = function($orig, $clone){
4228
- $orig.filter(':visible').each(function(i) {
4229
- var width, border,
4230
- $cell = $clone.filter(':visible').eq(i),
4231
- $this = $(this);
4278
+ var index, width, border, $cell, $this,
4279
+ $cells = $orig.filter(':visible'),
4280
+ len = $cells.length;
4281
+ for ( index = 0; index < len; index++ ) {
4282
+ $cell = $clone.filter(':visible').eq(index);
4283
+ $this = $cells.eq(index);
4232
4284
  // code from https://github.com/jmosbech/StickyTableHeaders
4233
4285
  if ($this.css('box-sizing') === 'border-box') {
4234
4286
  width = $this.outerWidth();
4235
4287
  } else {
4236
4288
  if ($cell.css('border-collapse') === 'collapse') {
4237
4289
  if (window.getComputedStyle) {
4238
- width = parseFloat( window.getComputedStyle(this, null).width );
4290
+ width = parseFloat( window.getComputedStyle($this[0], null).width );
4239
4291
  } else {
4240
4292
  // ie8 only
4241
4293
  border = parseFloat( $this.css('border-width') );
@@ -4246,10 +4298,11 @@ ts.addWidget({
4246
4298
  }
4247
4299
  }
4248
4300
  $cell.css({
4301
+ 'width': width,
4249
4302
  'min-width': width,
4250
4303
  'max-width': width
4251
4304
  });
4252
- });
4305
+ }
4253
4306
  },
4254
4307
  resizeHeader = function() {
4255
4308
  stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0;
@@ -4261,6 +4314,39 @@ ts.addWidget({
4261
4314
  });
4262
4315
  setWidth( $table, $stickyTable );
4263
4316
  setWidth( $header, $stickyCells );
4317
+ },
4318
+ scrollSticky = function( resizing ) {
4319
+ if (!$table.is(':visible')) { return; } // fixes #278
4320
+ // Detect nested tables - fixes #724
4321
+ nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0;
4322
+ var offset = $table.offset(),
4323
+ yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3
4324
+ xWindow = $.isWindow( $xScroll[0] ),
4325
+ // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop,
4326
+ scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop,
4327
+ tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)),
4328
+ isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden',
4329
+ cssSettings = { visibility : isVisible };
4330
+
4331
+ if ($attach.length) {
4332
+ cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop();
4333
+ }
4334
+ if (xWindow) {
4335
+ // adjust when scrolling horizontally - fixes issue #143
4336
+ cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing;
4337
+ }
4338
+ if ($nestedSticky.length) {
4339
+ cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop;
4340
+ }
4341
+ $stickyWrap
4342
+ .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide )
4343
+ .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide )
4344
+ .css(cssSettings);
4345
+ if (isVisible !== laststate || resizing) {
4346
+ // make sure the column widths match
4347
+ resizeHeader();
4348
+ laststate = isVisible;
4349
+ }
4264
4350
  };
4265
4351
  // only add a position relative if a position isn't already defined
4266
4352
  if ($attach.length && !$attach.css('position')) {
@@ -4292,48 +4378,26 @@ ts.addWidget({
4292
4378
 
4293
4379
  // onRenderHeader is defined, we need to do something about it (fixes #641)
4294
4380
  if (c.onRenderHeader) {
4295
- $stickyThead.children('tr').children().each(function(index){
4381
+ $t = $stickyThead.children('tr').children();
4382
+ len = $t.length;
4383
+ for ( index = 0; index < len; index++ ) {
4296
4384
  // send second parameter
4297
- c.onRenderHeader.apply( $(this), [ index, c, $stickyTable ] );
4298
- });
4385
+ c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] );
4386
+ }
4299
4387
  }
4300
4388
 
4301
4389
  // make it sticky!
4302
4390
  $xScroll.add($yScroll)
4303
- .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') )
4304
- .bind('scroll resize '.split(' ').join( namespace ), function(event) {
4305
- if (!$table.is(':visible')) { return; } // fixes #278
4306
- // Detect nested tables - fixes #724
4307
- nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0;
4308
- var offset = $table.offset(),
4309
- yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3
4310
- xWindow = $.isWindow( $xScroll[0] ),
4311
- // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop,
4312
- scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop,
4313
- tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)),
4314
- isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden',
4315
- cssSettings = { visibility : isVisible };
4316
-
4317
- if ($attach.length) {
4318
- cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop();
4319
- }
4320
- if (xWindow) {
4321
- // adjust when scrolling horizontally - fixes issue #143
4322
- cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing;
4323
- }
4324
- if ($nestedSticky.length) {
4325
- cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop;
4326
- }
4327
- $stickyWrap
4328
- .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide )
4329
- .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide )
4330
- .css(cssSettings);
4331
- if (isVisible !== laststate || event.type === 'resize') {
4332
- // make sure the column widths match
4333
- resizeHeader();
4334
- laststate = isVisible;
4335
- }
4336
- });
4391
+ .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') )
4392
+ .bind('scroll resize '.split(' ').join( namespace ), function( event ) {
4393
+ scrollSticky( event.type === 'resize' );
4394
+ });
4395
+ c.$table
4396
+ .unbind('stickyHeadersUpdate' + namespace)
4397
+ .bind('stickyHeadersUpdate' + namespace, function(){
4398
+ scrollSticky( true );
4399
+ });
4400
+
4337
4401
  if (wo.stickyHeaders_addResizeEvent) {
4338
4402
  ts.addHeaderResizeEvent(table);
4339
4403
  }
@@ -4369,7 +4433,7 @@ ts.addWidget({
4369
4433
  var namespace = c.namespace + 'stickyheaders ';
4370
4434
  c.$table
4371
4435
  .removeClass('hasStickyHeaders')
4372
- .unbind( ('pagerComplete filterEnd '.split(' ').join(namespace)).replace(/\s+/g, ' ') )
4436
+ .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') )
4373
4437
  .next('.' + ts.css.stickyWrap).remove();
4374
4438
  if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table
4375
4439
  $(window)
@@ -4383,7 +4447,8 @@ ts.addWidget({
4383
4447
 
4384
4448
  })(jQuery, window);
4385
4449
 
4386
- /*! Widget: resizable - updated 5/17/2015 (v2.22.0) */
4450
+ /*! Widget: resizable - updated 6/26/2015 (v2.22.2) */
4451
+ /*jshint browser:true, jquery:true, unused:false */
4387
4452
  ;(function ($, window) {
4388
4453
  'use strict';
4389
4454
  var ts = $.tablesorter || {};
@@ -4402,8 +4467,8 @@ $(function(){
4402
4467
  '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' +
4403
4468
  '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' +
4404
4469
  // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header
4405
- '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px; top: 1px;' +
4406
- 'cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' +
4470
+ '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' +
4471
+ 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' +
4407
4472
  '</style>';
4408
4473
  $(s).appendTo('body');
4409
4474
  });
@@ -4412,34 +4477,69 @@ ts.resizable = {
4412
4477
  init : function( c, wo ) {
4413
4478
  if ( c.$table.hasClass( 'hasResizable' ) ) { return; }
4414
4479
  c.$table.addClass( 'hasResizable' );
4415
- ts.resizableReset( c.table, true ); // set default widths
4480
+
4481
+ var noResize, $header, column, storedSizes, tmp,
4482
+ $table = c.$table,
4483
+ $parent = $table.parent(),
4484
+ marginTop = parseInt( $table.css( 'margin-top' ), 10 ),
4416
4485
 
4417
4486
  // internal variables
4418
- wo.resizable_ = {
4419
- $wrap : c.$table.parent(),
4487
+ vars = wo.resizable_ = {
4488
+ useStorage : ts.storage && wo.resizable !== false,
4489
+ $wrap : $parent,
4420
4490
  mouseXPosition : 0,
4421
4491
  $target : null,
4422
4492
  $next : null,
4423
- overflow : c.$table.parent().css('overflow') === 'auto',
4424
- fullWidth : Math.abs(c.$table.parent().width() - c.$table.width()) < 20,
4493
+ overflow : $parent.css('overflow') === 'auto' ||
4494
+ $parent.css('overflow') === 'scroll' ||
4495
+ $parent.css('overflow-x') === 'auto' ||
4496
+ $parent.css('overflow-x') === 'scroll',
4425
4497
  storedSizes : []
4426
4498
  };
4427
4499
 
4428
- var noResize, $header, column, storedSizes,
4429
- marginTop = parseInt( c.$table.css( 'margin-top' ), 10 );
4500
+ // set default widths
4501
+ ts.resizableReset( c.table, true );
4502
+
4503
+ // now get measurements!
4504
+ vars.tableWidth = $table.width();
4505
+ // attempt to autodetect
4506
+ vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20;
4507
+
4508
+ /*
4509
+ // Hacky method to determine if table width is set to "auto"
4510
+ // http://stackoverflow.com/a/20892048/145346
4511
+ if ( !vars.fullWidth ) {
4512
+ tmp = $table.width();
4513
+ $header = $table.wrap('<span>').parent(); // temp variable
4514
+ storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0;
4515
+ $table.css( 'margin-left', storedSizes + 50 );
4516
+ vars.tableWidth = $header.width() > tmp ? 'auto' : tmp;
4517
+ $table.css( 'margin-left', storedSizes ? storedSizes : '' );
4518
+ $header = null;
4519
+ $table.unwrap('<span>');
4520
+ }
4521
+ */
4430
4522
 
4431
- wo.resizable_.storedSizes = storedSizes = ( ( ts.storage && wo.resizable !== false ) ?
4523
+ if ( vars.useStorage && vars.overflow ) {
4524
+ // save table width
4525
+ ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth );
4526
+ tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto';
4527
+ ts.resizable.setWidth( $table, tmp, true );
4528
+ }
4529
+ wo.resizable_.storedSizes = storedSizes = ( vars.useStorage ?
4432
4530
  ts.storage( c.table, ts.css.resizableStorage ) :
4433
4531
  [] ) || [];
4434
4532
  ts.resizable.setWidths( c, wo, storedSizes );
4533
+ ts.resizable.updateStoredSizes( c, wo );
4435
4534
 
4436
4535
  wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' )
4437
4536
  .css({ top : marginTop })
4438
- .insertBefore( c.$table );
4537
+ .insertBefore( $table );
4439
4538
  // add container
4440
4539
  for ( column = 0; column < c.columns; column++ ) {
4441
4540
  $header = c.$headerIndexed[ column ];
4442
- noResize = ts.getData( $header, ts.getColumnData( c.table, c.headers, column ), 'resizable' ) === 'false';
4541
+ tmp = ts.getColumnData( c.table, c.headers, column );
4542
+ noResize = ts.getData( $header, tmp, 'resizable' ) === 'false';
4443
4543
  if ( !noResize ) {
4444
4544
  $( '<div class="' + ts.css.resizableHandle + '">' )
4445
4545
  .appendTo( wo.$resizable_container )
@@ -4451,37 +4551,52 @@ ts.resizable = {
4451
4551
  .bind( 'selectstart', false );
4452
4552
  }
4453
4553
  }
4454
- c.$table.one('tablesorter-initialized', function() {
4554
+ $table.one('tablesorter-initialized', function() {
4455
4555
  ts.resizable.setHandlePosition( c, wo );
4456
4556
  ts.resizable.bindings( this.config, this.config.widgetOptions );
4457
4557
  });
4458
4558
  },
4459
4559
 
4460
- setWidth : function( $el, width ) {
4560
+ updateStoredSizes : function( c, wo ) {
4561
+ var column, $header,
4562
+ len = c.columns,
4563
+ vars = wo.resizable_;
4564
+ vars.storedSizes = [];
4565
+ for ( column = 0; column < len; column++ ) {
4566
+ $header = c.$headerIndexed[ column ];
4567
+ vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0;
4568
+ }
4569
+ },
4570
+
4571
+ setWidth : function( $el, width, overflow ) {
4572
+ // overflow tables need min & max width set as well
4461
4573
  $el.css({
4462
4574
  'width' : width,
4463
- 'min-width' : '',
4464
- 'max-width' : ''
4575
+ 'min-width' : overflow ? width : '',
4576
+ 'max-width' : overflow ? width : ''
4465
4577
  });
4466
4578
  },
4467
4579
 
4468
4580
  setWidths : function( c, wo, storedSizes ) {
4469
- var column,
4581
+ var column, $temp,
4582
+ vars = wo.resizable_,
4470
4583
  $extra = $( c.namespace + '_extra_headers' ),
4471
4584
  $col = c.$table.children( 'colgroup' ).children( 'col' );
4472
- storedSizes = storedSizes || wo.resizable_.storedSizes || [];
4585
+ storedSizes = storedSizes || vars.storedSizes || [];
4473
4586
  // process only if table ID or url match
4474
4587
  if ( storedSizes.length ) {
4475
4588
  for ( column = 0; column < c.columns; column++ ) {
4476
4589
  // set saved resizable widths
4477
- c.$headerIndexed[ column ].width( storedSizes[ column ] );
4590
+ ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow );
4478
4591
  if ( $extra.length ) {
4479
4592
  // stickyHeaders needs to modify min & max width as well
4480
- ts.resizable.setWidth( $extra.eq( column ).add( $col.eq( column ) ), storedSizes[ column ] );
4593
+ $temp = $extra.eq( column ).add( $col.eq( column ) );
4594
+ ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow );
4481
4595
  }
4482
4596
  }
4483
- if ( $( c.namespace + '_extra_table' ).length && !ts.hasWidget( c.table, 'scroller' ) ) {
4484
- ts.resizable.setWidth( $( c.namespace + '_extra_table' ), c.$table.outerWidth() );
4597
+ $temp = $( c.namespace + '_extra_table' );
4598
+ if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) {
4599
+ ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow );
4485
4600
  }
4486
4601
  }
4487
4602
  },
@@ -4541,7 +4656,7 @@ ts.resizable = {
4541
4656
  var namespace = c.namespace + 'tsresize';
4542
4657
  wo.$resizable_container.children().bind( 'mousedown', function( event ) {
4543
4658
  // save header cell and mouse position
4544
- var column, $this,
4659
+ var column,
4545
4660
  vars = wo.resizable_,
4546
4661
  $extras = $( c.namespace + '_extra_headers' ),
4547
4662
  $header = $( event.target ).data( 'header' );
@@ -4560,11 +4675,7 @@ ts.resizable = {
4560
4675
  vars.next = column;
4561
4676
 
4562
4677
  vars.mouseXPosition = event.pageX;
4563
- vars.storedSizes = [];
4564
- for ( column = 0; column < c.columns; column++ ) {
4565
- $this = c.$headerIndexed[ column ];
4566
- vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0;
4567
- }
4678
+ ts.resizable.updateStoredSizes( c, wo );
4568
4679
  ts.resizable.toggleTextSelection( c, true );
4569
4680
  });
4570
4681
 
@@ -4615,47 +4726,51 @@ ts.resizable = {
4615
4726
  mouseMove : function( c, wo, event ) {
4616
4727
  if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; }
4617
4728
  // resize columns
4618
- var vars = wo.resizable_,
4729
+ var column,
4730
+ total = 0,
4731
+ vars = wo.resizable_,
4619
4732
  $next = vars.$next,
4733
+ tar = vars.storedSizes[ vars.target ],
4620
4734
  leftEdge = event.pageX - vars.mouseXPosition;
4621
- if ( vars.fullWidth ) {
4622
- vars.storedSizes[ vars.target ] += leftEdge;
4623
- vars.storedSizes[ vars.next ] -= leftEdge;
4624
- ts.resizable.setWidths( c, wo );
4625
-
4626
- } else if ( vars.overflow ) {
4627
- c.$table.add( $( c.namespace + '_extra_table' ) ).width(function(i, w){
4628
- return w + leftEdge;
4629
- });
4735
+ if ( vars.overflow ) {
4736
+ if ( tar + leftEdge > 0 ) {
4737
+ vars.storedSizes[ vars.target ] += leftEdge;
4738
+ ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true );
4739
+ // update the entire table width
4740
+ for ( column = 0; column < c.columns; column++ ) {
4741
+ total += vars.storedSizes[ column ];
4742
+ }
4743
+ ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total );
4744
+ }
4630
4745
  if ( !$next.length ) {
4631
4746
  // if expanding right-most column, scroll the wrapper
4632
4747
  vars.$wrap[0].scrollLeft = c.$table.width();
4633
4748
  }
4749
+ } else if ( vars.fullWidth ) {
4750
+ vars.storedSizes[ vars.target ] += leftEdge;
4751
+ vars.storedSizes[ vars.next ] -= leftEdge;
4752
+ ts.resizable.setWidths( c, wo );
4634
4753
  } else {
4635
4754
  vars.storedSizes[ vars.target ] += leftEdge;
4636
4755
  ts.resizable.setWidths( c, wo );
4637
4756
  }
4638
4757
  vars.mouseXPosition = event.pageX;
4758
+ // dynamically update sticky header widths
4759
+ c.$table.trigger('stickyHeadersUpdate');
4639
4760
  },
4640
4761
 
4641
4762
  stopResize : function( c, wo ) {
4642
- var $this, column,
4643
- vars = wo.resizable_;
4644
- vars.storedSizes = [];
4645
- if ( ts.storage ) {
4646
- vars.storedSizes = [];
4647
- for ( column = 0; column < c.columns; column++ ) {
4648
- $this = c.$headerIndexed[ column ];
4649
- vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0;
4650
- }
4651
- if ( wo.resizable !== false ) {
4652
- // save all column widths
4653
- ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes );
4654
- }
4763
+ var vars = wo.resizable_;
4764
+ ts.resizable.updateStoredSizes( c, wo );
4765
+ if ( vars.useStorage ) {
4766
+ // save all column widths
4767
+ ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes );
4768
+ ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() );
4655
4769
  }
4656
4770
  vars.mouseXPosition = 0;
4657
4771
  vars.$target = vars.$next = null;
4658
- $(window).trigger('resize'); // will update stickyHeaders, just in case
4772
+ // will update stickyHeaders, just in case, see #912
4773
+ c.$table.trigger('stickyHeadersUpdate');
4659
4774
  }
4660
4775
  };
4661
4776
 
@@ -4666,11 +4781,12 @@ ts.addWidget({
4666
4781
  id: "resizable",
4667
4782
  priority: 40,
4668
4783
  options: {
4669
- resizable : true,
4784
+ resizable : true, // save column widths to storage
4670
4785
  resizable_addLastColumn : false,
4671
4786
  resizable_widths : [],
4672
4787
  resizable_throttle : false, // set to true (5ms) or any number 0-10 range
4673
- resizable_targetLast : false
4788
+ resizable_targetLast : false,
4789
+ resizable_fullWidth : null
4674
4790
  },
4675
4791
  init: function(table, thisWidget, c, wo) {
4676
4792
  ts.resizable.init( c, wo );
@@ -4694,19 +4810,28 @@ ts.resizableReset = function( table, refreshing ) {
4694
4810
  $( table ).each(function(){
4695
4811
  var index, $t,
4696
4812
  c = this.config,
4697
- wo = c && c.widgetOptions;
4813
+ wo = c && c.widgetOptions,
4814
+ vars = wo.resizable_;
4698
4815
  if ( table && c && c.$headerIndexed.length ) {
4816
+ // restore the initial table width
4817
+ if ( vars.overflow && vars.tableWidth ) {
4818
+ ts.resizable.setWidth( c.$table, vars.tableWidth, true );
4819
+ if ( vars.useStorage ) {
4820
+ ts.storage( table, 'tablesorter-table-resized-width', 'auto' );
4821
+ }
4822
+ }
4699
4823
  for ( index = 0; index < c.columns; index++ ) {
4700
4824
  $t = c.$headerIndexed[ index ];
4701
4825
  if ( wo.resizable_widths && wo.resizable_widths[ index ] ) {
4702
- $t.css( 'width', wo.resizable_widths[ index ] );
4826
+ ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow );
4703
4827
  } else if ( !$t.hasClass( 'resizable-false' ) ) {
4704
4828
  // don't clear the width of any column that is not resizable
4705
- $t.css( 'width', '' );
4829
+ ts.resizable.setWidth( $t, '', vars.overflow );
4706
4830
  }
4707
4831
  }
4832
+
4708
4833
  // reset stickyHeader widths
4709
- $( window ).trigger( 'resize' );
4834
+ c.$table.trigger( 'stickyHeadersUpdate' );
4710
4835
  if ( ts.storage && !refreshing ) {
4711
4836
  ts.storage( this, ts.css.resizableStorage, {} );
4712
4837
  }