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
@@ -31,7 +31,8 @@ output = ts.output = {
31
31
  init : function(c) {
32
32
  c.$table
33
33
  .off(output.event)
34
- .on(output.event, function(){
34
+ .on(output.event, function( e ) {
35
+ e.stopPropagation();
35
36
  // explicitly use table.config.widgetOptions because we want
36
37
  // the most up-to-date values; not the "wo" from initialization
37
38
  output.process(c, c.widgetOptions);
@@ -39,81 +40,101 @@ output = ts.output = {
39
40
  },
40
41
 
41
42
  processRow: function(c, $rows, isHeader, isJSON) {
42
- var $this, row, col, rowlen, collen, txt,
43
+ var $cell, $cells, cellsLen, rowIndex, row, col, indx, rowspanLen, colspanLen, txt,
43
44
  wo = c.widgetOptions,
44
45
  tmpRow = [],
45
46
  dupe = wo.output_duplicateSpans,
46
47
  addSpanIndex = isHeader && isJSON && wo.output_headerRows && $.isFunction(wo.output_callbackJSON),
47
- cellIndex = 0;
48
- $rows.each(function(rowIndex) {
48
+ cellIndex = 0,
49
+ rowsLength = $rows.length;
50
+
51
+ for ( rowIndex = 0; rowIndex < rowsLength; rowIndex++ ) {
49
52
  if (!tmpRow[rowIndex]) { tmpRow[rowIndex] = []; }
50
53
  cellIndex = 0;
51
- $(this).children().each(function(){
52
- $this = $(this);
54
+ $cells = $rows.eq( rowIndex ).children();
55
+ cellsLen = $cells.length;
56
+ for ( indx = 0; indx < cellsLen; indx++ ) {
57
+ $cell = $cells.eq( indx );
53
58
  // process rowspans
54
- if ($this.filter('[rowspan]').length) {
55
- rowlen = parseInt( $this.attr('rowspan'), 10) - 1;
56
- txt = output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader );
57
- for (row = 1; row <= rowlen; row++) {
59
+ if ($cell.filter('[rowspan]').length) {
60
+ rowspanLen = parseInt( $cell.attr('rowspan'), 10) - 1;
61
+ txt = output.formatData( wo, $cell, isHeader );
62
+ for (row = 1; row <= rowspanLen; row++) {
58
63
  if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; }
59
64
  tmpRow[rowIndex + row][cellIndex] = isHeader ? txt : dupe ? txt : '';
60
65
  }
61
66
  }
62
67
  // process colspans
63
- if ($this.filter('[colspan]').length) {
64
- collen = parseInt( $this.attr('colspan'), 10) - 1;
65
- txt = output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader );
66
- for (col = 1; col <= collen; col++) {
68
+ if ($cell.filter('[colspan]').length) {
69
+ colspanLen = parseInt( $cell.attr('colspan'), 10) - 1;
70
+ // allow data-attribute to be an empty string
71
+ txt = output.formatData( wo, $cell, isHeader );
72
+ for (col = 1; col <= colspanLen; col++) {
67
73
  // if we're processing the header & making JSON, the header names need to be unique
68
- if ($this.filter('[rowspan]').length) {
69
- rowlen = parseInt( $this.attr('rowspan'), 10);
70
- for (row = 0; row < rowlen; row++) {
74
+ if ($cell.filter('[rowspan]').length) {
75
+ rowspanLen = parseInt( $cell.attr('rowspan'), 10);
76
+ for (row = 0; row < rowspanLen; row++) {
71
77
  if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; }
72
78
  tmpRow[rowIndex + row][cellIndex + col] = addSpanIndex ?
73
- wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : '';
79
+ wo.output_callbackJSON($cell, txt, cellIndex + col) ||
80
+ txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : '';
74
81
  }
75
82
  } else {
76
83
  tmpRow[rowIndex][cellIndex + col] = addSpanIndex ?
77
- wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : '';
84
+ wo.output_callbackJSON($cell, txt, cellIndex + col) ||
85
+ txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : '';
78
86
  }
79
87
  }
80
88
  }
81
89
 
82
- // don't include hidden columns, unless option is set
83
- if ( !wo.output_hiddenColumns && $this.css('display') !== 'none' ) {
84
- // skip column if already defined
85
- while (typeof tmpRow[rowIndex][cellIndex] !== 'undefined') { cellIndex++; }
86
- tmpRow[rowIndex][cellIndex] = tmpRow[rowIndex][cellIndex] ||
87
- output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader );
88
- cellIndex++;
89
- }
90
- });
91
- });
92
- return tmpRow;
90
+ // skip column if already defined
91
+ while (typeof tmpRow[rowIndex][cellIndex] !== 'undefined') { cellIndex++; }
92
+
93
+ tmpRow[rowIndex][cellIndex] = tmpRow[rowIndex][cellIndex] ||
94
+ output.formatData( wo, $cell, isHeader );
95
+ cellIndex++;
96
+ }
97
+ }
98
+ return ts.output.removeColumns( c, wo, tmpRow );
93
99
  },
94
100
 
95
- ignoreColumns : function(wo, data) {
96
- // ignore columns -> remove data from built array (because we've already processed any rowspan/colspan)
97
- $.each( data, function(indx, val){
98
- data[indx] = $.grep(val, function(v, cellIndx){
99
- return $.inArray(cellIndx, wo.output_ignoreColumns) < 0;
100
- });
101
- });
101
+ // remove hidden/ignored columns
102
+ removeColumns : function( c, wo, arry ) {
103
+ var rowIndex, row, colIndex,
104
+ data = [],
105
+ len = arry.length;
106
+ for ( rowIndex = 0; rowIndex < len; rowIndex++ ) {
107
+ row = arry[ rowIndex ];
108
+ data[ rowIndex ] = [];
109
+ for ( colIndex = 0; colIndex < c.columns; colIndex++ ) {
110
+ if ( !wo.output_hiddenColumnArray[ colIndex ] ) {
111
+ data[ rowIndex ].push( row[ colIndex ] );
112
+ }
113
+ }
114
+ }
102
115
  return data;
103
116
  },
104
117
 
105
118
  process : function(c, wo) {
106
- var mydata, $this, $rows, headers, csvData, len,
119
+ var mydata, $this, $rows, headers, csvData, len, rowsLen, tmp,
107
120
  hasStringify = window.JSON && JSON.hasOwnProperty('stringify'),
108
121
  indx = 0,
109
122
  tmpData = (wo.output_separator || ',').toLowerCase(),
110
123
  outputJSON = tmpData === 'json',
111
124
  outputArray = tmpData === 'array',
112
125
  separator = outputJSON || outputArray ? ',' : wo.output_separator,
126
+ saveRows = wo.output_saveRows,
113
127
  $el = c.$table;
114
128
  // regex to look for the set separator or HTML
115
129
  wo.output_regex = new RegExp('(' + (/\\/.test(separator) ? '\\' : '' ) + separator + ')' );
116
130
 
131
+ // make a list of hidden columns
132
+ wo.output_hiddenColumnArray = [];
133
+ for ( indx = 0; indx < c.columns; indx++ ) {
134
+ wo.output_hiddenColumnArray[ indx ] = $.inArray( indx, wo.output_ignoreColumns ) > -1 ||
135
+ c.$headerIndexed[ indx ].css( 'display' ) === 'none';
136
+ }
137
+
117
138
  // get header cells
118
139
  $this = $el.find('thead tr:visible').not('.' + (ts.css.filterRow || 'tablesorter-filter-row') );
119
140
  headers = output.processRow(c, $this, true, outputJSON);
@@ -121,36 +142,40 @@ output = ts.output = {
121
142
  // all tbody rows
122
143
  $rows = $el.children('tbody').children('tr');
123
144
 
145
+ // get (f)iltered, (v)isible, all rows (look for the first letter only), or jQuery filter selector
146
+ $rows = /^f/.test(saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) :
147
+ /^v/.test(saveRows) ? $rows.filter(':visible') :
148
+ // look for '.' (class selector), '#' (id selector),
149
+ // ':' (basic filters, e.g. ':not()') or '[' (attribute selector start)
150
+ /^[.#:\[]/.test(saveRows) ? $rows.filter(saveRows) :
151
+ // default to all rows
152
+ $rows;
153
+
154
+ // process to array of arrays
155
+ csvData = output.processRow(c, $rows);
156
+
124
157
  if (wo.output_includeFooter) {
125
158
  // clone, to force the tfoot rows to the end of this selection of rows
126
159
  // otherwise they appear after the thead (the order in the HTML)
127
- $rows = $rows.add( $el.children('tfoot').children('tr').clone() );
160
+ csvData = csvData.concat( output.processRow( c, $el.children('tfoot').children('tr:visible') ) );
128
161
  }
129
162
 
130
- // get (f)iltered, (v)isible or all rows (look for the first letter only)
131
- $rows = /f/.test(wo.output_saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) :
132
- /v/.test(wo.output_saveRows) ? $rows.filter(':visible') : $rows;
133
-
134
- // process to array of arrays
135
- csvData = output.processRow(c, $rows);
136
163
  len = headers.length;
137
164
 
138
- if (wo.output_ignoreColumns.length) {
139
- headers = output.ignoreColumns(wo, headers);
140
- csvData = output.ignoreColumns(wo, csvData);
141
- }
142
-
143
165
  if (outputJSON) {
144
166
  tmpData = [];
145
- $.each( csvData, function(indx, val){
167
+ rowsLen = csvData.length;
168
+ for ( indx = 0; indx < rowsLen; indx++ ) {
146
169
  // multiple header rows & output_headerRows = true, pick the last row...
147
- tmpData.push( output.row2Hash( headers[ (len > 1 && wo.output_headerRows) ? indx % len : len - 1], val ) );
148
- });
170
+ tmp = headers[ ( len > 1 && wo.output_headerRows ) ? indx % len : len - 1 ];
171
+ tmpData.push( output.row2Hash( tmp, csvData[ indx ] ) );
172
+ }
149
173
 
150
174
  // requires JSON stringify; if it doesn't exist, the output will show [object Object],... in the output window
151
175
  mydata = hasStringify ? JSON.stringify(tmpData) : tmpData;
152
176
  } else {
153
- tmpData = output.row2CSV(wo, wo.output_headerRows ? headers : [ headers[ (len > 1 && wo.output_headerRows) ? indx % len : len - 1] ], outputArray)
177
+ tmp = [ headers[ ( len > 1 && wo.output_headerRows ) ? indx % len : len - 1 ] ];
178
+ tmpData = output.row2CSV(wo, wo.output_headerRows ? headers : tmp, outputArray)
154
179
  .concat( output.row2CSV(wo, csvData, outputArray) );
155
180
 
156
181
  // stringify the array; if stringify doesn't exist the array will be flattened
@@ -174,30 +199,33 @@ output = ts.output = {
174
199
  rowLen = tmpRow.length;
175
200
  for (rowIndex = 0; rowIndex < rowLen; rowIndex++) {
176
201
  // remove any blank rows
177
- tmp = tmpRow[rowIndex].join('').replace(/\"/g,'');
178
- if (tmpRow[rowIndex].length > 0 && tmp !== '') {
202
+ tmp = ( tmpRow[rowIndex] || [] ).join('').replace(/\"/g,'');
203
+ if ( ( tmpRow[rowIndex] || [] ).length > 0 && tmp !== '' ) {
179
204
  csvData[csvData.length] = outputArray ? tmpRow[rowIndex] : tmpRow[rowIndex].join(wo.output_separator);
180
205
  }
181
206
  }
182
207
  return csvData;
183
208
  },
184
209
 
185
- row2Hash : function(keys, values) {
186
- var json = {};
187
- $.each(values, function(indx, val) {
210
+ row2Hash : function( keys, values ) {
211
+ var indx,
212
+ json = {},
213
+ len = values.length;
214
+ for ( indx = 0; indx < len; indx++ ) {
188
215
  if ( indx < keys.length ) {
189
- json[ keys[indx] ] = val;
216
+ json[ keys[ indx ] ] = values[ indx ];
190
217
  }
191
- });
218
+ }
192
219
  return json;
193
220
  },
194
221
 
195
- formatData : function(wo, input, isHeader) {
196
- var txt,
222
+ formatData : function(wo, $el, isHeader) {
223
+ var attr = $el.attr(wo.output_dataAttrib),
224
+ txt = typeof attr !== 'undefined' ? attr : $el.html(),
197
225
  quotes = (wo.output_separator || ',').toLowerCase(),
198
226
  separator = quotes === 'json' || quotes === 'array',
199
227
  // replace " with “ if undefined
200
- result = input.replace(/\"/g, wo.output_replaceQuote || '\u201c');
228
+ result = txt.replace(/\"/g, wo.output_replaceQuote || '\u201c');
201
229
  // replace line breaks with \\n & tabs with \\t
202
230
  if (!wo.output_trimSpaces) {
203
231
  result = result.replace(output.regexBR, output.replaceCR).replace(/\t/g, output.replaceTab);
@@ -269,7 +297,8 @@ output = ts.output = {
269
297
  // Dispatching click event; using $(link).trigger() won't work
270
298
  if (document.createEvent) {
271
299
  e = document.createEvent('MouseEvents');
272
- // event.initMouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);
300
+ // event.initMouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY,
301
+ // ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);
273
302
  e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
274
303
  link.dispatchEvent(e);
275
304
  }
@@ -300,7 +329,7 @@ ts.addWidget({
300
329
  output_dataAttrib : 'data-name', // header attrib containing modified header name
301
330
  output_headerRows : false, // if true, include multiple header rows (JSON only)
302
331
  output_delivery : 'popup', // popup, download
303
- output_saveRows : 'filtered', // all, visible or filtered
332
+ output_saveRows : 'filtered', // (a)ll, (v)isible, (f)iltered or jQuery filter selector
304
333
  output_duplicateSpans: true, // duplicate output data in tbody colspan/rowspan
305
334
  output_replaceQuote : '\u201c;', // left double quote
306
335
  output_includeHTML : false,
@@ -661,7 +661,7 @@ tsp = ts.pager = {
661
661
  c.totalRows = p.totalRows = result.total;
662
662
  c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total;
663
663
  th = result.headers;
664
- d = result.rows;
664
+ d = result.rows || [];
665
665
  } else {
666
666
  // allow [ total, rows, headers ] or [ rows, total, headers ]
667
667
  t = isNaN(result[0]) && !isNaN(result[1]);
@@ -670,7 +670,8 @@ tsp = ts.pager = {
670
670
  p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count;
671
671
  // can't set filtered rows when returning an array
672
672
  c.totalRows = c.filteredRows = p.filteredRows = p.totalRows;
673
- d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data
673
+ // set row data to empty array if nothing found - see http://stackoverflow.com/q/30875583/145346
674
+ d = p.totalRows === 0 ? [] : result[t ? 0 : 1] || []; // row data
674
675
  th = result[2]; // headers
675
676
  }
676
677
  l = d && d.length;
@@ -17,7 +17,7 @@
17
17
  var h = '', i, $tr, l, skip;
18
18
  // cache and collect all TH headers
19
19
  if (!wo.repeatHeaders) {
20
- h = '<tr class="repeated-header remove-me">';
20
+ h = '<tr class="repeated-header ' + c.selectorRemove.slice(1) + '">';
21
21
  for (i = 0; i < c.columns; i++) {
22
22
  // only get the headerContent text
23
23
  h += '<th>' + $.trim( c.$headers.eq(i).text() ) + '</th>';
@@ -1,4 +1,5 @@
1
- /*! Widget: resizable - updated 5/17/2015 (v2.22.0) */
1
+ /*! Widget: resizable - updated 6/26/2015 (v2.22.2) */
2
+ /*jshint browser:true, jquery:true, unused:false */
2
3
  ;(function ($, window) {
3
4
  'use strict';
4
5
  var ts = $.tablesorter || {};
@@ -17,8 +18,8 @@ $(function(){
17
18
  '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' +
18
19
  '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' +
19
20
  // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header
20
- '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px; top: 1px;' +
21
- 'cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' +
21
+ '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' +
22
+ 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' +
22
23
  '</style>';
23
24
  $(s).appendTo('body');
24
25
  });
@@ -27,34 +28,69 @@ ts.resizable = {
27
28
  init : function( c, wo ) {
28
29
  if ( c.$table.hasClass( 'hasResizable' ) ) { return; }
29
30
  c.$table.addClass( 'hasResizable' );
30
- ts.resizableReset( c.table, true ); // set default widths
31
+
32
+ var noResize, $header, column, storedSizes, tmp,
33
+ $table = c.$table,
34
+ $parent = $table.parent(),
35
+ marginTop = parseInt( $table.css( 'margin-top' ), 10 ),
31
36
 
32
37
  // internal variables
33
- wo.resizable_ = {
34
- $wrap : c.$table.parent(),
38
+ vars = wo.resizable_ = {
39
+ useStorage : ts.storage && wo.resizable !== false,
40
+ $wrap : $parent,
35
41
  mouseXPosition : 0,
36
42
  $target : null,
37
43
  $next : null,
38
- overflow : c.$table.parent().css('overflow') === 'auto',
39
- fullWidth : Math.abs(c.$table.parent().width() - c.$table.width()) < 20,
44
+ overflow : $parent.css('overflow') === 'auto' ||
45
+ $parent.css('overflow') === 'scroll' ||
46
+ $parent.css('overflow-x') === 'auto' ||
47
+ $parent.css('overflow-x') === 'scroll',
40
48
  storedSizes : []
41
49
  };
42
50
 
43
- var noResize, $header, column, storedSizes,
44
- marginTop = parseInt( c.$table.css( 'margin-top' ), 10 );
51
+ // set default widths
52
+ ts.resizableReset( c.table, true );
53
+
54
+ // now get measurements!
55
+ vars.tableWidth = $table.width();
56
+ // attempt to autodetect
57
+ vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20;
45
58
 
46
- wo.resizable_.storedSizes = storedSizes = ( ( ts.storage && wo.resizable !== false ) ?
59
+ /*
60
+ // Hacky method to determine if table width is set to "auto"
61
+ // http://stackoverflow.com/a/20892048/145346
62
+ if ( !vars.fullWidth ) {
63
+ tmp = $table.width();
64
+ $header = $table.wrap('<span>').parent(); // temp variable
65
+ storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0;
66
+ $table.css( 'margin-left', storedSizes + 50 );
67
+ vars.tableWidth = $header.width() > tmp ? 'auto' : tmp;
68
+ $table.css( 'margin-left', storedSizes ? storedSizes : '' );
69
+ $header = null;
70
+ $table.unwrap('<span>');
71
+ }
72
+ */
73
+
74
+ if ( vars.useStorage && vars.overflow ) {
75
+ // save table width
76
+ ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth );
77
+ tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto';
78
+ ts.resizable.setWidth( $table, tmp, true );
79
+ }
80
+ wo.resizable_.storedSizes = storedSizes = ( vars.useStorage ?
47
81
  ts.storage( c.table, ts.css.resizableStorage ) :
48
82
  [] ) || [];
49
83
  ts.resizable.setWidths( c, wo, storedSizes );
84
+ ts.resizable.updateStoredSizes( c, wo );
50
85
 
51
86
  wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' )
52
87
  .css({ top : marginTop })
53
- .insertBefore( c.$table );
88
+ .insertBefore( $table );
54
89
  // add container
55
90
  for ( column = 0; column < c.columns; column++ ) {
56
91
  $header = c.$headerIndexed[ column ];
57
- noResize = ts.getData( $header, ts.getColumnData( c.table, c.headers, column ), 'resizable' ) === 'false';
92
+ tmp = ts.getColumnData( c.table, c.headers, column );
93
+ noResize = ts.getData( $header, tmp, 'resizable' ) === 'false';
58
94
  if ( !noResize ) {
59
95
  $( '<div class="' + ts.css.resizableHandle + '">' )
60
96
  .appendTo( wo.$resizable_container )
@@ -66,37 +102,52 @@ ts.resizable = {
66
102
  .bind( 'selectstart', false );
67
103
  }
68
104
  }
69
- c.$table.one('tablesorter-initialized', function() {
105
+ $table.one('tablesorter-initialized', function() {
70
106
  ts.resizable.setHandlePosition( c, wo );
71
107
  ts.resizable.bindings( this.config, this.config.widgetOptions );
72
108
  });
73
109
  },
74
110
 
75
- setWidth : function( $el, width ) {
111
+ updateStoredSizes : function( c, wo ) {
112
+ var column, $header,
113
+ len = c.columns,
114
+ vars = wo.resizable_;
115
+ vars.storedSizes = [];
116
+ for ( column = 0; column < len; column++ ) {
117
+ $header = c.$headerIndexed[ column ];
118
+ vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0;
119
+ }
120
+ },
121
+
122
+ setWidth : function( $el, width, overflow ) {
123
+ // overflow tables need min & max width set as well
76
124
  $el.css({
77
125
  'width' : width,
78
- 'min-width' : '',
79
- 'max-width' : ''
126
+ 'min-width' : overflow ? width : '',
127
+ 'max-width' : overflow ? width : ''
80
128
  });
81
129
  },
82
130
 
83
131
  setWidths : function( c, wo, storedSizes ) {
84
- var column,
132
+ var column, $temp,
133
+ vars = wo.resizable_,
85
134
  $extra = $( c.namespace + '_extra_headers' ),
86
135
  $col = c.$table.children( 'colgroup' ).children( 'col' );
87
- storedSizes = storedSizes || wo.resizable_.storedSizes || [];
136
+ storedSizes = storedSizes || vars.storedSizes || [];
88
137
  // process only if table ID or url match
89
138
  if ( storedSizes.length ) {
90
139
  for ( column = 0; column < c.columns; column++ ) {
91
140
  // set saved resizable widths
92
- c.$headerIndexed[ column ].width( storedSizes[ column ] );
141
+ ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow );
93
142
  if ( $extra.length ) {
94
143
  // stickyHeaders needs to modify min & max width as well
95
- ts.resizable.setWidth( $extra.eq( column ).add( $col.eq( column ) ), storedSizes[ column ] );
144
+ $temp = $extra.eq( column ).add( $col.eq( column ) );
145
+ ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow );
96
146
  }
97
147
  }
98
- if ( $( c.namespace + '_extra_table' ).length && !ts.hasWidget( c.table, 'scroller' ) ) {
99
- ts.resizable.setWidth( $( c.namespace + '_extra_table' ), c.$table.outerWidth() );
148
+ $temp = $( c.namespace + '_extra_table' );
149
+ if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) {
150
+ ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow );
100
151
  }
101
152
  }
102
153
  },
@@ -156,7 +207,7 @@ ts.resizable = {
156
207
  var namespace = c.namespace + 'tsresize';
157
208
  wo.$resizable_container.children().bind( 'mousedown', function( event ) {
158
209
  // save header cell and mouse position
159
- var column, $this,
210
+ var column,
160
211
  vars = wo.resizable_,
161
212
  $extras = $( c.namespace + '_extra_headers' ),
162
213
  $header = $( event.target ).data( 'header' );
@@ -175,11 +226,7 @@ ts.resizable = {
175
226
  vars.next = column;
176
227
 
177
228
  vars.mouseXPosition = event.pageX;
178
- vars.storedSizes = [];
179
- for ( column = 0; column < c.columns; column++ ) {
180
- $this = c.$headerIndexed[ column ];
181
- vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0;
182
- }
229
+ ts.resizable.updateStoredSizes( c, wo );
183
230
  ts.resizable.toggleTextSelection( c, true );
184
231
  });
185
232
 
@@ -230,47 +277,51 @@ ts.resizable = {
230
277
  mouseMove : function( c, wo, event ) {
231
278
  if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; }
232
279
  // resize columns
233
- var vars = wo.resizable_,
280
+ var column,
281
+ total = 0,
282
+ vars = wo.resizable_,
234
283
  $next = vars.$next,
284
+ tar = vars.storedSizes[ vars.target ],
235
285
  leftEdge = event.pageX - vars.mouseXPosition;
236
- if ( vars.fullWidth ) {
237
- vars.storedSizes[ vars.target ] += leftEdge;
238
- vars.storedSizes[ vars.next ] -= leftEdge;
239
- ts.resizable.setWidths( c, wo );
240
-
241
- } else if ( vars.overflow ) {
242
- c.$table.add( $( c.namespace + '_extra_table' ) ).width(function(i, w){
243
- return w + leftEdge;
244
- });
286
+ if ( vars.overflow ) {
287
+ if ( tar + leftEdge > 0 ) {
288
+ vars.storedSizes[ vars.target ] += leftEdge;
289
+ ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true );
290
+ // update the entire table width
291
+ for ( column = 0; column < c.columns; column++ ) {
292
+ total += vars.storedSizes[ column ];
293
+ }
294
+ ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total );
295
+ }
245
296
  if ( !$next.length ) {
246
297
  // if expanding right-most column, scroll the wrapper
247
298
  vars.$wrap[0].scrollLeft = c.$table.width();
248
299
  }
300
+ } else if ( vars.fullWidth ) {
301
+ vars.storedSizes[ vars.target ] += leftEdge;
302
+ vars.storedSizes[ vars.next ] -= leftEdge;
303
+ ts.resizable.setWidths( c, wo );
249
304
  } else {
250
305
  vars.storedSizes[ vars.target ] += leftEdge;
251
306
  ts.resizable.setWidths( c, wo );
252
307
  }
253
308
  vars.mouseXPosition = event.pageX;
309
+ // dynamically update sticky header widths
310
+ c.$table.trigger('stickyHeadersUpdate');
254
311
  },
255
312
 
256
313
  stopResize : function( c, wo ) {
257
- var $this, column,
258
- vars = wo.resizable_;
259
- vars.storedSizes = [];
260
- if ( ts.storage ) {
261
- vars.storedSizes = [];
262
- for ( column = 0; column < c.columns; column++ ) {
263
- $this = c.$headerIndexed[ column ];
264
- vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0;
265
- }
266
- if ( wo.resizable !== false ) {
267
- // save all column widths
268
- ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes );
269
- }
314
+ var vars = wo.resizable_;
315
+ ts.resizable.updateStoredSizes( c, wo );
316
+ if ( vars.useStorage ) {
317
+ // save all column widths
318
+ ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes );
319
+ ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() );
270
320
  }
271
321
  vars.mouseXPosition = 0;
272
322
  vars.$target = vars.$next = null;
273
- $(window).trigger('resize'); // will update stickyHeaders, just in case
323
+ // will update stickyHeaders, just in case, see #912
324
+ c.$table.trigger('stickyHeadersUpdate');
274
325
  }
275
326
  };
276
327
 
@@ -281,11 +332,12 @@ ts.addWidget({
281
332
  id: "resizable",
282
333
  priority: 40,
283
334
  options: {
284
- resizable : true,
335
+ resizable : true, // save column widths to storage
285
336
  resizable_addLastColumn : false,
286
337
  resizable_widths : [],
287
338
  resizable_throttle : false, // set to true (5ms) or any number 0-10 range
288
- resizable_targetLast : false
339
+ resizable_targetLast : false,
340
+ resizable_fullWidth : null
289
341
  },
290
342
  init: function(table, thisWidget, c, wo) {
291
343
  ts.resizable.init( c, wo );
@@ -309,19 +361,28 @@ ts.resizableReset = function( table, refreshing ) {
309
361
  $( table ).each(function(){
310
362
  var index, $t,
311
363
  c = this.config,
312
- wo = c && c.widgetOptions;
364
+ wo = c && c.widgetOptions,
365
+ vars = wo.resizable_;
313
366
  if ( table && c && c.$headerIndexed.length ) {
367
+ // restore the initial table width
368
+ if ( vars.overflow && vars.tableWidth ) {
369
+ ts.resizable.setWidth( c.$table, vars.tableWidth, true );
370
+ if ( vars.useStorage ) {
371
+ ts.storage( table, 'tablesorter-table-resized-width', 'auto' );
372
+ }
373
+ }
314
374
  for ( index = 0; index < c.columns; index++ ) {
315
375
  $t = c.$headerIndexed[ index ];
316
376
  if ( wo.resizable_widths && wo.resizable_widths[ index ] ) {
317
- $t.css( 'width', wo.resizable_widths[ index ] );
377
+ ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow );
318
378
  } else if ( !$t.hasClass( 'resizable-false' ) ) {
319
379
  // don't clear the width of any column that is not resizable
320
- $t.css( 'width', '' );
380
+ ts.resizable.setWidth( $t, '', vars.overflow );
321
381
  }
322
382
  }
383
+
323
384
  // reset stickyHeader widths
324
- $( window ).trigger( 'resize' );
385
+ c.$table.trigger( 'stickyHeadersUpdate' );
325
386
  if ( ts.storage && !refreshing ) {
326
387
  ts.storage( this, ts.css.resizableStorage, {} );
327
388
  }