jquery-tablesorter 1.0.0 → 1.0.1

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.
@@ -3,7 +3,9 @@ jQuery Table Sorter plugin for Rails
3
3
 
4
4
  Simple integration of jquery-tablesorter into the asset pipeline.
5
5
 
6
- Current version: 2.3.4
6
+ Current version: 2.3.7(6/3/2012)
7
+
8
+ Any issue associate with the js/css files, please report to [Mottie's fork].
7
9
 
8
10
  NOTICE:
9
11
  ---
@@ -1,3 +1,3 @@
1
1
  module JqueryTablesorter
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
@@ -1,5 +1,5 @@
1
- /*!
2
- * TableSorter 2.3.4 - Client-side table sorting with ease!
1
+ /*!
2
+ * TableSorter 2.3.7 - Client-side table sorting with ease!
3
3
  * @requires jQuery v1.2.6+
4
4
  *
5
5
  * Copyright (c) 2007 Christian Bach
@@ -18,13 +18,13 @@
18
18
  $.extend({
19
19
  tablesorter: new function() {
20
20
 
21
- this.version = "2.3.4";
21
+ this.version = "2.3.7";
22
22
 
23
23
  var parsers = [], widgets = [];
24
24
  this.defaults = {
25
25
 
26
26
  // appearance
27
- widthFixed : false,
27
+ widthFixed : false, // adds colgroup to fix widths of columns
28
28
 
29
29
  // functionality
30
30
  cancelSelection : true, // prevent text selection in the header
@@ -51,9 +51,11 @@
51
51
  textSorter : null, // use custom text sorter - function(a,b){ return a.sort(b); } // basic sort
52
52
 
53
53
  // widget options
54
+ widgets: [], // method to add widgets, e.g. widgets: ['zebra']
54
55
  widgetOptions : {
55
56
  zebra : [ "even", "odd" ] // zebra widget alternating row class names
56
57
  },
58
+ initWidgets : true, // apply widgets on tablesorter initialization
57
59
 
58
60
  // callbacks
59
61
  initialized : null, // function(table){},
@@ -78,8 +80,7 @@
78
80
  headerList: [],
79
81
  empties: {},
80
82
  strings: {},
81
- parsers: [],
82
- widgets: []
83
+ parsers: []
83
84
 
84
85
  // deprecated; but retained for backwards compatibility
85
86
  // widgetZebra: { css: ["even", "odd"] }
@@ -103,17 +104,26 @@
103
104
  this.hasInitialized = false;
104
105
 
105
106
  function getElementText(table, node, cellIndex) {
106
- var text = "", t = table.config.textExtraction;
107
107
  if (!node) { return ""; }
108
+ var c = table.config,
109
+ t = c.textExtraction, text = "";
108
110
  if (t === "simple") {
109
- text = $(node).text();
111
+ if (c.supportsTextContent) {
112
+ text = node.textContent; // newer browsers support this
113
+ } else {
114
+ if (node.childNodes[0] && node.childNodes[0].hasChildNodes()) {
115
+ text = node.childNodes[0].innerHTML;
116
+ } else {
117
+ text = node.innerHTML;
118
+ }
119
+ }
110
120
  } else {
111
121
  if (typeof(t) === "function") {
112
122
  text = t(node, table, cellIndex);
113
123
  } else if (typeof(t) === "object" && t.hasOwnProperty(cellIndex)) {
114
124
  text = t[cellIndex](node, table, cellIndex);
115
125
  } else {
116
- text = $(node).text();
126
+ text = c.supportsTextContent ? node.textContent : $(node).text();
117
127
  }
118
128
  }
119
129
  return $.trim(text);
@@ -141,7 +151,7 @@
141
151
  node = rows[rowIndex].cells[cellIndex];
142
152
  nodeValue = getElementText(table, node, cellIndex);
143
153
  if (table.config.debug) {
144
- log('Checking if value was empty on row ' + rowIndex + ', column:' + cellIndex + ": " + nodeValue);
154
+ log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': ' + nodeValue);
145
155
  }
146
156
  } else {
147
157
  keepLooking = false;
@@ -157,8 +167,9 @@
157
167
  }
158
168
 
159
169
  function buildParserCache(table, $headers) {
160
- var c = table.config, tb = $(table.tBodies).filter(':not(.' + c.cssInfoBlock + ')'),
161
- ts = $.tablesorter, rows, list, l, i, h, m, ch, cl, p, parsersDebug = "";
170
+ var c = table.config,
171
+ tb = $(table.tBodies).filter(':not(.' + c.cssInfoBlock + ')'),
172
+ ts = $.tablesorter, rows, list, l, i, h, m, ch, cl, p, parsersDebug = "";
162
173
  if ( tb.length === 0) { return; } // In the case of empty tables
163
174
  rows = tb[0].rows;
164
175
  if (rows[0]) {
@@ -190,19 +201,6 @@
190
201
  }
191
202
 
192
203
  /* utils */
193
- function buildRegex(){
194
- var a, acc = '[', t = $.tablesorter,
195
- reg = t.characterEquivalents;
196
- t.characterRegexArray = {};
197
- for (a in reg) {
198
- if (typeof a === 'string') {
199
- acc += reg[a];
200
- t.characterRegexArray[a] = new RegExp('[' + reg[a] + ']', 'g');
201
- }
202
- }
203
- t.characterRegex = new RegExp(acc + ']');
204
- }
205
-
206
204
  function buildCache(table) {
207
205
  var b = table.tBodies,
208
206
  tc = table.config,
@@ -218,6 +216,7 @@
218
216
  tc.cache[k] = { row: [], normalized: [] };
219
217
  // ignore tbodies with class name from css.cssInfoBlock
220
218
  if (!$(b[k]).hasClass(tc.cssInfoBlock)) {
219
+ $(b[k]).addClass('tablesorter-hidden');
221
220
  totalRows = (b[k] && b[k].rows.length) || 0;
222
221
  totalCells = (b[k].rows[0] && b[k].rows[0].cells.length) || 0;
223
222
  for (i = 0; i < totalRows; ++i) {
@@ -240,6 +239,7 @@
240
239
  cols.push(tc.cache[k].normalized.length); // add position for rowCache
241
240
  tc.cache[k].normalized.push(cols);
242
241
  }
242
+ $(b[k]).removeClass('tablesorter-hidden');
243
243
  }
244
244
  }
245
245
  if (tc.debug) {
@@ -258,21 +258,28 @@
258
258
  }
259
259
 
260
260
  function applyWidget(table, init) {
261
- var c = table.config.widgets,
262
- i, w, l = c.length;
261
+ var tc = table.config, c = tc.widgets,
262
+ time, i, w, l = c.length;
263
+ if (tc.debug) {
264
+ time = new Date();
265
+ }
263
266
  for (i = 0; i < l; i++) {
264
267
  w = getWidgetById(c[i]);
265
268
  if ( w ) {
266
- if (init && w.hasOwnProperty('init')) {
269
+ if (init === true && w.hasOwnProperty('init')) {
267
270
  w.init(table, widgets, w);
268
271
  } else if (!init && w.hasOwnProperty('format')) {
269
272
  w.format(table);
270
273
  }
271
274
  }
272
275
  }
276
+ if (tc.debug) {
277
+ benchmark("Completed " + (init === true ? "initializing" : "applying") + " widgets", time);
278
+ }
273
279
  }
274
280
 
275
- function appendToTable(table) {
281
+ // init flag (true) used by pager plugin to prevent widget application
282
+ function appendToTable(table, init) {
276
283
  var c = table.config,
277
284
  b = table.tBodies,
278
285
  rows = [],
@@ -283,6 +290,7 @@
283
290
  }
284
291
  for (k = 0; k < b.length; k++) {
285
292
  if (!$(b[k]).hasClass(c.cssInfoBlock)){
293
+ $(b[k]).addClass('tablesorter-hidden');
286
294
  f = document.createDocumentFragment();
287
295
  r = c2[k].row;
288
296
  n = c2[k].normalized;
@@ -300,6 +308,7 @@
300
308
  }
301
309
  }
302
310
  table.tBodies[k].appendChild(f);
311
+ $(b[k]).removeClass('tablesorter-hidden');
303
312
  }
304
313
  }
305
314
  if (c.appender) {
@@ -309,15 +318,15 @@
309
318
  benchmark("Rebuilt table", appendTime);
310
319
  }
311
320
  // apply table widgets
312
- applyWidget(table);
321
+ if (!init) { applyWidget(table); }
313
322
  // trigger sortend
314
323
  $(table).trigger("sortEnd", table);
315
324
  }
316
325
 
317
- // from:
326
+ // computeTableHeaderCellIndexes from:
318
327
  // http://www.javascripttoolbox.com/lib/table/examples.php
319
328
  // http://www.javascripttoolbox.com/temp/table_cellindex.html
320
- function computeTableHeaderCellIndexes(t) {
329
+ function computeThIndexes(t) {
321
330
  var matrix = [],
322
331
  lookup = {},
323
332
  trs = $(t).find('thead:eq(0) tr'),
@@ -362,19 +371,20 @@
362
371
  return (/^d/i.test(v) || v === 1);
363
372
  }
364
373
 
374
+
365
375
  function buildHeaders(table) {
366
- var meta = ($.metadata) ? true : false,
367
- header_index = computeTableHeaderCellIndexes(table), ch, $t,
368
- $th, lock, time, $tableHeaders, c = table.config, ts = $.tablesorter;
369
- c.headerList = [];
376
+ var header_index = computeThIndexes(table), ch, $t,
377
+ $th, lock, time, $tableHeaders, c = table.config, ts = $.tablesorter;
378
+ c.headerList = [];
370
379
  if (c.debug) {
371
380
  time = new Date();
372
381
  }
373
- $tableHeaders = $(c.selectorHeaders, table)
374
- .wrapInner("<div class='tablesorter-header-inner' />")
382
+ $tableHeaders = $(table).find(c.selectorHeaders)
375
383
  .each(function(index) {
376
384
  $t = $(this);
377
385
  ch = c.headers[index];
386
+ this.innerHTML = '<div class="tablesorter-header-inner">' + this.innerHTML + '</div>'; // faster than wrapInner
387
+ if (c.onRenderHeader) { c.onRenderHeader.apply($th, [index]); }
378
388
  this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex];
379
389
  this.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2];
380
390
  this.count = -1; // set to -1 because clicking on the header automatically adds one
@@ -386,15 +396,14 @@
386
396
  }
387
397
  if (!this.sortDisabled) {
388
398
  $th = $t.addClass(c.cssHeader);
389
- if (c.onRenderHeader) { c.onRenderHeader.apply($th, [index]); }
390
399
  }
391
400
  // add cell to headerList
392
401
  c.headerList[index] = this;
393
402
  // add to parent in case there are multiple rows
394
403
  $t.parent().addClass(c.cssHeader);
395
404
  });
396
- if (c.debug) {
397
- benchmark("Built headers", time);
405
+ if (table.config.debug) {
406
+ benchmark("Built headers:", time);
398
407
  log($tableHeaders);
399
408
  }
400
409
  return $tableHeaders;
@@ -423,7 +432,10 @@
423
432
  l = list.length;
424
433
  for (i = 0; i < l; i++) {
425
434
  if (list[i][1] === 2) { continue; } // direction = 2 means reset!
426
- h[list[i][0]].addClass(css[list[i][1]]);
435
+ if (h[list[i][0]]) {
436
+ // add class if cell exists - fix for issue #78
437
+ h[list[i][0]].addClass(css[list[i][1]]);
438
+ }
427
439
  // multicolumn sorting updating
428
440
  f = $headers.filter('[data-column="' + list[i][0] + '"]');
429
441
  if (l > 1 && f.length) {
@@ -457,7 +469,7 @@
457
469
  }
458
470
 
459
471
  function getCachedSortType(parsers, i) {
460
- return (parsers) ? parsers[i].type : '';
472
+ return (parsers && parsers[i]) ? parsers[i].type || '' : '';
461
473
  }
462
474
 
463
475
  /* sorting methods - reverted sorting method back to version 2.0.3 */
@@ -490,7 +502,7 @@
490
502
  dir = (tc.strings[c]) ? tc.string[tc.strings[c]] || 0 : 0;
491
503
  }
492
504
  }
493
- dynamicExp += "var " + e + " = sort" + s + "(table, a[" + c + "],b[" + c + "]," + c + "," + mx + "," + dir + "); ";
505
+ dynamicExp += "var " + e + " = sort" + s + "(table,a[" + c + "],b[" + c + "]," + c + "," + mx + "," + dir + "); ";
494
506
  dynamicExp += "if (" + e + ") { return " + e + "; } ";
495
507
  dynamicExp += "else { ";
496
508
  }
@@ -596,8 +608,8 @@
596
608
  // if no thead or tbody quit.
597
609
  if (!this.tHead || this.tBodies.length === 0) { return; }
598
610
  // declare
599
- var $headers, $cell, totalRows, $this,
600
- config, c, i, j, k, a, s, o,
611
+ var $headers, $cell, $this,
612
+ config, c, i, j, k, a, s, o, downTime,
601
613
  m = $.metadata;
602
614
  // new blank config object
603
615
  this.config = {};
@@ -609,8 +621,7 @@
609
621
  $this = $(this).addClass(c.tableClass);
610
622
  // save the settings where they read
611
623
  $.data(this, "tablesorter", c);
612
- // build up character equivalent cross-reference
613
- buildRegex();
624
+ c.supportsTextContent = $('<span>x</span>')[0].textContent === 'x';
614
625
  // digit sort text location; keeping max+/- for backwards compatibility
615
626
  c.string = { 'max': 1, 'min': -1, 'max+': 1, 'max-': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false };
616
627
  // build headers
@@ -624,8 +635,14 @@
624
635
  fixColumnWidth(this);
625
636
  // apply event handling to headers
626
637
  // this is to big, perhaps break it out?
627
- $headers
628
- .click(function(e) {
638
+ $headers.bind('mousedown.tablesorter mouseup.tablesorter', function(e, external) {
639
+ if (e.type === 'mousedown') {
640
+ downTime = new Date().getTime();
641
+ return !c.cancelSelection;
642
+ }
643
+
644
+ // prevent resizable widget from initializing a sort (long clicks are ignored)
645
+ if (external !== true && (new Date().getTime() - downTime > 500)) { return false; }
629
646
  if (c.delayInit && !c.cache) { buildCache($this[0]); }
630
647
  if (!this.sortDisabled) {
631
648
  // Only call sortStart if sorting is enabled.
@@ -712,20 +729,20 @@
712
729
  $this.trigger("sortBegin", $this[0]);
713
730
  // set css for headers
714
731
  setHeadersCss($this[0], $headers, c.sortList);
715
- appendToTable($this[0], multisort($this[0], c.sortList));
732
+ multisort($this[0], c.sortList);
733
+ appendToTable($this[0]);
716
734
  // stop normal event by returning false
717
735
  return false;
718
736
  }
737
+ });
738
+ if (c.cancelSelection) {
719
739
  // cancel selection
720
- })
721
- .mousedown(function() {
722
- if (c.cancelSelection) {
740
+ $headers.each(function() {
723
741
  this.onselectstart = function() {
724
742
  return false;
725
743
  };
726
- return false;
727
- }
728
- });
744
+ });
745
+ }
729
746
  // apply easy methods that trigger binded events
730
747
  $this
731
748
  .bind("update", function(e, resort) {
@@ -739,9 +756,11 @@
739
756
  })
740
757
  .bind("updateCell", function(e, cell, resort) {
741
758
  // get position from the dom.
742
- var t = this, pos = [(cell.parentNode.rowIndex - 1), cell.cellIndex],
759
+ var t = this, $tb = $(this).find('tbody'), row, pos,
743
760
  // update cache - format: function(s, table, cell, cellIndex)
744
- tbdy = $(this).find('tbody').index( $(cell).closest('tbody') );
761
+ tbdy = $tb.index( $(cell).closest('tbody') );
762
+ row = $tb.eq(tbdy).find('tr').index( $(cell).closest('tr') );
763
+ pos = [ row, cell.cellIndex];
745
764
  t.config.cache[tbdy].normalized[pos[0]][pos[1]] = c.parsers[pos[1]].format( getElementText(t, cell, pos[1]), t, cell, pos[1] );
746
765
  if (resort !== false) { $(this).trigger("sorton", [c.sortList]); }
747
766
  })
@@ -765,7 +784,7 @@
765
784
  // resort using current settings
766
785
  if (resort !== false) { $(this).trigger("sorton", [c.sortList]); }
767
786
  })
768
- .bind("sorton", function(e, list) {
787
+ .bind("sorton", function(e, list, init) {
769
788
  $(this).trigger("sortStart", this);
770
789
  // update and store the sortlist
771
790
  c.sortList = list;
@@ -774,17 +793,18 @@
774
793
  // set css for headers
775
794
  setHeadersCss(this, $headers, c.sortList);
776
795
  // sort the table and append it to the dom
777
- appendToTable(this, multisort(this, c.sortList));
796
+ multisort(this, c.sortList);
797
+ appendToTable(this, init);
778
798
  })
779
- .bind("appendCache", function() {
780
- appendToTable(this);
799
+ .bind("appendCache", function(e, init) {
800
+ appendToTable(this, init);
781
801
  })
782
802
  .bind("applyWidgetId", function(e, id) {
783
803
  getWidgetById(id).format(this);
784
804
  })
785
- .bind("applyWidgets", function() {
805
+ .bind("applyWidgets", function(e, init) {
786
806
  // apply widgets
787
- applyWidget(this);
807
+ applyWidget(this, init);
788
808
  })
789
809
  .bind("destroy", function(e,c){
790
810
  $.tablesorter.destroy(this, c);
@@ -800,8 +820,8 @@
800
820
  applyWidget(this, true);
801
821
  // if user has supplied a sort list to constructor.
802
822
  if (c.sortList.length > 0) {
803
- $this.trigger("sorton", [c.sortList]);
804
- } else {
823
+ $this.trigger("sorton", [c.sortList, !c.initWidgets]);
824
+ } else if (c.initWidgets) {
805
825
  // apply widget format
806
826
  applyWidget(this);
807
827
  }
@@ -894,8 +914,18 @@
894
914
  "U" : "\u00da\u00d9\u00db\u00dc" // ÚÙÛÜ
895
915
  };
896
916
  this.replaceAccents = function(s) {
917
+ var a, acc = '[', eq = this.characterEquivalents;
918
+ if (!this.characterRegex) {
919
+ this.characterRegexArray = {};
920
+ for (a in eq) {
921
+ if (typeof a === 'string') {
922
+ acc += eq[a];
923
+ this.characterRegexArray[a] = new RegExp('[' + eq[a] + ']', 'g');
924
+ }
925
+ }
926
+ this.characterRegex = new RegExp(acc + ']');
927
+ }
897
928
  if (this.characterRegex.test(s)) {
898
- var a, eq = this.characterEquivalents;
899
929
  for (a in eq) {
900
930
  if (typeof a === 'string') {
901
931
  s = s.replace( this.characterRegexArray[a], a );
@@ -906,13 +936,14 @@
906
936
  };
907
937
 
908
938
  // get sorter, string, empty, etc options for each column from
909
- // metadata, header option or header class name ("sorter-false")
939
+ // jQuery data, metadata, header option or header class name ("sorter-false")
910
940
  // priority = jQuery data > meta > headers option > header class name
911
941
  this.getData = function(h, ch, key) {
912
- var val = '', $h = $(h),
913
- m = $.metadata ? $h.metadata() : false,
914
- cl = ' ' + ($h.length ? $h.attr('class') || '' : '');
915
- if ($h.length && $h.data() && ( typeof $h.data(key) !== 'undefined' || typeof $h.data(key.toLowerCase()) !== 'undefined') ){
942
+ var val = '', $h = $(h), m, cl;
943
+ if (!$h.length) { return ''; }
944
+ m = $.metadata ? $h.metadata() : false;
945
+ cl = ' ' + ($h.attr('class') || '');
946
+ if ($h.data() && ( typeof $h.data(key) !== 'undefined' || typeof $h.data(key.toLowerCase()) !== 'undefined') ){
916
947
  // "data-lockedOrder" is assigned to "lockedorder"; but "data-locked-order" is assigned to "lockedOrder"
917
948
  // "data-sort-initial-order" is assigned to "sortInitialOrder"
918
949
  val += $h.data(key) || $h.data(key.toLowerCase());
@@ -992,7 +1023,7 @@
992
1023
  ts.addParser({
993
1024
  id: "url",
994
1025
  is: function(s) {
995
- return (/^(https?|ftp|file):\/\/$/).test(s);
1026
+ return (/^(https?|ftp|file):\/\//).test(s);
996
1027
  },
997
1028
  format: function(s) {
998
1029
  return $.trim(s.replace(/(https?|ftp|file):\/\//, ''));
@@ -1036,8 +1067,8 @@
1036
1067
  ts.addParser({
1037
1068
  id: "shortDate", // "mmddyyyy", "ddmmyyyy" or "yyyymmdd"
1038
1069
  is: function(s) {
1039
- // testing for ####-####-#### - so it's not perfect
1040
- return (/\d{1,4}[\/\-\,\.\s+]\d{1,4}[\/\-\.\,\s+]\d{1,4}/).test(s);
1070
+ // testing for ####-##-#### - so it's not perfect
1071
+ return (/^(\d{2}|\d{4})[\/\-\,\.\s+]\d{2}[\/\-\.\,\s+](\d{2}|\d{4})$/).test(s);
1041
1072
  },
1042
1073
  format: function(s, table, cell, cellIndex) {
1043
1074
  var c = table.config, ci = c.headerList[cellIndex],
@@ -1098,7 +1129,7 @@
1098
1129
  ts.addWidget({
1099
1130
  id: "zebra",
1100
1131
  format: function(table) {
1101
- var $tb, $tr, $f, row, even, time, k, j, l, n,
1132
+ var $tb, $tv, $tr, row, even, time, k, l,
1102
1133
  c = table.config,
1103
1134
  child = new RegExp(c.cssChildRow, 'i'),
1104
1135
  b = $(table).children('tbody:not(.' + c.cssInfoBlock + ')'),
@@ -1110,24 +1141,23 @@
1110
1141
  time = new Date();
1111
1142
  }
1112
1143
  for (k = 0; k < b.length; k++ ) {
1113
- row = 0;
1114
1144
  // loop through the visible rows
1115
1145
  $tb = $(b[k]);
1116
1146
  l = $tb.children('tr').length;
1117
1147
  if (l > 1) {
1118
- $f = $(document.createDocumentFragment());
1119
- $tr = $tb.children('tr').appendTo($f);
1120
- for (j = 0; j < l; j++) {
1121
- if ($tr[j].style.display !== 'none') {
1122
- n = $tr[j].className;
1123
- // style children rows the same way the parent row was styled
1124
- if (!child.test(n)) { row++; }
1125
- even = (row % 2 === 0);
1126
- $tr[j].className = n.replace(/\s+/g,'').replace(css[0],'').replace(css[1],'') + ' ' + css[even ? 0 : 1];
1127
- }
1128
- }
1148
+ row = 0;
1149
+ $tv = $tb.find('tr:visible');
1150
+ $tb.addClass('tablesorter-hidden');
1151
+ // revered back to using jQuery each - strangely it's the fastest method
1152
+ $tv.each(function(){
1153
+ $tr = $(this);
1154
+ // style children rows the same way the parent row was styled
1155
+ if (!child.test(this.className)) { row++; }
1156
+ even = (row % 2 === 0);
1157
+ $tr.removeClass(css[even ? 1 : 0]).addClass(css[even ? 0 : 1]);
1158
+ });
1159
+ $tb.removeClass('tablesorter-hidden');
1129
1160
  }
1130
- $tb.append($tr);
1131
1161
  }
1132
1162
  if (c.debug) {
1133
1163
  ts.benchmark("Applying Zebra widget", time);
@@ -1,8 +1,8 @@
1
- /*! tableSorter 2.3 widgets - updated 5/19/2012
1
+ /*! tableSorter 2.3 widgets - updated 6/1/2012
2
2
  *
3
3
  * jQuery UI Theme
4
4
  * Column Styles
5
- * Column Filters (not compatible with tablesorter v2.0.5)
5
+ * Column Filters
6
6
  * Sticky Header
7
7
  * Column Resizing
8
8
  * Save Sort
@@ -125,7 +125,7 @@ $.tablesorter.addWidget({
125
125
  $.tablesorter.addWidget({
126
126
  id: "columns",
127
127
  format: function(table) {
128
- var $tb, $tr, $td, $f, time, last, rmv, i, j, k, l,
128
+ var $tb, $tr, $td, $t, time, last, rmv, i, k, l,
129
129
  c = table.config,
130
130
  b = $(table).children('tbody:not(.' + c.cssInfoBlock + ')'),
131
131
  list = c.sortList,
@@ -140,35 +140,30 @@ $.tablesorter.addWidget({
140
140
  time = new Date();
141
141
  }
142
142
  // check if there is a sort (on initialization there may not be one)
143
- if (list && list[0]) {
144
- for (k = 0; k < b.length; k++ ) {
145
- $tb = $(b[k]);
146
- $f = $(document.createDocumentFragment());
147
- $tr = $tb.children('tr').appendTo($f);
148
- l = $tr.length;
149
- // loop through the visible rows
150
- for (j = 0; j < l; j++) {
151
- $td = $tr.eq(j).children().removeClass(rmv);
152
- // primary sort column class
153
- $td.eq(list[0][0]).addClass(css[0]);
154
- if (len > 1) {
155
- for (i = 1; i < len; i++){
156
- // secondary, tertiary, etc sort column classes
157
- $td.eq(list[i][0]).addClass( css[i] || css[last] );
143
+ for (k = 0; k < b.length; k++ ) {
144
+ $tb = $(b[k]);
145
+ $tr = $tb.addClass('tablesorter-hidden').children('tr');
146
+ l = $tr.length;
147
+ // loop through the visible rows
148
+ $tr.each(function(){
149
+ $t = $(this);
150
+ if (this.style.display !== 'none') {
151
+ // remove all columns class names
152
+ $td = $t.children().removeClass(rmv);
153
+ // add appropriate column class names
154
+ if (list && list[0]) {
155
+ // primary sort column class
156
+ $td.eq(list[0][0]).addClass(css[0]);
157
+ if (len > 1) {
158
+ for (i = 1; i < len; i++){
159
+ // secondary, tertiary, etc sort column classes
160
+ $td.eq(list[i][0]).addClass( css[i] || css[last] );
161
+ }
158
162
  }
159
163
  }
160
164
  }
161
- $tb.append($tr);
162
- }
163
- } else {
164
- // remove all column classes if sort is cleared (sortReset)
165
- // $(table).find('td').removeClass(rmv); slow
166
- $td = $(table).find('td');
167
- len = $td.length;
168
- rmv = new RegExp('(' + css.join('|') + ')');
169
- for (i = 0; i < len; i++){
170
- $td[i].className = $td[i].className.replace(rmv, '');
171
- }
165
+ });
166
+ $tb.removeClass('tablesorter-hidden');
172
167
  }
173
168
  if (c.debug) {
174
169
  $.tablesorter.benchmark("Applying Columns widget", time);
@@ -177,65 +172,192 @@ $.tablesorter.addWidget({
177
172
  });
178
173
 
179
174
  // Widget: Filter
180
- // "filter_startsWith" & "filter_childRows" options in "widgetOptions"
175
+ // "filter_startsWith", "filter_childRows", "filter_ignoreCase",
176
+ // "filter_searchDelay" & "filter_functions" options in "widgetOptions"
181
177
  // **************************
182
178
  $.tablesorter.addWidget({
183
179
  id: "filter",
184
180
  format: function(table) {
185
181
  if (!$(table).hasClass('hasFilters')) {
186
- var i, v, r, t, x, cr, $td, c = table.config,
187
- wo = c.widgetOptions,
188
- css = wo.filter_cssFilter || 'tablesorter-filter',
189
- $t = $(table).addClass('hasFilters'),
190
- cols = c.parsers.length,
191
- fr = '<tr class="' + css + '">',
192
- time;
182
+ var i, j, k, l, cv, v, val, r, ff, t, x, xi, cr,
183
+ sel, $tb, $tr, $td, reg2,
184
+ c = table.config,
185
+ wo = c.widgetOptions,
186
+ css = wo.filter_cssFilter || 'tablesorter-filter',
187
+ $t = $(table).addClass('hasFilters'),
188
+ b = $t.children('tbody:not(.' + c.cssInfoBlock + ')'),
189
+ cols = c.parsers.length,
190
+ fr = '<tr class="' + css + '">',
191
+ regexp = /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/,
192
+ reg1 = new RegExp(c.cssChildRow),
193
+ time, timer,
194
+ findRows = function(){
195
+ if (c.debug) { time = new Date(); }
196
+ v = $t.find('thead').eq(0).children('tr').find('select.' + css + ', input.' + css).map(function(){
197
+ return $(this).val() || '';
198
+ }).get();
199
+ cv = v.join('');
200
+ for (k = 0; k < b.length; k++ ) {
201
+ $tb = $(b[k]);
202
+ $tr = $tb.addClass('tablesorter-hidden').children('tr');
203
+ l = $tr.length;
204
+ // loop through the rows
205
+ for (j = 0; j < l; j++) {
206
+ // skip child rows
207
+ if (reg1.test($tr[j].className)) { continue; }
208
+ if (cv === '') {
209
+ $tr[j].style.display = '';
210
+ } else {
211
+ r = true;
212
+ cr = $tr.eq(j).nextUntil('tr:not(.' + c.cssChildRow + ')');
213
+ // so, if "table.config.widgetOptions.filter_childRows" is true and there is
214
+ // a match anywhere in the child row, then it will make the row visible
215
+ // checked here so the option can be changed dynamically
216
+ t = (cr.length && (wo && wo.hasOwnProperty('filter_childRows') &&
217
+ typeof wo.filter_childRows !== 'undefined' ? wo.filter_childRows : true)) ? cr.text() : '';
218
+ $td = $tr.eq(j).children('td');
219
+ for (i = 0; i < cols; i++) {
220
+ x = $.trim($td.eq(i).text());
221
+ xi = wo.filter_ignoreCase ? x.toLocaleLowerCase() : x;
222
+ // ignore if filter is empty
223
+ if (v[i] !== '') {
224
+ ff = r; // if r is true, show that row
225
+ // val = case insensitive, v[i] = case sensitive
226
+ val = wo.filter_ignoreCase ? v[i].toLocaleLowerCase() : v[i];
227
+ if (wo.filter_functions && wo.filter_functions[i]) {
228
+ if (wo.filter_functions[i] === true) {
229
+ // default selector; no "filter-select" class
230
+ ff = wo.filter_ignoreCase ? val === xi : v[i] === x;
231
+ } else if (typeof wo.filter_functions[i] === 'function') {
232
+ // filter callback( exact cell content, parser normalized content, filter input value, column index )
233
+ ff = wo.filter_functions[i](x, c.cache[k].normalized[j][i], v[i], i);
234
+ } else if (typeof wo.filter_functions[i][v[i]] === 'function'){
235
+ // selector option function
236
+ ff = wo.filter_functions[i][v[i]](x, c.cache[k].normalized[j][i], v[i], i);
237
+ }
238
+ // Look for regex
239
+ } else if (regexp.test(val)) {
240
+ reg2 = regexp.exec(val);
241
+ ff = new RegExp(reg2[1], reg2[2]).test(xi);
242
+ // Look for quotes to get an exact match
243
+ } else if (/[\"|\']$/.test(val) && xi === val.replace(/(\"|\')/g,'')) {
244
+ r = (r) ? true : false;
245
+ // Look for wild card: ? = single, or * = multiple
246
+ } else if (/[\?|\*]/.test(val)) {
247
+ ff = new RegExp( val.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(xi);
248
+ // Look for match, and add child row data for matching
249
+ } else {
250
+ x = (xi + t).indexOf(val);
251
+ if ( (!wo.filter_startsWith && x >= 0) || (wo.filter_startsWith && x === 0) ) {
252
+ r = (r) ? true : false;
253
+ } else {
254
+ r = false;
255
+ }
256
+ }
257
+ r = (ff) ? (r ? true : false) : false;
258
+ }
259
+ }
260
+ $tr[j].style.display = (r ? '' : 'none');
261
+ if (cr.length) { cr[r ? 'show' : 'hide'](); }
262
+ }
263
+ }
264
+ $tb.removeClass('tablesorter-hidden');
265
+ }
266
+ if (c.debug) {
267
+ $.tablesorter.benchmark("Completed filter widget search", time);
268
+ }
269
+ $t.trigger('applyWidgets'); // make sure zebra widget is applied
270
+ },
271
+ buildSelect = function(i){
272
+ var o, arry = [];
273
+ i = parseInt(i, 10);
274
+ o = '<option value="">' + ($(c.headerList[i]).attr('data-placeholder') || '') + '</option>';
275
+ for (k = 0; k < b.length; k++ ) {
276
+ l = c.cache[k].row.length;
277
+ // loop through the rows
278
+ for (j = 0; j < l; j++) {
279
+ // get non-normalized cell content
280
+ t = c.cache[k].row[j][0].cells[i];
281
+ arry.push( c.supportsTextContent ? t.textContent : $(t).text() );
282
+ }
283
+ }
284
+ // get unique elements and sort the list
285
+ arry = arry.getUnique(true);
286
+ // build option list
287
+ for (k = 0; k < arry.length; k++) {
288
+ o += '<option value="' + arry[k] + '">' + arry[k] + '</option>';
289
+ }
290
+ $t.find('thead').find('select.' + css + '[data-col="' + i + '"]').append(o);
291
+ };
193
292
  if (c.debug) {
194
293
  time = new Date();
195
294
  }
196
295
  for (i=0; i < cols; i++){
197
- fr += '<td><input type="search" data-col="' + i + '" class="' + css;
296
+ sel = (wo.filter_functions && wo.filter_functions[i] && typeof wo.filter_functions[i] !== 'function') || $(c.headerList[i]).hasClass('filter-select');
297
+ fr += '<td>';
298
+ if (sel){
299
+ fr += '<select data-col="' + i + '" class="' + css;
300
+ } else {
301
+ fr += '<input type="search" placeholder="' + ($(c.headerList[i]).attr('data-placeholder') || "") + '" data-col="' + i + '" class="' + css;
302
+ }
198
303
  // use header option - headers: { 1: { filter: false } } OR add class="filter-false"
199
304
  if ($.tablesorter.getData) {
200
305
  // get data from jQuery data, metadata, headers option or header class name
201
306
  fr += $.tablesorter.getData(c.headerList[i], c.headers[i], 'filter') === 'false' ? ' disabled" disabled' : '"';
202
307
  } else {
203
308
  // only class names and header options - keep this for compatibility with tablesorter v2.0.5
204
- fr += ((c.headers[i] && c.headers[i].hasOwnProperty('filter') && c.headers[i].filter === false) || $(c.headerList[i]).is('.filter-false') ) ? ' disabled" disabled' : '"';
309
+ fr += ((c.headers[i] && c.headers[i].hasOwnProperty('filter') && c.headers[i].filter === false) || $(c.headerList[i]).hasClass('filter-false') ) ? ' disabled" disabled' : '"';
205
310
  }
206
- fr += '></td>';
311
+ fr += (sel ? '></select>' : '>') + '</td>';
207
312
  }
208
313
  $t
209
- .find('thead').eq(0).append(fr += '</tr>')
210
- .find('input.' + css).bind('keyup search', function(e){
211
- v = $t.find('thead').eq(0).children('tr').find('input.' + css).map(function(){ return ($(this).val() || '').toLowerCase(); }).get();
212
- if (v.join('') === '') {
213
- $t.find('tr').show();
214
- } else {
215
- // *** to do *** optimize this to work better with very large tables
216
- $t.children('tbody:not(.' + c.cssInfoBlock + ')').children('tr:not(.' + c.cssChildRow + ')').each(function(){
217
- r = true;
218
- cr = $(this).nextUntil('tr:not(.' + c.cssChildRow + ')');
219
- // so, if "table.config.widgetOptions.filter_childRows" is true and there is
220
- // a match anywhere in the child row, then it will make the row visible
221
- // checked here so the option can be changed dynamically
222
- t = (cr.length && (wo && wo.hasOwnProperty('filter_childRows') &&
223
- typeof wo.filter_childRows !== 'undefined' ? wo.filter_childRows : true)) ? cr.text() : '';
224
- $td = $(this).children('td');
225
- for (i=0; i < cols; i++){
226
- x = $.trim(($td.eq(i).text() + t)).toLowerCase().indexOf(v[i]);
227
- if (v[i] !== '' && ( (!wo.filter_startsWith && x >= 0) || (wo.filter_startsWith && x === 0) ) ) {
228
- r = (r) ? true : false;
229
- } else if (v[i] !== '') {
230
- r = false;
231
- }
314
+ .find('thead').eq(0).append(fr += '</tr>')
315
+ .find('input.' + css).bind('keyup search', function(e, delay){
316
+ // ignore arrow and meta keys; allow backspace
317
+ if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; }
318
+ // skip delay
319
+ if (delay === false) {
320
+ findRows();
321
+ return;
322
+ }
323
+ // delay filtering
324
+ clearTimeout(timer);
325
+ timer = setTimeout(function(){
326
+ findRows();
327
+ }, wo.filter_searchDelay || 300);
328
+ });
329
+ if (wo.filter_functions) {
330
+ // i = column # (string)
331
+ for (i in wo.filter_functions) {
332
+ t = $(c.headerList[i]);
333
+ fr = '';
334
+ if (typeof i === 'string' && wo.filter_functions[i] === true && !t.hasClass('filter-false')) {
335
+ buildSelect(i);
336
+ } else if (typeof i === 'string' && !t.hasClass('filter-false')) {
337
+ // add custom drop down list
338
+ for (j in wo.filter_functions[i]) {
339
+ if (typeof j === 'string') {
340
+ fr += fr === '' ? '<option>' + (t.attr('data-placeholder') || '') + '</option>' : '';
341
+ fr += '<option>' + j + '</option>';
232
342
  }
233
- $(this)[r ? 'show' : 'hide']();
234
- if (cr.length) { cr[r ? 'show' : 'hide'](); }
235
- });
343
+ }
344
+ $t.find('thead').find('select.' + css + '[data-col="' + i + '"]').append(fr);
236
345
  }
237
- $t.trigger('applyWidgets'); // make sure zebra widget is applied
238
- });
346
+ }
347
+ }
348
+ // build default select dropdown
349
+ for (i = 0; i < c.headerList.length; i++) {
350
+ t = $(c.headerList[i]);
351
+ // look for the filter-select class, but don't build it twice.
352
+ if (t.hasClass('filter-select') && !t.hasClass('filter-false') && !(wo.filter_functions && wo.filter_functions[i] === true)){
353
+ buildSelect(i);
354
+ }
355
+ }
356
+
357
+ $t.find('select.' + css).bind('change', function(){
358
+ findRows();
359
+ });
360
+
239
361
  if (c.debug) {
240
362
  $.tablesorter.benchmark("Applying Filter widget", time);
241
363
  }
@@ -286,11 +408,11 @@ $.tablesorter.addWidget({
286
408
  });
287
409
  // set sticky header cell width and link clicks to real header
288
410
  hdrCells.each(function(i){
289
- var t = $(this),
290
- s = stkyCells.eq(i)
411
+ var t = $(this);
412
+ stkyCells.eq(i)
291
413
  // clicking on sticky will trigger sort
292
- .bind('click', function(e){
293
- t.trigger(e);
414
+ .bind('mouseup', function(e){
415
+ t.trigger(e, true); // external mouseup flag (click timer is ignored)
294
416
  })
295
417
  // prevent sticky header text selection
296
418
  .bind('mousedown', function(){
@@ -343,12 +465,11 @@ $.tablesorter.addWidget({
343
465
  format: function(table) {
344
466
  if ($(table).hasClass('hasResizable')) { return; }
345
467
  $(table).addClass('hasResizable');
346
- var i, j, w, s, c = table.config,
468
+ var j, s, c = table.config,
347
469
  $cols = $(c.headerList).filter(':gt(0)'),
348
470
  position = 0,
349
471
  $target = null,
350
472
  $prev = null,
351
- len = $cols.length,
352
473
  stopResize = function(){
353
474
  position = 0;
354
475
  $target = $prev = null;
@@ -372,8 +493,7 @@ $.tablesorter.addWidget({
372
493
  .bind('mousemove', function(e){
373
494
  // ignore mousemove if no mousedown
374
495
  if (position === 0 || !$target) { return; }
375
- var w = e.pageX - position,
376
- n = $prev;
496
+ var w = e.pageX - position;
377
497
  // make sure
378
498
  if ( $target.width() < -w || ( $prev && $prev.width() <= w )) { return; }
379
499
  // resize current column
@@ -394,6 +514,7 @@ $.tablesorter.addWidget({
394
514
  $target = $(e.target).closest('th');
395
515
  $prev = $target.prev();
396
516
  position = e.pageX;
517
+ return false;
397
518
  });
398
519
  $(table).find('thead').bind('mouseup mouseleave', function(){
399
520
  stopResize();
@@ -412,7 +533,7 @@ $.tablesorter.addWidget({
412
533
  thisWidget.format(table, true);
413
534
  },
414
535
  format: function(table, init) {
415
- var n, d, k, sl, time, c = table.config, sortList = { "sortList" : c.sortList };
536
+ var sl, time, c = table.config, sortList = { "sortList" : c.sortList };
416
537
  if (c.debug) {
417
538
  time = new Date();
418
539
  }
@@ -447,4 +568,17 @@ $.tablesorter.addWidget({
447
568
  }
448
569
  });
449
570
 
450
- })(jQuery);
571
+ })(jQuery);
572
+
573
+ // return an array with unique values https://gist.github.com/461516
574
+ Array.prototype.getUnique = function(s){
575
+ var c, a = [], o = {}, i, j = 0, l = this.length;
576
+ for(i=0; i < l; ++i) {
577
+ c = this[i];
578
+ if (!o[c]) {
579
+ o[c] = {};
580
+ a[j++] = c;
581
+ }
582
+ }
583
+ return (s) ? a.sort() : a;
584
+ };
@@ -59,6 +59,10 @@ table.tablesorter th.tablesorter-headerSortDown {
59
59
  /* image */
60
60
  /* background-image: url(black-desc.gif); */
61
61
  }
62
+ /* used to hide a tbody while rebuilding to speed it up */
63
+ table.tablesorter .tablesorter-hidden {
64
+ display: none;
65
+ }
62
66
 
63
67
  /* Zebra Widget - row alternating colors */
64
68
  table.tablesorter tr.odd td {
@@ -91,7 +95,8 @@ table.tablesorter tr.even td.tertiary {
91
95
  }
92
96
 
93
97
  /* filter widget */
94
- table.tablesorter input.tablesorter-filter {
98
+ table.tablesorter input.tablesorter-filter,
99
+ table.tablesorter select.tablesorter-filter {
95
100
  width: 95%;
96
101
  height: inherit;
97
102
  -webkit-box-sizing: border-box;
@@ -104,7 +109,8 @@ table.tablesorter tr.tablesorter-filter td {
104
109
  background: #fff;
105
110
  }
106
111
  /* optional disabled input styling */
107
- table.tablesorter input.tablesorter-filter.disabled {
112
+ table.tablesorter input.tablesorter-filter.disabled,
113
+ table.tablesorter select.tablesorter-filter.disabled {
108
114
  opacity: 0.5;
109
115
  filter: alpha(opacity=50);
110
116
  }
@@ -39,10 +39,16 @@ table.tablesorter tbody td {
39
39
  /* This allows you to use ui-state-default as the zebra stripe color */
40
40
  table.tablesorter tr.ui-state-default {
41
41
  background-image: url();
42
+ font-weight: normal;
43
+ }
44
+ /* used to hide a tbody while rebuilding to speed it up */
45
+ table.tablesorter .tablesorter-hidden {
46
+ display: none;
42
47
  }
43
48
 
44
49
  /* filter widget */
45
- table.tablesorter thead tr.tablesorter-filter input.tablesorter-filter {
50
+ table.tablesorter thead tr.tablesorter-filter input.tablesorter-filter,
51
+ table.tablesorter thead tr.tablesorter-filter select.tablesorter-filter {
46
52
  width: 95%;
47
53
  height: inherit;
48
54
  -webkit-box-sizing: border-box;
@@ -54,7 +60,8 @@ table.tablesorter thead tr.tablesorter-filter td {
54
60
  text-align: center;
55
61
  }
56
62
  /* optional disabled input styling */
57
- table.tablesorter thead tr.tablesorter-filter input.tablesorter-filter.disabled {
63
+ table.tablesorter thead tr.tablesorter-filter input.tablesorter-filter.disabled,
64
+ table.tablesorter thead tr.tablesorter-filter select.tablesorter-filter.disabled {
58
65
  opacity: 0.5;
59
66
  filter: alpha(opacity=50);
60
67
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jquery-tablesorter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-01 00:00:00.000000000 Z
12
+ date: 2012-06-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: railties
@@ -63,7 +63,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
63
63
  version: '0'
64
64
  segments:
65
65
  - 0
66
- hash: 2900046933026637168
66
+ hash: 1964242122684658673
67
67
  required_rubygems_version: !ruby/object:Gem::Requirement
68
68
  none: false
69
69
  requirements:
@@ -72,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
72
  version: '0'
73
73
  segments:
74
74
  - 0
75
- hash: 2900046933026637168
75
+ hash: 1964242122684658673
76
76
  requirements: []
77
77
  rubyforge_project:
78
78
  rubygems_version: 1.8.23