jquery-tablesorter 1.10.10 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1591f84710f85ceb7162d750a39df47356371b14
4
- data.tar.gz: 1be6e92aa78bf994599a91a5c8ce4107442800fd
3
+ metadata.gz: 618934fed3abb717299bc8c54013a3b1d3d53863
4
+ data.tar.gz: afbd307142ac50a3577cee070ae22a9f8b1a2bea
5
5
  SHA512:
6
- metadata.gz: 6f844b8b9ea9af7ccdc7389b55e69ed24f727a7c565355de3a9212552400a1327498fd82466ca3deb5c537a3beee49d931833c8a31724a3bb2b23078901d9181
7
- data.tar.gz: 8eb94e20072ac16362d837190fd8407c50d17b290db3b46ea037549aab87e338ba1836068ac24279622250e372d030f5c2a075d33b973d496ebb8e17dc7d9fd8
6
+ metadata.gz: b5fd391890c8feef67fba3c66f00f9e9f1f45d2bc1bc78fad870fa2db6d10c54ce0b68269ce4afcb8c04fb5d5b0b402d9c086add0085b53d94ec4905570cb758
7
+ data.tar.gz: e29cceab65caa5c1a248d1b90ae61cdc46364dbadb9be1ae68e01097fe34428b8f4e462928452b98f1f659b41c7a472aa90df1defffcba228d237ae356cf2503
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.15.14 (4/10/2014), [documentation]
7
+ Current tablesorter version: 2.16.1 (4/24/2014), [documentation]
8
8
 
9
9
  Any issue associate with the js/css files, please report to [Mottie's fork].
10
10
 
@@ -1,3 +1,3 @@
1
1
  module JqueryTablesorter
2
- VERSION = '1.10.10'
2
+ VERSION = '1.11.0'
3
3
  end
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * tablesorter pager plugin
3
- * updated 4/10/2014 (v2.15.14)
3
+ * updated 4/23/2014 (v2.16.0)
4
4
  */
5
5
  /*jshint browser:true, jquery:true, unused:false */
6
6
  ;(function($) {
@@ -58,6 +58,10 @@
58
58
  // starting page of the pager (zero based index)
59
59
  page: 0,
60
60
 
61
+ // reset pager after filtering; set to desired page #
62
+ // set to false to not change page at filter start
63
+ pageReset: 0,
64
+
61
65
  // Number of visible rows
62
66
  size: 10,
63
67
 
@@ -125,15 +129,23 @@
125
129
  },
126
130
 
127
131
  updatePageDisplay = function(table, p, completed) {
128
- var i, pg, s, out,
132
+ var i, pg, s, out, regex,
129
133
  c = table.config,
130
134
  f = c.$table.hasClass('hasFilters') && !p.ajaxUrl,
131
- t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove +
132
- (p.countChildRows ? '' : ',.' + c.cssChildRow),
135
+ t = [],
133
136
  sz = p.size || 10; // don't allow dividing by zero
137
+ t = [ (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered'), c.selectorRemove ];
138
+ if (p.countChildRows) { t.push(c.cssChildRow); }
139
+ regex = new RegExp( '(' + t.join('|') + ')' );
134
140
  p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method
135
- p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr').not('.' + t ).length : p.totalRows;
136
- p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages;
141
+ p.filteredRows = (f) ? 0 : p.totalRows;
142
+ p.filteredPages = p.totalPages;
143
+ if (f) {
144
+ $.each(c.cache[0].normalized, function(i, el) {
145
+ p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1;
146
+ });
147
+ p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0;
148
+ }
137
149
  if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) {
138
150
  t = (p.size * p.page > p.filteredRows);
139
151
  p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1);
@@ -448,12 +460,13 @@
448
460
  },
449
461
 
450
462
  renderTable = function(table, rows, p) {
451
- var i, $tb,
463
+ var $tb, index, count, added,
452
464
  $t = $(table),
453
465
  c = table.config,
466
+ f = c.$table.hasClass('hasFilters'),
454
467
  l = rows && rows.length || 0, // rows may be undefined
455
468
  s = ( p.page * p.size ),
456
- e = ( s + p.size );
469
+ e = p.size;
457
470
  if ( l < 1 ) { return; } // empty table, abort!
458
471
  if ( p.page >= p.totalPages ) {
459
472
  // lets not render the table more than once
@@ -465,18 +478,25 @@
465
478
  if ( !p.removeRows ) {
466
479
  hideRows(table, p);
467
480
  } else {
468
- if ( e > rows.length ) {
469
- e = rows.length;
470
- }
471
481
  ts.clearTableBody(table);
472
482
  $tb = ts.processTbody(table, c.$tbodies.eq(0), true);
473
- for ( i = s; i < e; i++ ) {
474
- $tb.append(rows[i]);
483
+ // not filtered, start from the calculated starting point (s)
484
+ // if filtered, start from zero
485
+ index = f ? 0 : s;
486
+ count = f ? 0 : s;
487
+ added = 0;
488
+ while (added < e && index < rows.length) {
489
+ if (!f || !/filtered/.test(rows[index][0].className)){
490
+ count++;
491
+ if (count > s && added <= e) {
492
+ added++;
493
+ $tb.append(rows[index]);
494
+ }
495
+ }
496
+ index++;
475
497
  }
476
-
477
498
  ts.processTbody(table, $tb, false);
478
499
  }
479
-
480
500
  updatePageDisplay(table, p);
481
501
  if ( !p.isDisabled ) { fixHeight(table, p); }
482
502
  $t.trigger('applyWidgets');
@@ -594,6 +614,7 @@
594
614
  p.$container.hide(); // hide pager
595
615
  table.config.appender = null; // remove pager appender function
596
616
  p.initialized = false;
617
+ delete table.config.rowsCopy;
597
618
  $(table).unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager');
598
619
  if (ts.storage) {
599
620
  ts.storage(table, p.storageKey, '');
@@ -647,7 +668,8 @@
647
668
  var t, ctrls, fxn,
648
669
  table = this,
649
670
  c = table.config,
650
- p = c.pager = $.extend( {}, $.tablesorterPager.defaults, settings ),
671
+ wo = c.widgetOptions,
672
+ p = c.pager = $.extend( true, {}, $.tablesorterPager.defaults, settings ),
651
673
  $t = c.$table,
652
674
  // added in case the pager is reinitialized after being destroyed.
653
675
  pager = p.$container = $(p.container).addClass('tablesorter-pager').show();
@@ -669,17 +691,24 @@
669
691
  $.data(table, 'pagerLastSize', p.size);
670
692
  }
671
693
 
694
+ // skipped rows
695
+ p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.substring(1) + '|' + c.cssChildRow + ')');
696
+
672
697
  $t
673
698
  .unbind('filterStart filterEnd sortEnd disable enable destroy update updateRows updateAll addRows pageSize '.split(' ').join('.pager '))
674
699
  .bind('filterStart.pager', function(e, filters) {
675
700
  p.currentFilters = filters;
676
- p.page = 0; // fixes #456
701
+ // don't change page is filters are the same (pager updating, etc)
702
+ if (p.pageReset !== false && (c.lastCombinedFilter || '') !== (filters || []).join('')) {
703
+ p.page = p.pageReset; // fixes #456 & #565
704
+ }
677
705
  })
678
706
  // update pager after filter widget completes
679
707
  .bind('filterEnd.pager sortEnd.pager', function() {
680
708
  if (p.initialized) {
681
- moveToPage(table, p, false);
709
+ // update page display first, so we update p.filteredPages
682
710
  updatePageDisplay(table, p, false);
711
+ moveToPage(table, p, false);
683
712
  fixHeight(table, p);
684
713
  }
685
714
  })
@@ -1,5 +1,5 @@
1
1
  /**!
2
- * TableSorter 2.15.14 - Client-side table sorting with ease!
2
+ * TableSorter 2.16.1 - Client-side table sorting with ease!
3
3
  * @requires jQuery v1.2.6+
4
4
  *
5
5
  * Copyright (c) 2007 Christian Bach
@@ -24,7 +24,7 @@
24
24
 
25
25
  var ts = this;
26
26
 
27
- ts.version = "2.15.14";
27
+ ts.version = "2.16.1";
28
28
 
29
29
  ts.parsers = [];
30
30
  ts.widgets = [];
@@ -64,7 +64,8 @@
64
64
 
65
65
  emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero
66
66
  stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero
67
- textExtraction : 'simple', // text extraction method/function - function(node, table, cellIndex){}
67
+ textExtraction : 'basic', // text extraction method/function - function(node, table, cellIndex){}
68
+ textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in textExtraction function)
68
69
  textSorter : null, // choose overall or specific column sorter function(a, b, direction, table, columnIndex) [alt: ts.sortText]
69
70
  numberSorter : null, // choose overall numeric sorter function(a, b, direction, maxColumnValue)
70
71
 
@@ -167,20 +168,19 @@
167
168
  function getElementText(table, node, cellIndex) {
168
169
  if (!node) { return ""; }
169
170
  var c = table.config,
170
- t = c.textExtraction, text = "";
171
- if (t === "simple") {
172
- if (c.supportsTextContent) {
173
- text = node.textContent; // newer browsers support this
174
- } else {
175
- text = $(node).text();
176
- }
171
+ t = c.textExtraction || '',
172
+ text = "";
173
+ if (t === "basic") {
174
+ // check data-attribute first
175
+ text = $(node).attr(c.textAttribute) || node.textContent || node.innerText || $(node).text() || "";
177
176
  } else {
178
- if (typeof t === "function") {
177
+ if (typeof(t) === "function") {
179
178
  text = t(node, table, cellIndex);
180
- } else if (typeof t === "object" && t.hasOwnProperty(cellIndex)) {
179
+ } else if (typeof(t) === "object" && t.hasOwnProperty(cellIndex)) {
181
180
  text = t[cellIndex](node, table, cellIndex);
182
181
  } else {
183
- text = c.supportsTextContent ? node.textContent : $(node).text();
182
+ // previous "simple" method
183
+ text = node.textContent || node.innerText || $(node).text() || "";
184
184
  }
185
185
  }
186
186
  return $.trim(text);
@@ -219,41 +219,47 @@
219
219
  var c = table.config,
220
220
  // update table bodies in case we start with an empty table
221
221
  tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'),
222
- rows, list, l, i, h, ch, p, time, parsersDebug = "";
223
- if ( tb.length === 0) {
222
+ rows, list, l, i, h, ch, p, time,
223
+ j = 0,
224
+ parsersDebug = "",
225
+ len = tb.length;
226
+ if ( len === 0) {
224
227
  return c.debug ? log('Warning: *Empty table!* Not building a parser cache') : '';
225
228
  } else if (c.debug) {
226
229
  time = new Date();
227
230
  log('Detecting parsers for each column');
228
231
  }
229
- rows = tb[0].rows;
230
- if (rows[0]) {
231
- list = [];
232
- l = rows[0].cells.length;
233
- for (i = 0; i < l; i++) {
234
- // tons of thanks to AnthonyM1229 for working out the following selector (issue #74) to make this work in IE8!
235
- // More fixes to this selector to work properly in iOS and jQuery 1.8+ (issue #132 & #174)
236
- h = c.$headers.filter(':not([colspan])');
237
- h = h.add( c.$headers.filter('[colspan="1"]') ) // ie8 fix
238
- .filter('[data-column="' + i + '"]:last');
239
- ch = c.headers[i];
240
- // get column parser
241
- p = ts.getParserById( ts.getData(h, ch, 'sorter') );
242
- // empty cells behaviour - keeping emptyToBottom for backwards compatibility
243
- c.empties[i] = ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' );
244
- // text strings behaviour in numerical sorts
245
- c.strings[i] = ts.getData(h, ch, 'string') || c.stringTo || 'max';
246
- if (!p) {
247
- p = detectParserForColumn(table, rows, -1, i);
248
- }
249
- if (c.debug) {
250
- parsersDebug += "column:" + i + "; parser:" + p.id + "; string:" + c.strings[i] + '; empty: ' + c.empties[i] + "\n";
232
+ list = [];
233
+ while (j < len) {
234
+ rows = tb[j].rows;
235
+ if (rows[j]) {
236
+ l = rows[j].cells.length;
237
+ for (i = 0; i < l; i++) {
238
+ // tons of thanks to AnthonyM1229 for working out the following selector (issue #74) to make this work in IE8!
239
+ // More fixes to this selector to work properly in iOS and jQuery 1.8+ (issue #132 & #174)
240
+ h = c.$headers.filter(':not([colspan])');
241
+ h = h.add( c.$headers.filter('[colspan="1"]') ) // ie8 fix
242
+ .filter('[data-column="' + i + '"]:last');
243
+ ch = c.headers[i];
244
+ // get column parser
245
+ p = ts.getParserById( ts.getData(h, ch, 'sorter') );
246
+ // empty cells behaviour - keeping emptyToBottom for backwards compatibility
247
+ c.empties[i] = ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' );
248
+ // text strings behaviour in numerical sorts
249
+ c.strings[i] = ts.getData(h, ch, 'string') || c.stringTo || 'max';
250
+ if (!p) {
251
+ p = detectParserForColumn(table, rows, -1, i);
252
+ }
253
+ if (c.debug) {
254
+ parsersDebug += "column:" + i + "; parser:" + p.id + "; string:" + c.strings[i] + '; empty: ' + c.empties[i] + "\n";
255
+ }
256
+ list.push(p);
251
257
  }
252
- list.push(p);
253
258
  }
259
+ j += (list.length) ? len : 1;
254
260
  }
255
261
  if (c.debug) {
256
- log(parsersDebug);
262
+ log(parsersDebug ? parsersDebug : "No parsers detected");
257
263
  benchmark("Completed detecting parsers", time);
258
264
  }
259
265
  c.parsers = list;
@@ -261,72 +267,87 @@
261
267
 
262
268
  /* utils */
263
269
  function buildCache(table) {
264
- var b = table.tBodies,
265
- tc = table.config,
266
- totalRows,
267
- totalCells,
268
- parsers = tc.parsers,
269
- t, v, i, j, k, c, cols, cacheTime, colMax = [];
270
- tc.cache = {};
270
+ var cc, t, v, i, j, k, $row, rows, cols, cacheTime,
271
+ totalRows, rowData, colMax,
272
+ c = table.config,
273
+ $tb = c.$table.children('tbody'),
274
+ parsers = c.parsers;
275
+ c.cache = {};
271
276
  // if no parsers found, return - it's an empty table.
272
277
  if (!parsers) {
273
- return tc.debug ? log('Warning: *Empty table!* Not building a cache') : '';
278
+ return c.debug ? log('Warning: *Empty table!* Not building a cache') : '';
274
279
  }
275
- if (tc.debug) {
280
+ if (c.debug) {
276
281
  cacheTime = new Date();
277
282
  }
278
283
  // processing icon
279
- if (tc.showProcessing) {
284
+ if (c.showProcessing) {
280
285
  ts.isProcessing(table, true);
281
286
  }
282
- for (k = 0; k < b.length; k++) {
283
- tc.cache[k] = { row: [], normalized: [] };
287
+ for (k = 0; k < $tb.length; k++) {
288
+ colMax = []; // column max value per tbody
289
+ cc = c.cache[k] = {
290
+ normalized: [] // array of normalized row data; last entry contains "rowData" above
291
+ // colMax: # // added at the end
292
+ };
293
+
284
294
  // ignore tbodies with class name from c.cssInfoBlock
285
- if (!$(b[k]).hasClass(tc.cssInfoBlock)) {
286
- totalRows = (b[k] && b[k].rows.length) || 0;
287
- totalCells = (b[k].rows[0] && b[k].rows[0].cells.length) || 0;
295
+ if (!$tb.eq(k).hasClass(c.cssInfoBlock)) {
296
+ totalRows = ($tb[k] && $tb[k].rows.length) || 0;
288
297
  for (i = 0; i < totalRows; ++i) {
298
+ rowData = {
299
+ // order: original row order #
300
+ // $row : jQuery Object[]
301
+ child: [] // child row text (filter widget)
302
+ };
289
303
  /** Add the table data to main data array */
290
- c = $(b[k].rows[i]);
304
+ $row = $($tb[k].rows[i]);
305
+ rows = [ new Array(c.columns) ];
291
306
  cols = [];
292
307
  // if this is a child row, add it to the last row's children and continue to the next row
293
308
  // ignore child row class, if it is the first row
294
- if (c.hasClass(tc.cssChildRow) && i !== 0) {
295
- tc.cache[k].row[tc.cache[k].row.length - 1] = tc.cache[k].row[tc.cache[k].row.length - 1].add(c);
309
+ if ($row.hasClass(c.cssChildRow) && i !== 0) {
310
+ t = cc.normalized.length - 1;
311
+ cc.normalized[t][c.columns].$row = cc.normalized[t][c.columns].$row.add($row);
296
312
  // add "hasChild" class name to parent row
297
- if (!c.prev().hasClass(tc.cssChildRow)) {
298
- c.prev().addClass(ts.css.cssHasChild);
313
+ if (!$row.prev().hasClass(c.cssChildRow)) {
314
+ $row.prev().addClass(ts.css.cssHasChild);
299
315
  }
316
+ // save child row content (un-parsed!)
317
+ rowData.child[t] = $.trim( $row[0].textContent || $row[0].innerText || $row.text() || "" );
300
318
  // go to the next for loop
301
319
  continue;
302
320
  }
303
- tc.cache[k].row.push(c);
304
- for (j = 0; j < totalCells; ++j) {
321
+ rowData.$row = $row;
322
+ rowData.order = i; // add original row position to rowCache
323
+ for (j = 0; j < c.columns; ++j) {
305
324
  if (typeof parsers[j] === 'undefined') {
306
- if (tc.debug) {
307
- log('No parser found for cell:', c[0].cells[j], 'does it have a header?');
325
+ if (c.debug) {
326
+ log('No parser found for cell:', $row[0].cells[j], 'does it have a header?');
308
327
  }
309
328
  continue;
310
329
  }
311
- t = getElementText(table, c[0].cells[j], j);
330
+ t = getElementText(table, $row[0].cells[j], j);
312
331
  // allow parsing if the string is empty, previously parsing would change it to zero,
313
332
  // in case the parser needs to extract data from the table cell attributes
314
- v = parsers[j].format(t, table, c[0].cells[j], j);
333
+ v = parsers[j].format(t, table, $row[0].cells[j], j);
315
334
  cols.push(v);
316
335
  if ((parsers[j].type || '').toLowerCase() === "numeric") {
317
- colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); // determine column max value (ignore sign)
336
+ // determine column max value (ignore sign)
337
+ colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0);
318
338
  }
319
339
  }
320
- cols.push(tc.cache[k].normalized.length); // add position for rowCache
321
- tc.cache[k].normalized.push(cols);
340
+ // ensure rowData is always in the same location (after the last column)
341
+ cols[c.columns] = rowData;
342
+ cc.normalized.push(cols);
322
343
  }
323
- tc.cache[k].colMax = colMax;
344
+ cc.colMax = colMax;
324
345
  }
325
346
  }
326
- if (tc.showProcessing) {
347
+ if (c.showProcessing) {
327
348
  ts.isProcessing(table); // remove processing icon
328
349
  }
329
- if (tc.debug) {
350
+ if (c.debug) {
330
351
  benchmark("Building cache for " + totalRows + " rows", cacheTime);
331
352
  }
332
353
  }
@@ -337,11 +358,11 @@
337
358
  wo = c.widgetOptions,
338
359
  b = table.tBodies,
339
360
  rows = [],
340
- c2 = c.cache,
341
- r, n, totalRows, checkCell, $bk, $tb,
342
- i, j, k, l, pos, appendTime;
361
+ cc = c.cache,
362
+ n, totalRows, $bk, $tb,
363
+ i, k, appendTime;
343
364
  // empty table - fixes #206/#346
344
- if (isEmptyObject(c2)) {
365
+ if (isEmptyObject(cc)) {
345
366
  // run pager appender in case the table was just emptied
346
367
  return c.appender ? c.appender(table, rows) :
347
368
  table.isUpdating ? c.$table.trigger("updateComplete", table) : ''; // Fixes #532
@@ -354,19 +375,13 @@
354
375
  if ($bk.length && !$bk.hasClass(c.cssInfoBlock)) {
355
376
  // get tbody
356
377
  $tb = ts.processTbody(table, $bk, true);
357
- r = c2[k].row;
358
- n = c2[k].normalized;
378
+ n = cc[k].normalized;
359
379
  totalRows = n.length;
360
- checkCell = totalRows ? (n[0].length - 1) : 0;
361
380
  for (i = 0; i < totalRows; i++) {
362
- pos = n[i][checkCell];
363
- rows.push(r[pos]);
381
+ rows.push(n[i][c.columns].$row);
364
382
  // removeRows used by the pager plugin; don't render if using ajax - fixes #411
365
383
  if (!c.appender || (c.pager && (!c.pager.removeRows || !wo.pager_removeRows) && !c.pager.ajax)) {
366
- l = r[pos].length;
367
- for (j = 0; j < l; j++) {
368
- $tb.append(r[pos][j]);
369
- }
384
+ $tb.append(n[i][c.columns].$row);
370
385
  }
371
386
  }
372
387
  // restore tbody
@@ -386,52 +401,6 @@
386
401
  }
387
402
  }
388
403
 
389
- // computeTableHeaderCellIndexes from:
390
- // http://www.javascripttoolbox.com/lib/table/examples.php
391
- // http://www.javascripttoolbox.com/temp/table_cellindex.html
392
- function computeThIndexes(t) {
393
- var matrix = [],
394
- lookup = {},
395
- cols = 0, // determine the number of columns
396
- trs = $(t).children('thead, tfoot').children('tr'), // children tr in tfoot - see issue #196 & #547
397
- i, j, k, l, c, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, matrixrow;
398
- for (i = 0; i < trs.length; i++) {
399
- cells = trs[i].cells;
400
- for (j = 0; j < cells.length; j++) {
401
- c = cells[j];
402
- rowIndex = c.parentNode.rowIndex;
403
- cellId = rowIndex + "-" + $(c).index();
404
- rowSpan = c.rowSpan || 1;
405
- colSpan = c.colSpan || 1;
406
- if (typeof(matrix[rowIndex]) === "undefined") {
407
- matrix[rowIndex] = [];
408
- }
409
- // Find first available column in the first row
410
- for (k = 0; k < matrix[rowIndex].length + 1; k++) {
411
- if (typeof(matrix[rowIndex][k]) === "undefined") {
412
- firstAvailCol = k;
413
- break;
414
- }
415
- }
416
- lookup[cellId] = firstAvailCol;
417
- cols = Math.max(firstAvailCol, cols);
418
- // add data-column
419
- $(c).attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex
420
- for (k = rowIndex; k < rowIndex + rowSpan; k++) {
421
- if (typeof(matrix[k]) === "undefined") {
422
- matrix[k] = [];
423
- }
424
- matrixrow = matrix[k];
425
- for (l = firstAvailCol; l < firstAvailCol + colSpan; l++) {
426
- matrixrow[l] = "x";
427
- }
428
- }
429
- }
430
- }
431
- // may not be accurate if # header columns !== # tbody columns
432
- t.config.columns = cols + 1; // add one because it's a zero-based index
433
- }
434
-
435
404
  function formatSortingOrder(v) {
436
405
  // look for "d" in "desc" order; return true
437
406
  return (/^d/i.test(v) || v === 1);
@@ -446,7 +415,8 @@
446
415
  if (c.debug) {
447
416
  time = new Date();
448
417
  }
449
- computeThIndexes(table);
418
+ // children tr in tfoot - see issue #196 & #547
419
+ c.columns = ts.computeColumnIndex( c.$table.children('thead, tfoot').children('tr') );
450
420
  // add icon if cssIcon option exists
451
421
  i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : '';
452
422
  c.$headers = $(table).find(c.selectorHeaders).each(function(index) {
@@ -701,8 +671,8 @@
701
671
 
702
672
  // sort multiple columns
703
673
  function multisort(table) { /*jshint loopfunc:true */
704
- var i, k, num, col, colMax, cache, lc,
705
- order, orgOrderCol, sortTime, sort, x, y,
674
+ var i, k, num, col, sortTime, colMax,
675
+ cache, order, sort, x, y,
706
676
  dir = 0,
707
677
  c = table.config,
708
678
  cts = c.textSorter || '',
@@ -716,8 +686,7 @@
716
686
  for (k = 0; k < bl; k++) {
717
687
  colMax = c.cache[k].colMax;
718
688
  cache = c.cache[k].normalized;
719
- lc = cache.length;
720
- orgOrderCol = (cache && cache[0]) ? cache[0].length - 1 : 0;
689
+
721
690
  cache.sort(function(a, b) {
722
691
  // cache is undefined here in IE, so don't use it!
723
692
  for (i = 0; i < l; i++) {
@@ -727,7 +696,7 @@
727
696
  dir = order === 0;
728
697
 
729
698
  if (c.sortStable && a[col] === b[col] && l === 1) {
730
- return a[orgOrderCol] - b[orgOrderCol];
699
+ return a[c.columns].order - b[c.columns].order;
731
700
  }
732
701
 
733
702
  // fallback to natural sort since it is more robust
@@ -761,7 +730,7 @@
761
730
  }
762
731
  if (sort) { return sort; }
763
732
  }
764
- return a[orgOrderCol] - b[orgOrderCol];
733
+ return a[c.columns].order - b[c.columns].order;
765
734
  });
766
735
  }
767
736
  if (c.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time", sortTime); }
@@ -772,7 +741,7 @@
772
741
  if (table.isUpdating) {
773
742
  $table.trigger('updateComplete');
774
743
  }
775
- if (typeof callback === "function") {
744
+ if ($.isFunction(callback)) {
776
745
  callback($table[0]);
777
746
  }
778
747
  }
@@ -786,8 +755,8 @@
786
755
  resortComplete($table, callback);
787
756
  }, true]);
788
757
  } else {
789
- $table.trigger('applyWidgets');
790
758
  resortComplete($table, callback);
759
+ ts.applyWidget($table[0], false);
791
760
  }
792
761
  }
793
762
 
@@ -797,12 +766,15 @@
797
766
  // apply easy methods that trigger bound events
798
767
  $table
799
768
  .unbind('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join(c.namespace + ' '))
800
- .bind("sortReset" + c.namespace, function(e){
769
+ .bind("sortReset" + c.namespace, function(e, callback){
801
770
  e.stopPropagation();
802
771
  c.sortList = [];
803
772
  setHeadersCss(table);
804
773
  multisort(table);
805
774
  appendToTable(table);
775
+ if ($.isFunction(callback)) {
776
+ callback(table);
777
+ }
806
778
  })
807
779
  .bind("updateAll" + c.namespace, function(e, resort, callback){
808
780
  e.stopPropagation();
@@ -826,20 +798,24 @@
826
798
  table.isUpdating = true;
827
799
  $table.find(c.selectorRemove).remove();
828
800
  // get position from the dom
829
- var l, row, icell,
801
+ var v, row, icell,
830
802
  $tb = $table.find('tbody'),
803
+ $cell = $(cell),
831
804
  // update cache - format: function(s, table, cell, cellIndex)
832
805
  // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr');
833
- tbdy = $tb.index( $(cell).parents('tbody').filter(':first') ),
834
- $row = $(cell).parents('tr').filter(':first');
835
- cell = $(cell)[0]; // in case cell is a jQuery object
806
+ tbdy = $tb.index( $cell.parents('tbody').filter(':first') ),
807
+ $row = $cell.parents('tr').filter(':first');
808
+ cell = $cell[0]; // in case cell is a jQuery object
836
809
  // tbody may not exist if update is initialized while tbody is removed for processing
837
810
  if ($tb.length && tbdy >= 0) {
838
811
  row = $tb.eq(tbdy).find('tr').index( $row );
839
- icell = $(cell).index();
840
- l = c.cache[tbdy].normalized[row].length - 1;
841
- c.cache[tbdy].row[ c.cache[tbdy].normalized[row][l] ] = $row;
842
- c.cache[tbdy].normalized[row][icell] = c.parsers[icell].format( getElementText(table, cell, icell), table, cell, icell );
812
+ icell = $cell.index();
813
+ c.cache[tbdy].normalized[row][c.columns].$row = $row;
814
+ v = c.cache[tbdy].normalized[row][icell] = c.parsers[icell].format( getElementText(table, cell, icell), table, cell, icell );
815
+ if ((c.parsers[icell].type || '').toLowerCase() === "numeric") {
816
+ // update column max value (ignore sign)
817
+ c.cache[tbdy].colMax[icell] = Math.max(Math.abs(v) || 0, c.cache[tbdy].colMax[icell] || 0);
818
+ }
843
819
  checkResort($table, resort, callback);
844
820
  }
845
821
  })
@@ -851,26 +827,34 @@
851
827
  updateHeader(table);
852
828
  commonUpdate(table, resort, callback);
853
829
  } else {
854
- var i, j,
830
+ var i, j, l, rowData, cells,
855
831
  rows = $row.filter('tr').length,
856
- dat = [], l = $row[0].cells.length,
857
832
  tbdy = $table.find('tbody').index( $row.parents('tbody').filter(':first') );
858
833
  // fixes adding rows to an empty table - see issue #179
859
- if (!c.parsers) {
834
+ if (!(c.parsers && c.parsers.length)) {
860
835
  buildParserCache(table);
861
836
  }
862
837
  // add each row
863
838
  for (i = 0; i < rows; i++) {
839
+ l = $row[i].cells.length;
840
+ cells = [];
841
+ rowData = {
842
+ child: [],
843
+ $row : $row.eq(i),
844
+ order: c.cache[tbdy].normalized.length
845
+ };
864
846
  // add each cell
865
847
  for (j = 0; j < l; j++) {
866
- dat[j] = c.parsers[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j );
848
+ cells[j] = c.parsers[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j );
849
+ if ((c.parsers[j].type || '').toLowerCase() === "numeric") {
850
+ // update column max value (ignore sign)
851
+ c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0);
852
+ }
867
853
  }
868
- // add the row index to the end
869
- dat.push(c.cache[tbdy].row.length);
854
+ // add the row data to the end
855
+ cells.push(rowData);
870
856
  // update cache
871
- c.cache[tbdy].row.push([$row[i]]);
872
- c.cache[tbdy].normalized.push(dat);
873
- dat = [];
857
+ c.cache[tbdy].normalized.push(cells);
874
858
  }
875
859
  // resort using current settings
876
860
  checkResort($table, resort, callback);
@@ -893,28 +877,27 @@
893
877
  // sort the table and append it to the dom
894
878
  multisort(table);
895
879
  appendToTable(table, init);
896
- $table
897
- .trigger("sortEnd", this)
898
- .trigger('applyWidgets');
899
- if (typeof callback === "function") {
880
+ $table.trigger("sortEnd", this);
881
+ ts.applyWidget(table);
882
+ if ($.isFunction(callback)) {
900
883
  callback(table);
901
884
  }
902
885
  })
903
886
  .bind("appendCache" + c.namespace, function(e, callback, init) {
904
887
  e.stopPropagation();
905
888
  appendToTable(table, init);
906
- if (typeof callback === "function") {
889
+ if ($.isFunction(callback)) {
907
890
  callback(table);
908
891
  }
909
892
  })
910
893
  .bind("updateCache" + c.namespace, function(e, callback){
911
894
  // rebuild parsers
912
- if (!c.parsers) {
895
+ if (!(c.parsers && c.parsers.length)) {
913
896
  buildParserCache(table);
914
897
  }
915
898
  // rebuild the cache map
916
899
  buildCache(table);
917
- if (typeof callback === "function") {
900
+ if ($.isFunction(callback)) {
918
901
  callback(table);
919
902
  }
920
903
  })
@@ -972,8 +955,6 @@
972
955
  $.data(table, "tablesorter", c);
973
956
  if (c.debug) { $.data( table, 'startoveralltimer', new Date()); }
974
957
 
975
- // constants
976
- c.supportsTextContent = $('<span>x</span>')[0].textContent === 'x';
977
958
  // removing this in version 3 (only supports jQuery 1.7+)
978
959
  c.supportsDataObject = (function(version) {
979
960
  version[0] = parseInt(version[0], 10);
@@ -1005,6 +986,8 @@
1005
986
  c.$table.attr('aria-labelledby', 'theCaption');
1006
987
  }
1007
988
  c.widgetInit = {}; // keep a list of initialized widgets
989
+ // change textExtraction via data-attribute
990
+ c.textExtraction = c.$table.attr('data-text-extraction') || c.textExtraction || 'basic';
1008
991
  // build headers
1009
992
  buildHeaders(table);
1010
993
  // fixate columns if the users supplies the fixedWidth option
@@ -1034,7 +1017,9 @@
1034
1017
  setHeadersCss(table);
1035
1018
  if (c.initWidgets) {
1036
1019
  // apply widget format
1037
- ts.applyWidget(table);
1020
+ setTimeout(function(){
1021
+ ts.applyWidget(table, false);
1022
+ }, 0);
1038
1023
  }
1039
1024
  }
1040
1025
 
@@ -1057,6 +1042,53 @@
1057
1042
  if (typeof c.initialized === 'function') { c.initialized(table); }
1058
1043
  };
1059
1044
 
1045
+
1046
+ // computeTableHeaderCellIndexes from:
1047
+ // http://www.javascripttoolbox.com/lib/table/examples.php
1048
+ // http://www.javascripttoolbox.com/temp/table_cellindex.html
1049
+ ts.computeColumnIndex = function(trs) {
1050
+ var matrix = [],
1051
+ lookup = {},
1052
+ cols = 0, // determine the number of columns
1053
+ i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, matrixrow;
1054
+ for (i = 0; i < trs.length; i++) {
1055
+ cells = trs[i].cells;
1056
+ for (j = 0; j < cells.length; j++) {
1057
+ cell = cells[j];
1058
+ $cell = $(cell);
1059
+ rowIndex = cell.parentNode.rowIndex;
1060
+ cellId = rowIndex + "-" + $cell.index();
1061
+ rowSpan = cell.rowSpan || 1;
1062
+ colSpan = cell.colSpan || 1;
1063
+ if (typeof(matrix[rowIndex]) === "undefined") {
1064
+ matrix[rowIndex] = [];
1065
+ }
1066
+ // Find first available column in the first row
1067
+ for (k = 0; k < matrix[rowIndex].length + 1; k++) {
1068
+ if (typeof(matrix[rowIndex][k]) === "undefined") {
1069
+ firstAvailCol = k;
1070
+ break;
1071
+ }
1072
+ }
1073
+ lookup[cellId] = firstAvailCol;
1074
+ cols = Math.max(firstAvailCol, cols);
1075
+ // add data-column
1076
+ $cell.attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex
1077
+ for (k = rowIndex; k < rowIndex + rowSpan; k++) {
1078
+ if (typeof(matrix[k]) === "undefined") {
1079
+ matrix[k] = [];
1080
+ }
1081
+ matrixrow = matrix[k];
1082
+ for (l = firstAvailCol; l < firstAvailCol + colSpan; l++) {
1083
+ matrixrow[l] = "x";
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+ // may not be accurate if # header columns !== # tbody columns
1089
+ return cols + 1; // add one because it's a zero-based index
1090
+ };
1091
+
1060
1092
  // *** Process table ***
1061
1093
  // add processing indicator
1062
1094
  ts.isProcessing = function(table, toggle, $ths) {
@@ -1186,6 +1218,7 @@
1186
1218
  $t.toggleClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false);
1187
1219
  // clear flag in case the plugin is initialized again
1188
1220
  table.hasInitialized = false;
1221
+ delete table.config.cache;
1189
1222
  if (typeof callback === 'function') {
1190
1223
  callback(table);
1191
1224
  }
@@ -1387,8 +1420,11 @@
1387
1420
  wo = c.widgetOptions,
1388
1421
  widgets = [],
1389
1422
  time, w, wd;
1423
+ // prevent numerous consecutive widget applications
1424
+ if (init !== false && table.hasInitialized && (table.isApplyingWidgets || table.isUpdating)) { return; }
1390
1425
  if (c.debug) { time = new Date(); }
1391
1426
  if (c.widgets.length) {
1427
+ table.isApplyingWidgets = true;
1392
1428
  // ensure unique widget ids
1393
1429
  c.widgets = $.grep(c.widgets, function(v, k){
1394
1430
  return $.inArray(v, c.widgets) === k;
@@ -1424,6 +1460,9 @@
1424
1460
  }
1425
1461
  });
1426
1462
  }
1463
+ setTimeout(function(){
1464
+ table.isApplyingWidgets = false;
1465
+ }, 0);
1427
1466
  if (c.debug) {
1428
1467
  w = c.widgets.length;
1429
1468
  benchmark("Completed " + (init === true ? "initializing " : "applying ") + w + " widget" + (w !== 1 ? "s" : ""), time);