jquery-tablesorter 1.18.5 → 1.19.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.
Files changed (31) 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 +68 -46
  5. data/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js +3 -3
  6. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +2383 -2100
  7. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +2298 -2039
  8. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +90 -66
  9. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js +45 -20
  10. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js +78 -20
  11. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js +37 -15
  12. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +4 -4
  13. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js +2 -2
  14. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +122 -30
  15. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +8 -6
  16. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +79 -58
  17. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +209 -128
  18. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js +5 -4
  19. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js +367 -0
  20. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +81 -35
  21. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +3 -3
  22. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +79 -53
  23. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +25 -14
  24. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js +5 -2
  25. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +11 -7
  26. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js +149 -50
  27. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js +7 -7
  28. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js +2 -2
  29. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +2 -2
  30. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js +192 -0
  31. metadata +4 -2
@@ -1,24 +1,35 @@
1
- /*! Parser: jQuery Globalize - updated 5/17/2015 (v2.22.0) */
1
+ /*! Parser: jQuery Globalize - updated 11/2/2015 (v2.24.1) */
2
2
  /* Extract localized data using jQuery's Globalize parsers; set
3
- Globalize.locale( 'xx' ) prior to initializing tablesorter! */
4
- /*jshint jquery:true */
3
+ Globalize.locale( 'xx' ) in the globalize settings */
4
+ /*jshint jquery:true, newcap: false */
5
5
  /*global Globalize:false */
6
6
  ;( function( $ ) {
7
7
  'use strict';
8
8
 
9
9
  /*! jQuery Globalize date parser (https://github.com/jquery/globalize#date-module) */
10
- /* demo: http://jsfiddle.net/Mottie/0j18Lw8r/ */
11
10
  $.tablesorter.addParser({
12
11
  id: 'globalize-date',
13
12
  is: function () {
14
13
  return false;
15
14
  },
16
15
  format: function ( str, table, cell, cellIndex ) {
17
- var c = table.config,
18
- // add options to 'config.globalize' for all columns --> globalize : { skeleton: 'GyMMMd' }
19
- // or per column by using the column index --> globalize : { 0 : { datetime: 'medium' } }
20
- options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {},
21
- date = Globalize && Globalize.dateParser ? Globalize.dateParser( options )( str ) :
16
+ var globalize, date,
17
+ c = table.config,
18
+ // add options to 'config.globalize' for all columns --> globalize : { skeleton: 'GyMMMd' }
19
+ // or per column by using the column index --> globalize : { 0 : { datetime: 'medium' } }
20
+ options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {};
21
+ if ( Globalize ) {
22
+ globalize = typeof options.Globalize === 'object' ?
23
+ // initialized Globalize object
24
+ options.Globalize :
25
+ // Globalize initialized from "lang" option
26
+ Globalize( options.lang || 'en' );
27
+ if ( !options.Globalize ) {
28
+ // cache the object
29
+ options.Globalize = globalize;
30
+ }
31
+ }
32
+ date = globalize && globalize.dateParser ? globalize.dateParser( options )( str ) :
22
33
  str ? new Date( str ) : str;
23
34
  return date instanceof Date && isFinite( date ) ? date.getTime() : str;
24
35
  },
@@ -26,18 +37,29 @@
26
37
  });
27
38
 
28
39
  /*! jQuery Globalize number parser (https://github.com/jquery/globalize#number-module) */
29
- /* demo: http://jsfiddle.net/Mottie/0j18Lw8r/ */
30
40
  $.tablesorter.addParser({
31
41
  id: 'globalize-number',
32
42
  is: function () {
33
43
  return false;
34
44
  },
35
45
  format: function ( str, table, cell, cellIndex ) {
36
- var c = table.config,
37
- // add options to 'config.globalize' for all columns --> globalize : { skeleton: 'GyMMMd' }
38
- // or per column by using the column index --> globalize : { 0 : { datetime: 'medium' } }
39
- options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {},
40
- num = Globalize && Globalize.numberParser ? Globalize.numberParser( options )( str ) :
46
+ var globalize, num,
47
+ c = table.config,
48
+ // add options to 'config.globalize' for all columns --> globalize : { skeleton: 'GyMMMd' }
49
+ // or per column by using the column index --> globalize : { 0 : { datetime: 'medium' } }
50
+ options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {};
51
+ if ( Globalize ) {
52
+ globalize = typeof options.Globalize === 'object' ?
53
+ // initialized Globalize object
54
+ options.Globalize :
55
+ // Globalize initialized from "lang" option
56
+ Globalize( options.lang || 'en' );
57
+ if ( !options.Globalize ) {
58
+ // cache the object
59
+ options.Globalize = globalize;
60
+ }
61
+ }
62
+ num = globalize && globalize.numberParser ? globalize.numberParser( options )( str ) :
41
63
  str ? $.tablesorter.formatFloat( ( str || '' ).replace( /[^\w,. \-()]/g, '' ), table ) : str;
42
64
  return str && typeof num === 'number' ? num : str;
43
65
  },
@@ -1,4 +1,4 @@
1
- /*! Parser: input & select - updated 8/17/2015 (v2.23.0) *//*
1
+ /*! Parser: input & select - updated 10/31/2015 (v2.24.0) *//*
2
2
  * for jQuery 1.7+ & tablesorter 2.7.11+
3
3
  * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html
4
4
  */
@@ -175,12 +175,12 @@
175
175
  return;
176
176
  }
177
177
  // ignore change event if nothing changed
178
- if ( val !== $target.data( 'ts-original-value' ) || event.target.type === 'checkbox' ) {
178
+ if ( c && val !== $target.data( 'ts-original-value' ) || event.target.type === 'checkbox' ) {
179
179
  $target.data( 'ts-original-value', val );
180
180
  // pass undefined resort value so it falls back to config.resort setting
181
- $table.trigger( 'updateCell', [ $cell, undef, function() {
181
+ $.tablesorter.updateCell( c, $cell, undef, function() {
182
182
  updateServer( event, $table, $target );
183
- } ]);
183
+ });
184
184
  }
185
185
  }
186
186
  });
@@ -1,4 +1,4 @@
1
- /* Widget: chart (beta) - updated 2/7/2015 (v2.19.0) *//*
1
+ /* Widget: chart (beta) - updated 10/31/2015 (v2.24.0) *//*
2
2
  * Requires tablesorter v2.8+ and jQuery 1.7+
3
3
  */
4
4
  /*jshint browser:true, jquery:true, unused:false */
@@ -213,7 +213,7 @@
213
213
  }
214
214
 
215
215
  if (value !== false) {
216
- if ( /s/i.test( '' + wo.chart_layout[row.length] ) ) {
216
+ if ( /s/i.test( '' + wo.chart_layout[indx] ) ) {
217
217
  row.push( value );
218
218
  chart_series[objIndex].data.push( value );
219
219
  chart_dataset[objIndex].data.push( value );
@@ -1,4 +1,4 @@
1
- /* Widget: columnSelector (responsive table widget) - updated 8/23/2015 (v2.23.2) *//*
1
+ /* Widget: columnSelector (responsive table widget) - updated 10/31/2015 (v2.24.0) *//*
2
2
  * Requires tablesorter v2.8+ and jQuery 1.7+
3
3
  * by Justin Hallett & Rob Garrison
4
4
  */
@@ -35,6 +35,7 @@
35
35
  colSel.$breakpoints = $('<style></style>').prop('disabled', true).appendTo('head');
36
36
 
37
37
  colSel.isInitializing = true;
38
+ tsColSel.setUpColspan(c, wo);
38
39
  tsColSel.setupSelector(c, wo);
39
40
 
40
41
  if (wo.columnSelector_mediaquery) {
@@ -103,10 +104,11 @@
103
104
  tsColSel.updateBreakpoints(c, wo);
104
105
  tsColSel.updateCols(c, wo);
105
106
  }
107
+ tsColSel.adjustColspans( c, wo );
106
108
  },
107
109
 
108
110
  setupSelector: function(c, wo) {
109
- var name,
111
+ var index, name, $header, priority, col, colId,
110
112
  colSel = c.selector,
111
113
  $container = colSel.$container,
112
114
  useStorage = wo.columnSelector_saveColumns && ts.storage,
@@ -121,18 +123,19 @@
121
123
  colSel.$wrapper = [];
122
124
  colSel.$checkbox = [];
123
125
  // populate the selector container
124
- c.$table.children('thead').find('tr:first th', c.table).each(function() {
125
- var $this = $(this),
126
- // if no data-priority is assigned, default to 1, but don't remove it from the selector list
127
- priority = $this.attr(wo.columnSelector_priority) || 1,
128
- colId = $this.attr('data-column'),
129
- state = ts.getData(this, c.headers[colId], 'columnSelector');
126
+ for ( index = 0; index < c.columns; index++ ) {
127
+ $header = c.$headerIndexed[ index ];
128
+ // if no data-priority is assigned, default to 1, but don't remove it from the selector list
129
+ priority = $header.attr(wo.columnSelector_priority) || 1;
130
+ colId = $header.attr('data-column');
131
+ col = ts.getColumnData( c.table, c.headers, colId );
132
+ state = ts.getData( $header, col, 'columnSelector');
130
133
 
131
134
  // if this column not hidable at all
132
135
  // include getData check (includes 'columnSelector-false' class, data attribute, etc)
133
136
  if ( isNaN(priority) && priority.length > 0 || state === 'disable' ||
134
137
  ( wo.columnSelector_columns[colId] && wo.columnSelector_columns[colId] === 'disable') ) {
135
- return true; // goto next
138
+ continue; // goto next
136
139
  }
137
140
 
138
141
  // set default state; storage takes priority
@@ -142,7 +145,7 @@
142
145
  colSel.$column[colId] = $(this);
143
146
 
144
147
  // set default col title
145
- name = $this.attr(wo.columnSelector_name) || $this.text();
148
+ name = $header.attr(wo.columnSelector_name) || $header.text();
146
149
  if ($container.length) {
147
150
  colSel.$wrapper[colId] = $(wo.columnSelector_layout.replace(/\{name\}/g, name)).appendTo($container);
148
151
  colSel.$checkbox[colId] = colSel.$wrapper[colId]
@@ -152,15 +155,17 @@
152
155
  .toggleClass( wo.columnSelector_cssChecked, colSel.states[colId] )
153
156
  .prop('checked', colSel.states[colId])
154
157
  .on('change', function(){
155
- colSel.states[colId] = this.checked;
158
+ // ensure states is accurate
159
+ var colId = $(this).attr('data-column');
160
+ c.selector.states[colId] = this.checked;
156
161
  tsColSel.updateCols(c, wo);
157
162
  }).change();
158
163
  }
159
- });
164
+ }
160
165
 
161
166
  },
162
167
 
163
- setupBreakpoints: function(c, wo){
168
+ setupBreakpoints: function(c, wo) {
164
169
  var colSel = c.selector;
165
170
 
166
171
  // add responsive breakpoints
@@ -222,14 +227,25 @@
222
227
  if (wo.columnSelector_saveColumns && ts.storage) {
223
228
  ts.storage( c.$table[0], 'tablesorter-columnSelector-auto', { auto : colSel.auto } );
224
229
  }
230
+ tsColSel.adjustColspans( c, wo );
225
231
  // trigger columnUpdate if auto is true (it gets skipped in updateCols()
226
232
  if (colSel.auto) {
227
233
  c.$table.trigger(wo.columnSelector_updated);
228
234
  }
229
235
  },
230
-
236
+ addSelectors: function( prefix, column ) {
237
+ var array = [],
238
+ temp = ' col:nth-child(' + column + ')';
239
+ array.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
240
+ temp = ' tr th:nth-child(' + column + ')';
241
+ array.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
242
+ temp = ' tr td:nth-child(' + column + ')';
243
+ array.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
244
+ return array;
245
+ },
231
246
  updateBreakpoints: function(c, wo) {
232
- var priority, column, breaks, temp,
247
+ var priority, col, column, breaks,
248
+ isHidden = [],
233
249
  colSel = c.selector,
234
250
  prefix = c.namespace + 'columnselector',
235
251
  mediaAll = [],
@@ -239,19 +255,27 @@
239
255
  colSel.$style.prop('disabled', false);
240
256
  return;
241
257
  }
242
-
258
+ if (wo.columnSelector_mediaqueryHidden) {
259
+ // add columns to be hidden; even when "auto" is set - see #964
260
+ for ( column = 0; column < c.columns; column++ ) {
261
+ col = ts.getColumnData( c.table, c.headers, column );
262
+ isHidden[ column + 1 ] = ts.getData( c.$headerIndexed[ column ], col, 'columnSelector' ) === 'false';
263
+ if ( isHidden[ column + 1 ] ) {
264
+ // hide columnSelector false column (in auto mode)
265
+ mediaAll = mediaAll.concat( tsColSel.addSelectors( prefix, column + 1 ) );
266
+ }
267
+ }
268
+ }
243
269
  // only 6 breakpoints (same as jQuery Mobile)
244
270
  for (priority = 0; priority < 6; priority++){
245
271
  /*jshint loopfunc:true */
246
272
  breaks = [];
247
273
  c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){
248
274
  column = parseInt($(this).attr('data-column'), 10) + 1;
249
- temp = ' col:nth-child(' + column + ')';
250
- breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
251
- temp = ' tr th:nth-child(' + column + ')';
252
- breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
253
- temp = ' tr td:nth-child(' + column + ')';
254
- breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
275
+ // don't reveal columnSelector false columns
276
+ if ( !isHidden[ column ] ) {
277
+ breaks = breaks.concat( tsColSel.addSelectors( prefix, column ) );
278
+ }
255
279
  });
256
280
  if (breaks.length) {
257
281
  mediaAll = mediaAll.concat( breaks );
@@ -269,24 +293,18 @@
269
293
  .html( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts );
270
294
  }
271
295
  },
272
-
273
296
  updateCols: function(c, wo) {
274
297
  if (wo.columnSelector_mediaquery && c.selector.auto || c.selector.isInitializing) {
275
298
  return;
276
299
  }
277
- var column, temp,
300
+ var column,
278
301
  colSel = c.selector,
279
302
  styles = [],
280
303
  prefix = c.namespace + 'columnselector';
281
304
  colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){
282
305
  if (!this.checked) {
283
306
  column = parseInt( $(this).attr('data-column'), 10 ) + 1;
284
- temp = ' col:nth-child(' + column + ')';
285
- styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
286
- temp = ' tr th:nth-child(' + column + ')';
287
- styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
288
- temp = ' tr td:nth-child(' + column + ')';
289
- styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
307
+ styles = styles.concat( tsColSel.addSelectors( prefix, column ) );
290
308
  }
291
309
  $(this).toggleClass( wo.columnSelector_cssChecked, this.checked );
292
310
  });
@@ -299,9 +317,71 @@
299
317
  if (wo.columnSelector_saveColumns && ts.storage) {
300
318
  ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states );
301
319
  }
320
+ tsColSel.adjustColspans( c, wo );
302
321
  c.$table.trigger(wo.columnSelector_updated);
303
322
  },
304
323
 
324
+ setUpColspan: function(c, wo) {
325
+ var index, span, nspace,
326
+ $window = $( window ),
327
+ hasSpans = false,
328
+ $cells = c.$table
329
+ .add( $(c.namespace + '_extra_table') )
330
+ .children('thead, tfoot')
331
+ .children('tr')
332
+ .children('th, td'),
333
+ len = $cells.length;
334
+ for ( index = 0; index < len; index++ ) {
335
+ span = $cells[ index ].colSpan;
336
+ if ( span > 1 ) {
337
+ hasSpans = true;
338
+ $cells.eq( index )
339
+ .addClass( c.namespace.slice( 1 ) + 'columnselectorHasSpan' )
340
+ .attr( 'data-col-span', span );
341
+ }
342
+ }
343
+ // only add resize end if using media queries
344
+ if ( hasSpans && wo.columnSelector_mediaquery ) {
345
+ nspace = c.namespace.slice( 1 ) + 'columnselector';
346
+ // Setup window.resizeEnd event
347
+ $window
348
+ .off( nspace )
349
+ .on( 'resize' + nspace, ts.window_resize )
350
+ .on( 'resizeEnd' + nspace, function() {
351
+ // IE calls resize when you modify content, so we have to unbind the resize event
352
+ // so we don't end up with an infinite loop. we can rebind after we're done.
353
+ $window.off( 'resize' + nspace, ts.window_resize );
354
+ tsColSel.adjustColspans( c, wo );
355
+ $window.on( 'resize' + nspace, ts.window_resize );
356
+ });
357
+ }
358
+ },
359
+ adjustColspans: function(c, wo) {
360
+ var index, cols, col, span, end,
361
+ colSel = c.selector,
362
+ autoModeOn = colSel.auto,
363
+ $colspans = $( c.namespace + 'columnselectorHasSpan' ),
364
+ len = $colspans.length;
365
+ if ( len ) {
366
+ for ( index = 0; index < len; index++ ) {
367
+ col = parseInt( $colspans.eq(index).attr('data-column'), 10 );
368
+ span = parseInt( $colspans.eq(index).attr('data-col-span'), 10 );
369
+ end = col + span;
370
+ for ( cols = col; cols < end; cols++ ) {
371
+ if ( !autoModeOn && colSel.states[ cols ] === false ||
372
+ autoModeOn && c.$headerIndexed[ cols ] && !c.$headerIndexed[ cols ].is(':visible') ) {
373
+ span--;
374
+ }
375
+ }
376
+ if ( span ) {
377
+ $colspans.eq(index).show()[0].colSpan = span;
378
+ } else {
379
+ $colspans.eq(index).hide();
380
+ }
381
+ }
382
+ }
383
+ },
384
+
305
385
  attachTo : function(table, elm) {
306
386
  table = $(table)[0];
307
387
  var colSel, wo, indx,
@@ -336,6 +416,16 @@
336
416
 
337
417
  };
338
418
 
419
+ /* Add window resizeEnd event (also used by scroller widget) */
420
+ ts.window_resize = function() {
421
+ if ( ts.timer_resize ) {
422
+ clearTimeout( ts.timer_resize );
423
+ }
424
+ ts.timer_resize = setTimeout( function() {
425
+ $( window ).trigger( 'resizeEnd' );
426
+ }, 250 );
427
+ };
428
+
339
429
  ts.addWidget({
340
430
  id: 'columnSelector',
341
431
  priority: 10,
@@ -360,6 +450,8 @@
360
450
  columnSelector_mediaqueryName: 'Auto: ',
361
451
  // breakpoints checkbox initial setting
362
452
  columnSelector_mediaqueryState: true,
453
+ // hide columnSelector false columns while in auto mode
454
+ columnSelector_mediaqueryHidden: false,
363
455
  // responsive table hides columns with priority 1-6 at these breakpoints
364
456
  // see http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/#Applyingapresetbreakpoint
365
457
  // *** set to false to disable ***
@@ -1,4 +1,4 @@
1
- /*! Widget: editable - updated 8/17/2015 (v2.23.0) *//*
1
+ /*! Widget: editable - updated 10/31/2015 (v2.24.0) *//*
2
2
  * Requires tablesorter v2.8+ and jQuery 1.7+
3
3
  * by Rob Garrison
4
4
  */
@@ -193,7 +193,9 @@
193
193
  }
194
194
  if ( e.which === 27 ) {
195
195
  // user cancelled
196
- $this.html( $this.data( 'original' ) ).trigger( 'blur' + namespace );
196
+ $this
197
+ .html( $this.data( 'original' ) )
198
+ .trigger( 'blur' + namespace );
197
199
  c.$table.data( 'contentFocused', false );
198
200
  c.table.isUpdating = false;
199
201
  return false;
@@ -220,17 +222,17 @@
220
222
  .data( 'before', valid )
221
223
  .data( 'original', valid )
222
224
  .trigger( 'change' );
223
- c.$table.trigger( 'updateCell', [ $this.closest( 'td' ), false, function() {
225
+ $.tablesorter.updateCell( c, $this.closest( 'td' ), false, function() {
224
226
  if ( wo.editable_autoResort ) {
225
227
  setTimeout( function() {
226
- c.$table.trigger( 'sorton', [ c.sortList, function() {
228
+ $.tablesorter.sortOn( c, c.sortList, function() {
227
229
  tse.editComplete( c, wo, c.$table.data( 'contentFocused' ), true );
228
- }, true ] );
230
+ }, true );
229
231
  }, 10 );
230
232
  } else {
231
233
  tse.editComplete( c, wo, c.$table.data( 'contentFocused' ) );
232
234
  }
233
- } ] );
235
+ });
234
236
  return false;
235
237
  }
236
238
  } else if ( !valid && e.type !== 'keydown' ) {
@@ -1,4 +1,4 @@
1
- /*! Widget: filter - updated 10/4/2015 (v2.23.5) *//*
1
+ /*! Widget: filter - updated 10/31/2015 (v2.24.0) *//*
2
2
  * Requires tablesorter v2.8+ and jQuery 1.7+
3
3
  * by Rob Garrison
4
4
  */
@@ -567,8 +567,9 @@
567
567
  // $cell parameter, but not the config, is passed to the filter_formatters,
568
568
  // so we have to work with it instead
569
569
  formatterUpdated: function( $cell, column ) {
570
- var wo = $cell.closest( 'table' )[0].config.widgetOptions;
571
- if ( !wo.filter_initialized ) {
570
+ // prevent error if $cell is undefined - see #1056
571
+ var wo = $cell && $cell.closest( 'table' )[0].config.widgetOptions;
572
+ if ( wo && !wo.filter_initialized ) {
572
573
  // add updates by column since this function
573
574
  // may be called numerous times before initialization
574
575
  wo.filter_formatterInit[ column ] = 1;
@@ -605,6 +606,16 @@
605
606
  }
606
607
  }
607
608
  },
609
+ // encode or decode filters for storage; see #1026
610
+ processFilters: function( filters, encode ) {
611
+ var indx,
612
+ mode = encode ? encodeURIComponent : decodeURIComponent,
613
+ len = filters.length;
614
+ for ( indx = 0; indx < len; indx++ ) {
615
+ filters[ indx ] = mode( filters[ indx ] );
616
+ }
617
+ return filters;
618
+ },
608
619
  setDefaults: function( table, c, wo ) {
609
620
  var isArray, saved, indx, col, $filters,
610
621
  // get current ( default ) filters
@@ -614,7 +625,7 @@
614
625
  isArray = $.isArray( saved );
615
626
  // make sure we're not just getting an empty array
616
627
  if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) {
617
- filters = saved;
628
+ filters = tsf.processFilters( saved );
618
629
  }
619
630
  }
620
631
  // if no filters saved, then check default settings
@@ -637,71 +648,81 @@
637
648
  return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter;
638
649
  },
639
650
  buildRow: function( table, c, wo ) {
640
- var col, column, $header, makeSelect, disabled, name, ffxn, tmp,
651
+ var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp,
641
652
  // c.columns defined in computeThIndexes()
642
653
  cellFilter = wo.filter_cellFilter,
643
654
  columns = c.columns,
644
655
  arry = $.isArray( cellFilter ),
645
656
  buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">';
646
657
  for ( column = 0; column < columns; column++ ) {
647
- buildFilter += '<td';
648
- if ( arry ) {
649
- buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' );
650
- } else {
651
- buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' );
658
+ if ( c.$headerIndexed[ column ].length ) {
659
+ buildFilter += '<td data-column="' + column + '"';
660
+ // account for entire column set with colspan. See #1047
661
+ tmp = c.$headerIndexed[ column ] && c.$headerIndexed[ column ][0].colSpan || 0;
662
+ if ( tmp > 1 ) {
663
+ buildFilter += ' colspan="' + tmp + '"';
664
+ }
665
+ if ( arry ) {
666
+ buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' );
667
+ } else {
668
+ buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' );
669
+ }
670
+ buildFilter += '></td>';
652
671
  }
653
- buildFilter += '></td>';
654
672
  }
655
673
  c.$filters = $( buildFilter += '</tr>' )
656
674
  .appendTo( c.$table.children( 'thead' ).eq( 0 ) )
657
- .find( 'td' );
675
+ .children( 'td' );
658
676
  // build each filter input
659
677
  for ( column = 0; column < columns; column++ ) {
660
678
  disabled = false;
661
679
  // assuming last cell of a column is the main column
662
680
  $header = c.$headerIndexed[ column ];
663
- ffxn = ts.getColumnData( table, wo.filter_functions, column );
664
- makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) ||
665
- $header.hasClass( 'filter-select' );
666
- // get data from jQuery data, metadata, headers option or header class name
667
- col = ts.getColumnData( table, c.headers, column );
668
- disabled = ts.getData( $header[0], col, 'filter' ) === 'false' ||
669
- ts.getData( $header[0], col, 'parser' ) === 'false';
681
+ if ( $header && $header.length ) {
682
+ $filter = c.$filters.filter( '[data-column="' + column + '"]' );
683
+ ffxn = ts.getColumnData( table, wo.filter_functions, column );
684
+ makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) ||
685
+ $header.hasClass( 'filter-select' );
686
+ // get data from jQuery data, metadata, headers option or header class name
687
+ col = ts.getColumnData( table, c.headers, column );
688
+ disabled = ts.getData( $header[0], col, 'filter' ) === 'false' ||
689
+ ts.getData( $header[0], col, 'parser' ) === 'false';
670
690
 
671
- if ( makeSelect ) {
672
- buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) );
673
- } else {
674
- ffxn = ts.getColumnData( table, wo.filter_formatter, column );
675
- if ( ffxn ) {
676
- wo.filter_formatterCount++;
677
- buildFilter = ffxn( c.$filters.eq( column ), column );
678
- // no element returned, so lets go find it
679
- if ( buildFilter && buildFilter.length === 0 ) {
680
- buildFilter = c.$filters.eq( column ).children( 'input' );
691
+ if ( makeSelect ) {
692
+ buildFilter = $( '<select>' ).appendTo( $filter );
693
+ } else {
694
+ ffxn = ts.getColumnData( table, wo.filter_formatter, column );
695
+ if ( ffxn ) {
696
+ wo.filter_formatterCount++;
697
+ buildFilter = ffxn( $filter, column );
698
+ // no element returned, so lets go find it
699
+ if ( buildFilter && buildFilter.length === 0 ) {
700
+ buildFilter = $filter.children( 'input' );
701
+ }
702
+ // element not in DOM, so lets attach it
703
+ if ( buildFilter && ( buildFilter.parent().length === 0 ||
704
+ ( buildFilter.parent().length && buildFilter.parent()[0] !== $filter[0] ) ) ) {
705
+ $filter.append( buildFilter );
706
+ }
707
+ } else {
708
+ buildFilter = $( '<input type="search">' ).appendTo( $filter );
681
709
  }
682
- // element not in DOM, so lets attach it
683
- if ( buildFilter && ( buildFilter.parent().length === 0 ||
684
- ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) {
685
- c.$filters.eq( column ).append( buildFilter );
710
+ if ( buildFilter ) {
711
+ tmp = $header.data( 'placeholder' ) ||
712
+ $header.attr( 'data-placeholder' ) ||
713
+ wo.filter_placeholder.search || '';
714
+ buildFilter.attr( 'placeholder', tmp );
686
715
  }
687
- } else {
688
- buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) );
689
716
  }
690
717
  if ( buildFilter ) {
691
- tmp = $header.data( 'placeholder' ) ||
692
- $header.attr( 'data-placeholder' ) ||
693
- wo.filter_placeholder.search || '';
694
- buildFilter.attr( 'placeholder', tmp );
695
- }
696
- }
697
- if ( buildFilter ) {
698
- // add filter class name
699
- name = ( $.isArray( wo.filter_cssFilter ) ?
700
- ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) :
701
- wo.filter_cssFilter ) || '';
702
- buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column );
703
- if ( disabled ) {
704
- buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true;
718
+ // add filter class name
719
+ name = ( $.isArray( wo.filter_cssFilter ) ?
720
+ ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) :
721
+ wo.filter_cssFilter ) || '';
722
+ buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column );
723
+ if ( disabled ) {
724
+ buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true;
725
+ }
705
726
  }
706
727
  }
707
728
  }
@@ -791,9 +812,9 @@
791
812
  if ( $.isEmptyObject( c.cache ) ) {
792
813
  // update cache if delayInit set & pager has initialized ( after user initiates a search )
793
814
  if ( c.delayInit && c.pager && c.pager.initialized ) {
794
- c.$table.trigger( 'updateCache', [ function() {
815
+ ts.updateCache( c, function() {
795
816
  tsf.checkFilters( table, false, skipFirst );
796
- } ] );
817
+ });
797
818
  }
798
819
  return;
799
820
  }
@@ -831,10 +852,10 @@
831
852
  return false;
832
853
  }
833
854
  },
834
- hideFilters: function( c ) {
835
- var timer;
836
- c.$table
837
- .find( '.' + tscss.filterRow )
855
+ hideFilters: function( c, $table ) {
856
+ var timer,
857
+ $row = ( $table || c.$table ).find( '.' + tscss.filterRow ).addClass( tscss.filterRowHide );
858
+ $row
838
859
  .bind( 'mouseenter mouseleave', function( e ) {
839
860
  // save event object - http://bugs.jquery.com/ticket/12140
840
861
  var event = e,
@@ -1251,7 +1272,7 @@
1251
1272
  // ( '> -10' => '> -100' will ignore hidden rows )
1252
1273
  !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) &&
1253
1274
  // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593
1254
- !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length &&
1275
+ !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length &&
1255
1276
  !c.$headerIndexed[indx].hasClass( 'filter-match' ) );
1256
1277
  }
1257
1278
  }
@@ -1358,7 +1379,7 @@
1358
1379
  c.lastSearch = storedFilters;
1359
1380
  c.$table.data( 'lastSearch', storedFilters );
1360
1381
  if ( wo.filter_saveFilters && ts.storage ) {
1361
- ts.storage( table, 'tablesorter-filters', storedFilters );
1382
+ ts.storage( table, 'tablesorter-filters', tsf.processFilters( storedFilters, true ) );
1362
1383
  }
1363
1384
  if ( c.debug ) {
1364
1385
  console.log( 'Completed filter widget search' + ts.benchmark(time) );
@@ -1367,7 +1388,7 @@
1367
1388
  c.$table.trigger( 'filterEnd', c );
1368
1389
  }
1369
1390
  setTimeout( function() {
1370
- c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied
1391
+ ts.applyWidget( c.table ); // make sure zebra widget is applied
1371
1392
  }, 0 );
1372
1393
  },
1373
1394
  getOptionSource: function( table, column, onlyAvail ) {