jquery-tablesorter 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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