jquery-tablesorter 1.8.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e6f685058fd350d114a2f272290ad8a7e5c3f7ed
4
- data.tar.gz: e9de584dc9a659b30b9bd79dec2a863cadf04749
3
+ metadata.gz: af7c049ad22e3ce61522a2bf73e2b73526a3350d
4
+ data.tar.gz: 6d6a5a150b0f3888edc4111c795a2f8deb07c297
5
5
  SHA512:
6
- metadata.gz: 49d6f67cce6923d1ac1fb24d651673dc3162608e41dfb70349fb3ec37c61b815f5cd4462887d2f891cdf1a05a90603d09bd4d45289f6e605270590f2cb744958
7
- data.tar.gz: d8c7d5b3890bea49ed049a17600ef2695a205450e432fac80d72b1835e69eb3c8edb7fddfd4517bef9d47207107186696c1fdee1b95992a10562871d357d21a2
6
+ metadata.gz: 2cd353a474b4912ef182752de68191dc376a4cd899ac4b08e621943469f1bdfec026a7827b0abcdb9232cee10ff7462217c1032bd3ade5389a66ac94d96b811b
7
+ data.tar.gz: c9f19227c27c964a19ea663ef944150aa22b8d0ecfc9ccb1f1f74d731366379672a32651042ae0330b1b2a440e457da7d9d87669ae5226a84699d4b01bf06e93
@@ -4,7 +4,7 @@
4
4
 
5
5
  Simple integration of jquery-tablesorter into the asset pipeline.
6
6
 
7
- Current tablesorter version: 2.13.3 (11/9/2013), [documentation]
7
+ Current tablesorter version: 2.14.0 (11/19/2013), [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.8.1"
2
+ VERSION = "1.9.0"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  /**!
2
- * TableSorter 2.13.3 - Client-side table sorting with ease!
2
+ * TableSorter 2.14.0 - 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.13.3";
27
+ ts.version = "2.14.0";
28
28
 
29
29
  ts.parsers = [];
30
30
  ts.widgets = [];
@@ -41,6 +41,7 @@
41
41
 
42
42
  // *** functionality
43
43
  cancelSelection : true, // prevent text selection in the header
44
+ tabIndex : true, // add tabindex to header for keyboard accessibility
44
45
  dateFormat : 'mmddyyyy', // other options: "ddmmyyy" or "yyyymmdd"
45
46
  sortMultiSortKey : 'shiftKey', // key used to select additional columns
46
47
  sortResetKey : 'ctrlKey', // key used to remove sorting on a column
@@ -54,6 +55,7 @@
54
55
  sortForce : null, // column(s) first sorted; always applied
55
56
  sortList : [], // Initial sort order; applied initially; updated when manually sorted
56
57
  sortAppend : null, // column(s) sorted last; always applied
58
+ sortStable : false, // when sorting two rows with exactly the same content, the original sort order is maintained
57
59
 
58
60
  sortInitialOrder : 'asc', // sort direction on first click
59
61
  sortLocaleCompare: false, // replace equivalent character (accented characters)
@@ -413,7 +415,8 @@
413
415
  if (c.debug) {
414
416
  time = new Date();
415
417
  }
416
- i = c.cssIcon ? '<i class="' + c.cssIcon + ' ' + ts.css.icon + '"></i>' : ''; // add icon if cssIcon option exists
418
+ // add icon if cssIcon option exists
419
+ i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : '';
417
420
  c.$headers = $(table).find(c.selectorHeaders).each(function(index) {
418
421
  $t = $(this);
419
422
  ch = c.headers[index];
@@ -442,7 +445,7 @@
442
445
  // add to parent in case there are multiple rows
443
446
  $t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow);
444
447
  // allow keyboard cursor to focus on element
445
- $t.attr("tabindex", 0);
448
+ if (c.tabIndex) { $t.attr("tabindex", 0); }
446
449
  });
447
450
  // enable/disable sorting
448
451
  updateHeader(table);
@@ -638,7 +641,7 @@
638
641
 
639
642
  // sort multiple columns
640
643
  function multisort(table) { /*jshint loopfunc:true */
641
- var i, k, e, num, col, colMax, cache, lc,
644
+ var i, k, num, col, colMax, cache, lc,
642
645
  order, orgOrderCol, sortTime, sort, x, y,
643
646
  dir = 0,
644
647
  c = table.config,
@@ -663,14 +666,9 @@
663
666
  // sort direction, true = asc, false = desc
664
667
  dir = order === 0;
665
668
 
666
- // set a & b depending on sort direction
667
- x = dir ? a : b;
668
- y = dir ? b : a;
669
-
670
- // determine how to sort empty cells
671
- e = c.string[ (c.empties[col] || c.emptyTo ) ];
672
- if (x[col] === '' && e !== 0) { return ((typeof(e) === 'boolean') ? (e ? -1 : 1) : (e || 1)) * (dir ? 1 : -1); }
673
- if (y[col] === '' && e !== 0) { return ((typeof(e) === 'boolean') ? (e ? 1 : -1) : (-e || -1)) * (dir ? 1 : -1); }
669
+ if (c.sortStable && a[col] === b[col] && l === 1) {
670
+ return a[orgOrderCol] - b[orgOrderCol];
671
+ }
674
672
 
675
673
  // fallback to natural sort since it is more robust
676
674
  num = /n/i.test(getCachedSortType(c.parsers, col));
@@ -683,8 +681,12 @@
683
681
  }
684
682
  // fall back to built-in numeric sort
685
683
  // var sort = $.tablesorter["sort" + s](table, a[c], b[c], c, colMax[c], dir);
686
- sort = c.numberSorter ? c.numberSorter(x[col], y[col], dir, colMax[col], table) : ts.sortNumeric(x[col], y[col], num, colMax[col]);
684
+ sort = c.numberSorter ? c.numberSorter(x[col], y[col], dir, colMax[col], table) :
685
+ ts[ 'sortNumeric' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], num, colMax[col], col, table);
687
686
  } else {
687
+ // set a & b depending on sort direction
688
+ x = dir ? a : b;
689
+ y = dir ? b : a;
688
690
  // text sort function
689
691
  if (typeof(cts) === 'function') {
690
692
  // custom OVERALL text sorter
@@ -694,7 +696,7 @@
694
696
  sort = cts[col](x[col], y[col], dir, col, table);
695
697
  } else {
696
698
  // fall back to natural sort
697
- sort = ts.sortNatural(x[col], y[col]);
699
+ sort = ts[ 'sortNatural' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], col, table, c);
698
700
  }
699
701
  }
700
702
  if (sort) { return sort; }
@@ -1080,6 +1082,7 @@
1080
1082
  if ( xD < yD ) { return -1; }
1081
1083
  if ( xD > yD ) { return 1; }
1082
1084
  }
1085
+
1083
1086
  // chunk/tokenize
1084
1087
  xN = a.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0');
1085
1088
  yN = b.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0');
@@ -1102,6 +1105,22 @@
1102
1105
  return 0;
1103
1106
  };
1104
1107
 
1108
+ ts.sortNaturalAsc = function(a, b, col, table, c) {
1109
+ if (a === b) { return 0; }
1110
+ var e = c.string[ (c.empties[col] || c.emptyTo ) ];
1111
+ if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; }
1112
+ if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; }
1113
+ return ts.sortNatural(a, b);
1114
+ };
1115
+
1116
+ ts.sortNaturalDesc = function(a, b, col, table, c) {
1117
+ if (a === b) { return 0; }
1118
+ var e = c.string[ (c.empties[col] || c.emptyTo ) ];
1119
+ if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; }
1120
+ if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; }
1121
+ return ts.sortNatural(b, a);
1122
+ };
1123
+
1105
1124
  // basic alphabetical sort
1106
1125
  ts.sortText = function(a, b) {
1107
1126
  return a > b ? 1 : (a < b ? -1 : 0);
@@ -1110,22 +1129,41 @@
1110
1129
  // return text string value by adding up ascii value
1111
1130
  // so the text is somewhat sorted when using a digital sort
1112
1131
  // this is NOT an alphanumeric sort
1113
- ts.getTextValue = function(a, d, mx) {
1132
+ ts.getTextValue = function(a, num, mx) {
1114
1133
  if (mx) {
1115
1134
  // make sure the text value is greater than the max numerical value (mx)
1116
- var i, l = a ? a.length : 0, n = mx + d;
1135
+ var i, l = a ? a.length : 0, n = mx + num;
1117
1136
  for (i = 0; i < l; i++) {
1118
1137
  n += a.charCodeAt(i);
1119
1138
  }
1120
- return d * n;
1139
+ return num * n;
1121
1140
  }
1122
1141
  return 0;
1123
1142
  };
1124
1143
 
1125
- ts.sortNumeric = function(a, b, dir, mx) {
1144
+ ts.sortNumericAsc = function(a, b, num, mx, col, table) {
1126
1145
  if (a === b) { return 0; }
1127
- if (isNaN(a)) { a = ts.getTextValue(a, dir, mx); }
1128
- if (isNaN(b)) { b = ts.getTextValue(b, dir, mx); }
1146
+ var c = table.config,
1147
+ e = c.string[ (c.empties[col] || c.emptyTo ) ];
1148
+ if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; }
1149
+ if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; }
1150
+ if (isNaN(a)) { a = ts.getTextValue(a, num, mx); }
1151
+ if (isNaN(b)) { b = ts.getTextValue(b, num, mx); }
1152
+ return a - b;
1153
+ };
1154
+
1155
+ ts.sortNumericDesc = function(a, b, num, mx, col, table) {
1156
+ if (a === b) { return 0; }
1157
+ var c = table.config,
1158
+ e = c.string[ (c.empties[col] || c.emptyTo ) ];
1159
+ if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; }
1160
+ if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; }
1161
+ if (isNaN(a)) { a = ts.getTextValue(a, num, mx); }
1162
+ if (isNaN(b)) { b = ts.getTextValue(b, num, mx); }
1163
+ return b - a;
1164
+ };
1165
+
1166
+ ts.sortNumeric = function(a, b) {
1129
1167
  return a - b;
1130
1168
  };
1131
1169
 
@@ -1515,7 +1553,7 @@
1515
1553
  l = $tb.children('tr').length;
1516
1554
  if (l > 1) {
1517
1555
  row = 0;
1518
- $tv = $tb.children('tr:visible');
1556
+ $tv = $tb.children('tr:visible').not(c.selectorRemove);
1519
1557
  // revered back to using jQuery each - strangely it's the fastest method
1520
1558
  /*jshint loopfunc:true */
1521
1559
  $tv.each(function(){
@@ -1,4 +1,4 @@
1
- /*! tableSorter 2.8+ widgets - updated 11/9/2013
1
+ /*! tableSorter 2.8+ widgets - updated 11/19/2013
2
2
  *
3
3
  * Column Styles
4
4
  * Column Filters
@@ -10,7 +10,7 @@
10
10
  */
11
11
  /*jshint browser:true, jquery:true, unused:false, loopfunc:true */
12
12
  /*global jQuery: false, localStorage: false, navigator: false */
13
- ;(function($){
13
+ ;(function($) {
14
14
  "use strict";
15
15
  var ts = $.tablesorter = $.tablesorter || {};
16
16
 
@@ -68,86 +68,95 @@ ts.themes = {
68
68
  val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : '';
69
69
  alert(val); // "data1" if saved, or "" if not
70
70
  */
71
- ts.storage = function(table, key, val, options){
72
- var d, k, ls = false, v = {},
73
- c = table.config,
74
- id = options && options.id || $(table).attr(options && options.group || 'data-table-group') || table.id || $('.tablesorter').index( $(table) ),
75
- url = options && options.url || $(table).attr(options && options.page || 'data-table-page') || c && c.fixedUrl || window.location.pathname;
71
+ ts.storage = function(table, key, value, options) {
72
+ table = $(table)[0];
73
+ var cookieIndex, cookies, date,
74
+ hasLocalStorage = false,
75
+ values = {},
76
+ c = table.config,
77
+ $table = $(table),
78
+ id = options && options.id || $table.attr(options && options.group ||
79
+ 'data-table-group') || table.id || $('.tablesorter').index( $table ),
80
+ url = options && options.url || $table.attr(options && options.page ||
81
+ 'data-table-page') || c && c.fixedUrl || window.location.pathname;
76
82
  // https://gist.github.com/paulirish/5558557
77
83
  if ("localStorage" in window) {
78
84
  try {
79
85
  window.localStorage.setItem('_tmptest', 'temp');
80
- ls = true;
86
+ hasLocalStorage = true;
81
87
  window.localStorage.removeItem('_tmptest');
82
- } catch(e) {}
88
+ } catch(error) {}
83
89
  }
84
- // *** get val ***
85
- if ($.parseJSON){
86
- if (ls){
87
- v = $.parseJSON(localStorage[key] || '{}');
90
+ // *** get value ***
91
+ if ($.parseJSON) {
92
+ if (hasLocalStorage) {
93
+ values = $.parseJSON(localStorage[key] || '{}');
88
94
  } else {
89
- k = document.cookie.split(/[;\s|=]/); // cookie
90
- d = $.inArray(key, k) + 1; // add one to get from the key to the value
91
- v = (d !== 0) ? $.parseJSON(k[d] || '{}') : {};
95
+ // old browser, using cookies
96
+ cookies = document.cookie.split(/[;\s|=]/);
97
+ // add one to get from the key to the value
98
+ cookieIndex = $.inArray(key, cookies) + 1;
99
+ values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || '{}') : {};
92
100
  }
93
101
  }
94
- // allow val to be an empty string to
95
- if ((val || val === '') && window.JSON && JSON.hasOwnProperty('stringify')){
102
+ // allow value to be an empty string too
103
+ if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) {
96
104
  // add unique identifiers = url pathname > table ID/index on page > data
97
- if (!v[url]) {
98
- v[url] = {};
105
+ if (!values[url]) {
106
+ values[url] = {};
99
107
  }
100
- v[url][id] = val;
101
- // *** set val ***
102
- if (ls){
103
- localStorage[key] = JSON.stringify(v);
108
+ values[url][id] = value;
109
+ // *** set value ***
110
+ if (hasLocalStorage) {
111
+ localStorage[key] = JSON.stringify(values);
104
112
  } else {
105
- d = new Date();
106
- d.setTime(d.getTime() + (31536e+6)); // 365 days
107
- document.cookie = key + '=' + (JSON.stringify(v)).replace(/\"/g,'\"') + '; expires=' + d.toGMTString() + '; path=/';
113
+ date = new Date();
114
+ date.setTime(date.getTime() + (31536e+6)); // 365 days
115
+ document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g,'\"') + '; expires=' + date.toGMTString() + '; path=/';
108
116
  }
109
117
  } else {
110
- return v && v[url] ? v[url][id] : {};
118
+ return values && values[url] ? values[url][id] : {};
111
119
  }
112
120
  };
113
121
 
114
122
  // Add a resize event to table headers
115
123
  // **************************
116
- ts.addHeaderResizeEvent = function(table, disable, options){
117
- var defaults = {
118
- timer : 250
119
- },
120
- o = $.extend({}, defaults, options),
121
- c = table.config,
122
- wo = c.widgetOptions,
123
- headers,
124
- checkSizes = function(){
125
- wo.resize_flag = true;
126
- headers = [];
127
- c.$headers.each(function(){
128
- var d = $.data(this, 'savedSizes') || [0,0], // fixes #394
129
- w = this.offsetWidth,
130
- h = this.offsetHeight;
131
- if (w !== d[0] || h !== d[1]) {
132
- $.data(this, 'savedSizes', [ w, h ]);
133
- headers.push(this);
124
+ ts.addHeaderResizeEvent = function(table, disable, settings) {
125
+ var headers,
126
+ defaults = {
127
+ timer : 250
128
+ },
129
+ options = $.extend({}, defaults, settings),
130
+ c = table.config,
131
+ wo = c.widgetOptions,
132
+ checkSizes = function(triggerEvent) {
133
+ wo.resize_flag = true;
134
+ headers = [];
135
+ c.$headers.each(function() {
136
+ var $header = $(this),
137
+ sizes = $header.data('savedSizes') || [0,0], // fixes #394
138
+ width = this.offsetWidth,
139
+ height = this.offsetHeight;
140
+ if (width !== sizes[0] || height !== sizes[1]) {
141
+ $header.data('savedSizes', [ width, height ]);
142
+ headers.push(this);
143
+ }
144
+ });
145
+ if (headers.length && triggerEvent !== false) {
146
+ c.$table.trigger('resize', [ headers ]);
134
147
  }
135
- });
136
- if (headers.length) { c.$table.trigger('resize', [ headers ]); }
137
- wo.resize_flag = false;
138
- };
139
- c.$headers.each(function(){
140
- $.data(this, 'savedSizes', [ this.offsetWidth, this.offsetHeight ]);
141
- });
148
+ wo.resize_flag = false;
149
+ };
150
+ checkSizes(false);
142
151
  clearInterval(wo.resize_timer);
143
152
  if (disable) {
144
153
  wo.resize_flag = false;
145
154
  return false;
146
155
  }
147
- wo.resize_timer = setInterval(function(){
156
+ wo.resize_timer = setInterval(function() {
148
157
  if (wo.resize_flag) { return; }
149
158
  checkSizes();
150
- }, o.timer);
159
+ }, options.timer);
151
160
  };
152
161
 
153
162
  // Widget: General UI theme
@@ -156,89 +165,88 @@ ts.addHeaderResizeEvent = function(table, disable, options){
156
165
  ts.addWidget({
157
166
  id: "uitheme",
158
167
  priority: 10,
159
- options: {
160
- uitheme : 'jui'
161
- },
162
- format: function(table, c, wo){
163
- var time, klass, $el, $tar,
164
- t = ts.themes,
165
- $t = c.$table,
166
- theme = c.theme !== 'default' ? c.theme : wo.uitheme || 'jui',
167
- o = t[ t[theme] ? theme : t[wo.uitheme] ? wo.uitheme : 'jui'],
168
- $h = c.$headers,
169
- sh = 'tr.' + (wo.stickyHeaders || 'tablesorter-stickyHeader'),
170
- rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc;
168
+ format: function(table, c, wo) {
169
+ var time, classes, $header, $icon, $tfoot,
170
+ themesAll = ts.themes,
171
+ $table = c.$table,
172
+ $headers = c.$headers,
173
+ theme = c.theme || 'jui',
174
+ themes = themesAll[theme] || themesAll.jui,
175
+ remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc;
171
176
  if (c.debug) { time = new Date(); }
172
177
  // initialization code - run once
173
- if (!$t.hasClass('tablesorter-' + theme) || c.theme === theme || !table.hasInitialized){
178
+ if (!$table.hasClass('tablesorter-' + theme) || c.theme === theme || !table.hasInitialized) {
174
179
  // update zebra stripes
175
- if (o.even !== '') { wo.zebra[0] += ' ' + o.even; }
176
- if (o.odd !== '') { wo.zebra[1] += ' ' + o.odd; }
180
+ if (themes.even !== '') { wo.zebra[0] += ' ' + themes.even; }
181
+ if (themes.odd !== '') { wo.zebra[1] += ' ' + themes.odd; }
177
182
  // add caption style
178
- $t.find('caption').addClass(o.caption);
183
+ $table.find('caption').addClass(themes.caption);
179
184
  // add table/footer class names
180
- t = $t
181
- // remove other selected themes; use widgetOptions.theme_remove
185
+ $tfoot = $table
186
+ // remove other selected themes
182
187
  .removeClass( c.theme === '' ? '' : 'tablesorter-' + c.theme )
183
- .addClass('tablesorter-' + theme + ' ' + o.table) // add theme widget class name
188
+ .addClass('tablesorter-' + theme + ' ' + themes.table) // add theme widget class name
184
189
  .find('tfoot');
185
- if (t.length) {
186
- t
187
- .find('tr').addClass(o.footerRow)
188
- .children('th, td').addClass(o.footerCells);
190
+ if ($tfoot.length) {
191
+ $tfoot
192
+ .find('tr').addClass(themes.footerRow)
193
+ .children('th, td').addClass(themes.footerCells);
189
194
  }
190
195
  // update header classes
191
- $h
192
- .addClass(o.header)
193
- .filter(':not(.sorter-false)')
194
- .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(e){
196
+ $headers
197
+ .addClass(themes.header)
198
+ .not('.sorter-false')
199
+ .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) {
195
200
  // toggleClass with switch added in jQuery 1.3
196
- $(this)[ e.type === 'mouseenter' ? 'addClass' : 'removeClass' ](o.hover);
201
+ $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover);
197
202
  });
198
- if (!$h.find('.tablesorter-wrapper').length) {
199
- // Firefox needs this inner div to position the icon/resizer correctly
200
- $h.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>');
203
+ if (!$headers.find('.tablesorter-wrapper').length) {
204
+ // Firefox needs this inner div to position the resizer correctly
205
+ $headers.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>');
201
206
  }
202
- if (c.cssIcon){
207
+ if (c.cssIcon) {
203
208
  // if c.cssIcon is '', then no <i> is added to the header
204
- $h.find('.' + ts.css.icon).addClass(o.icons);
209
+ $headers.find('.' + ts.css.icon).addClass(themes.icons);
205
210
  }
206
- if ($t.hasClass('hasFilters')){
207
- $h.find('.tablesorter-filter-row').addClass(o.filterRow);
211
+ if ($table.hasClass('hasFilters')) {
212
+ $headers.find('.tablesorter-filter-row').addClass(themes.filterRow);
208
213
  }
209
214
  }
210
- $.each($h, function(i){
211
- $el = $(this);
212
- $tar = (ts.css.icon) ? $el.find('.' + ts.css.icon) : $el;
213
- if (this.sortDisabled){
215
+ $.each($headers, function() {
216
+ $header = $(this);
217
+ $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $header;
218
+ if (this.sortDisabled) {
214
219
  // no sort arrows for disabled columns!
215
- $el.removeClass(rmv);
216
- $tar.removeClass(rmv + ' tablesorter-icon ' + o.icons);
220
+ $header.removeClass(remove);
221
+ $icon.removeClass(remove + ' tablesorter-icon ' + themes.icons);
217
222
  } else {
218
- t = ($t.hasClass('hasStickyHeaders')) ? $t.find(sh).find('th').eq(i).add($el) : $el;
219
- klass = ($el.hasClass(ts.css.sortAsc)) ? o.sortAsc : ($el.hasClass(ts.css.sortDesc)) ? o.sortDesc : $el.hasClass(ts.css.header) ? o.sortNone : '';
220
- $el[klass === o.sortNone ? 'removeClass' : 'addClass'](o.active);
221
- $tar.removeClass(rmv).addClass(klass);
223
+ classes = ($header.hasClass(ts.css.sortAsc)) ?
224
+ themes.sortAsc :
225
+ ($header.hasClass(ts.css.sortDesc)) ? themes.sortDesc :
226
+ $header.hasClass(ts.css.header) ? themes.sortNone : '';
227
+ $header[classes === themes.sortNone ? 'removeClass' : 'addClass'](themes.active);
228
+ $icon.removeClass(remove).addClass(classes);
222
229
  }
223
230
  });
224
- if (c.debug){
231
+ if (c.debug) {
225
232
  ts.benchmark("Applying " + theme + " theme", time);
226
233
  }
227
234
  },
228
- remove: function(table, c, wo){
229
- var $t = c.$table,
230
- theme = typeof wo.uitheme === 'object' ? 'jui' : wo.uitheme || 'jui',
231
- o = typeof wo.uitheme === 'object' ? wo.uitheme : ts.themes[ ts.themes.hasOwnProperty(theme) ? theme : 'jui'],
232
- $h = $t.children('thead').children(),
233
- rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc;
234
- $t
235
- .removeClass('tablesorter-' + theme + ' ' + o.table)
236
- .find(ts.css.header).removeClass(o.header);
237
- $h
235
+ remove: function(table, c, wo) {
236
+ var $table = c.$table,
237
+ theme = c.theme || 'jui',
238
+ themes = ts.themes[ theme ] || ts.themes.jui,
239
+ $headers = $table.children('thead').children(),
240
+ remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc;
241
+ $table
242
+ .removeClass('tablesorter-' + theme + ' ' + themes.table)
243
+ .find(ts.css.header).removeClass(themes.header);
244
+ $headers
238
245
  .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover
239
- .removeClass(o.hover + ' ' + rmv + ' ' + o.active)
240
- .find('.tablesorter-filter-row').removeClass(o.filterRow);
241
- $h.find('.tablesorter-icon').removeClass(o.icons);
246
+ .removeClass(themes.hover + ' ' + remove + ' ' + themes.active)
247
+ .find('.tablesorter-filter-row')
248
+ .removeClass(themes.filterRow);
249
+ $headers.find('.tablesorter-icon').removeClass(themes.icons);
242
250
  }
243
251
  });
244
252
 
@@ -252,76 +260,74 @@ ts.addWidget({
252
260
  options : {
253
261
  columns : [ "primary", "secondary", "tertiary" ]
254
262
  },
255
- format: function(table, c, wo){
256
- var $tb, $tr, $td, $t, time, last, rmv, i, k, l,
257
- $tbl = c.$table,
258
- b = c.$tbodies,
259
- list = c.sortList,
260
- len = list.length,
261
- // keep backwards compatibility, for now
262
- css = (c.widgetColumns && c.widgetColumns.hasOwnProperty('css')) ? c.widgetColumns.css || css :
263
- (wo && wo.hasOwnProperty('columns')) ? wo.columns || css : css;
264
- last = css.length-1;
265
- rmv = css.join(' ');
266
- if (c.debug){
263
+ format: function(table, c, wo) {
264
+ var time, $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx,
265
+ $table = c.$table,
266
+ $tbodies = c.$tbodies,
267
+ sortList = c.sortList,
268
+ len = sortList.length,
269
+ // removed c.widgetColumns support
270
+ css = wo && wo.columns || [ "primary", "secondary", "tertiary" ],
271
+ last = css.length - 1;
272
+ remove = css.join(' ');
273
+ if (c.debug) {
267
274
  time = new Date();
268
275
  }
269
276
  // check if there is a sort (on initialization there may not be one)
270
- for (k = 0; k < b.length; k++ ){
271
- $tb = ts.processTbody(table, b.eq(k), true); // detach tbody
272
- $tr = $tb.children('tr');
273
- l = $tr.length;
277
+ for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) {
278
+ $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody
279
+ $rows = $tbody.children('tr');
274
280
  // loop through the visible rows
275
- $tr.each(function(){
276
- $t = $(this);
277
- if (this.style.display !== 'none'){
281
+ $rows.each(function() {
282
+ $row = $(this);
283
+ if (this.style.display !== 'none') {
278
284
  // remove all columns class names
279
- $td = $t.children().removeClass(rmv);
285
+ $cells = $row.children().removeClass(remove);
280
286
  // add appropriate column class names
281
- if (list && list[0]){
287
+ if (sortList && sortList[0]) {
282
288
  // primary sort column class
283
- $td.eq(list[0][0]).addClass(css[0]);
284
- if (len > 1){
285
- for (i = 1; i < len; i++){
289
+ $cells.eq(sortList[0][0]).addClass(css[0]);
290
+ if (len > 1) {
291
+ for (indx = 1; indx < len; indx++) {
286
292
  // secondary, tertiary, etc sort column classes
287
- $td.eq(list[i][0]).addClass( css[i] || css[last] );
293
+ $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] );
288
294
  }
289
295
  }
290
296
  }
291
297
  }
292
298
  });
293
- ts.processTbody(table, $tb, false);
299
+ ts.processTbody(table, $tbody, false);
294
300
  }
295
301
  // add classes to thead and tfoot
296
- $tr = wo.columns_thead !== false ? ['thead tr'] : [];
302
+ rows = wo.columns_thead !== false ? ['thead tr'] : [];
297
303
  if (wo.columns_tfoot !== false) {
298
- $tr.push('tfoot tr');
304
+ rows.push('tfoot tr');
299
305
  }
300
- if ($tr.length) {
301
- $t = $tbl.find($tr.join(',')).children().removeClass(rmv);
302
- if (len){
303
- for (i = 0; i < len; i++){
306
+ if (rows.length) {
307
+ $rows = $table.find( rows.join(',') ).children().removeClass(remove);
308
+ if (len) {
309
+ for (indx = 0; indx < len; indx++) {
304
310
  // add primary. secondary, tertiary, etc sort column classes
305
- $t.filter('[data-column="' + list[i][0] + '"]').addClass(css[i] || css[last]);
311
+ $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]);
306
312
  }
307
313
  }
308
314
  }
309
- if (c.debug){
315
+ if (c.debug) {
310
316
  ts.benchmark("Applying Columns widget", time);
311
317
  }
312
318
  },
313
- remove: function(table, c, wo){
314
- var k, $tb,
315
- b = c.$tbodies,
316
- rmv = (wo.columns || [ "primary", "secondary", "tertiary" ]).join(' ');
317
- c.$headers.removeClass(rmv);
318
- c.$table.children('tfoot').children('tr').children('th, td').removeClass(rmv);
319
- for (k = 0; k < b.length; k++ ){
320
- $tb = ts.processTbody(table, b.eq(k), true); // remove tbody
321
- $tb.children('tr').each(function(){
322
- $(this).children().removeClass(rmv);
319
+ remove: function(table, c, wo) {
320
+ var tbodyIndex, $tbody,
321
+ $tbodies = c.$tbodies,
322
+ remove = (wo.columns || [ "primary", "secondary", "tertiary" ]).join(' ');
323
+ c.$headers.removeClass(remove);
324
+ c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove);
325
+ for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) {
326
+ $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody
327
+ $tbody.children('tr').each(function() {
328
+ $(this).children().removeClass(remove);
323
329
  });
324
- ts.processTbody(table, $tb, false); // restore tbody
330
+ ts.processTbody(table, $tbody, false); // restore tbody
325
331
  }
326
332
  }
327
333
  });
@@ -344,6 +350,7 @@ ts.addWidget({
344
350
  filter_liveSearch : true, // if true, search column content while the user types (with a delay)
345
351
  filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available (visible) options within the drop down
346
352
  filter_reset : null, // jQuery selector string of an element used to reset the filters
353
+ filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters
347
354
  filter_searchDelay : 300, // typing delay in milliseconds before starting a search
348
355
  filter_startsWith : false, // if true, filter start from the beginning of the cell contents
349
356
  filter_useParsedData : false, // filter all data using parsed content
@@ -563,7 +570,8 @@ ts.filter = {
563
570
  // reset button/link
564
571
  if (wo.filter_reset) {
565
572
  $(document).delegate(wo.filter_reset, 'click.tsfilter', function() {
566
- ts.filter.searching(table, []);
573
+ // trigger a reset event, so other functions (filterFormatter) know when to reset
574
+ c.$table.trigger('filterReset');
567
575
  });
568
576
  }
569
577
  if (wo.filter_functions) {
@@ -573,7 +581,7 @@ ts.filter = {
573
581
  $header = c.$headers.filter('[data-column="' + column + '"]:last');
574
582
  options = '';
575
583
  if (wo.filter_functions[column] === true && !$header.hasClass('filter-false')) {
576
- ts.filter.buildSelect(column);
584
+ ts.filter.buildSelect(table, column);
577
585
  } else if (typeof column === 'string' && !$header.hasClass('filter-false')) {
578
586
  // add custom drop down list
579
587
  for (string in wo.filter_functions[column]) {
@@ -610,6 +618,7 @@ ts.filter = {
610
618
  ts.isProcessing(table, event.type === 'filterStart', columns ? $header : '');
611
619
  });
612
620
  }
621
+
613
622
  if (c.debug) {
614
623
  ts.benchmark("Applying Filter widget", time);
615
624
  }
@@ -619,18 +628,26 @@ ts.filter = {
619
628
  if (filters.length) {
620
629
  ts.setFilters(table, filters, true);
621
630
  }
631
+ ts.filter.checkFilters(table, filters);
622
632
  });
623
633
  // filter widget initialized
624
634
  wo.filter_Initialized = true;
625
635
  c.$table.trigger('filterInit');
626
- ts.filter.checkFilters(table);
627
636
  },
628
- setDefaults: function(table, c, wo){
637
+ setDefaults: function(table, c, wo) {
629
638
  var indx,
630
639
  filters = [],
631
640
  columns = c.columns;
632
- for (indx = 0; indx < columns; indx++) {
633
- filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx];
641
+ if (wo.filter_saveFilters && ts.storage) {
642
+ filters = ts.storage( table, 'tablesorter-filters' ) || [];
643
+ // make sure we're not just saving an empty array
644
+ if (filters.join('') === '') { filters = []; }
645
+ }
646
+ // if not filters saved, then check default settings
647
+ if (!filters.length) {
648
+ for (indx = 0; indx < columns; indx++) {
649
+ filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx];
650
+ }
634
651
  }
635
652
  $(table).data('lastSearch', filters);
636
653
  return filters;
@@ -692,7 +709,7 @@ ts.filter = {
692
709
  bindSearch: function(table, $el) {
693
710
  table = $(table)[0];
694
711
  var external, wo = table.config.widgetOptions;
695
- $el.bind('keyup search', function(event, filter) {
712
+ $el.unbind('keyup search').bind('keyup search', function(event, filter) {
696
713
  // emulate what webkit does.... escape clears the filter
697
714
  if (event.which === 27) {
698
715
  this.value = '';
@@ -703,7 +720,15 @@ ts.filter = {
703
720
  return;
704
721
  }
705
722
  // external searches won't have a filter parameter, so grab the value
706
- external = $(this).hasClass('tablesorter-filter') ? filter : [ $(this).val() ];
723
+ if ($(this).hasClass('tablesorter-filter')) {
724
+ external = filter;
725
+ } else {
726
+ external = [];
727
+ $el.each(function(){
728
+ // target the appropriate column if the external input has a data-column attribute
729
+ external[ $(this).data('column') || 0 ] = $(this).val();
730
+ });
731
+ }
707
732
  ts.filter.searching(table, filter, external);
708
733
  });
709
734
  },
@@ -782,7 +807,7 @@ ts.filter = {
782
807
  columns = c.columns,
783
808
  $tbodies = c.$tbodies,
784
809
  // anyMatch really screws up with these types of filters
785
- anyMatchNotAllowedTypes = [ 'range', 'operators' ],
810
+ anyMatchNotAllowedTypes = [ 'range', 'notMatch', 'operators' ],
786
811
  // parse columns after formatter, in case the class is added at that point
787
812
  parsed = c.$headers.map(function(columnIndex) {
788
813
  return (ts.getData) ?
@@ -793,7 +818,7 @@ ts.filter = {
793
818
  for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) {
794
819
  if ($tbodies.eq(tbodyIndex).hasClass(ts.css.info)) { continue; } // ignore info blocks, issue #264
795
820
  $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true);
796
- $rows = $tbody.children('tr').not('.' + c.cssChildRow);
821
+ $rows = $tbody.children('tr').not('.' + c.cssChildRow).not('.group-header');
797
822
  len = $rows.length;
798
823
  if (combinedFilters === '' || wo.filter_serversideFiltering) {
799
824
  $tbody.children().show().removeClass(wo.filter_filteredRow);
@@ -902,6 +927,9 @@ ts.filter = {
902
927
  c.lastCombinedFilter = combinedFilters; // save last search
903
928
  c.lastSearch = filters;
904
929
  c.$table.data('lastSearch', filters);
930
+ if (wo.filter_saveFilters && ts.storage) {
931
+ ts.storage( table, 'tablesorter-filters', filters );
932
+ }
905
933
  if (c.debug) {
906
934
  ts.benchmark("Completed filter widget search", time);
907
935
  }
@@ -1015,7 +1043,7 @@ ts.setFilters = function(table, filter, apply) {
1015
1043
  // **************************
1016
1044
  ts.addWidget({
1017
1045
  id: "stickyHeaders",
1018
- priority: 60,
1046
+ priority: 60, // sticky widget must be initialized after the filter widget!
1019
1047
  options: {
1020
1048
  stickyHeaders : '', // extra class name added to the sticky header row
1021
1049
  stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element
@@ -1024,55 +1052,54 @@ ts.addWidget({
1024
1052
  stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header
1025
1053
  stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs
1026
1054
  },
1027
- format: function(table, c, wo){
1055
+ format: function(table, c, wo) {
1028
1056
  if (c.$table.hasClass('hasStickyHeaders')) { return; }
1029
- var $t = c.$table,
1057
+ var $cell,
1058
+ $table = c.$table,
1030
1059
  $win = $(window),
1031
- header = $t.children('thead:first'),
1032
- hdrCells = header.children('tr:not(.sticky-false)').children(),
1033
- innr = '.tablesorter-header-inner',
1034
- tfoot = $t.find('tfoot'),
1060
+ $thead = $table.children('thead:first'),
1061
+ $header = $thead.children('tr').not('.sticky-false').children(),
1062
+ innerHeader = '.tablesorter-header-inner',
1063
+ $tfoot = $table.find('tfoot'),
1035
1064
  filterInputs = '.tablesorter-filter',
1036
1065
  $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '',
1037
1066
  stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0,
1038
- stickyzIndex = wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2,
1039
- $stickyTable = wo.$sticky = $t.clone()
1067
+ $stickyTable = wo.$sticky = $table.clone()
1040
1068
  .addClass('containsStickyHeaders')
1041
1069
  .css({
1042
1070
  position : 'fixed',
1043
1071
  margin : 0,
1044
1072
  top : stickyOffset,
1045
1073
  visibility : 'hidden',
1046
- zIndex : stickyzIndex
1074
+ zIndex : wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2
1047
1075
  }),
1048
- stkyHdr = $stickyTable.children('thead:first').addClass('tablesorter-stickyHeader ' + wo.stickyHeaders),
1049
- stkyCells,
1076
+ $stickyThead = $stickyTable.children('thead:first').addClass('tablesorter-stickyHeader ' + wo.stickyHeaders),
1077
+ $stickyCells,
1050
1078
  laststate = '',
1051
1079
  spacing = 0,
1052
- flag = false,
1053
- resizeHdr = function(){
1080
+ updatingStickyFilters = false,
1081
+ resizeHeader = function() {
1054
1082
  stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0;
1055
- var bwsr = navigator.userAgent;
1056
1083
  spacing = 0;
1057
1084
  // yes, I dislike browser sniffing, but it really is needed here :(
1058
1085
  // webkit automatically compensates for border spacing
1059
- if ($t.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(bwsr)) {
1086
+ if ($table.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(navigator.userAgent)) {
1060
1087
  // Firefox & Opera use the border-spacing
1061
1088
  // update border-spacing here because of demos that switch themes
1062
- spacing = parseInt(hdrCells.eq(0).css('border-left-width'), 10) * 2;
1089
+ spacing = parseInt($header.eq(0).css('border-left-width'), 10) * 2;
1063
1090
  }
1064
1091
  $stickyTable.css({
1065
- left : header.offset().left - $win.scrollLeft() - spacing,
1066
- width: $t.width()
1092
+ left : $thead.offset().left - $win.scrollLeft() - spacing,
1093
+ width: $table.width()
1067
1094
  });
1068
- stkyCells.filter(':visible').each(function(i){
1069
- var $h = hdrCells.filter(':visible').eq(i);
1095
+ $stickyCells.filter(':visible').each(function(i) {
1096
+ var $cell = $header.filter(':visible').eq(i);
1070
1097
  $(this)
1071
1098
  .css({
1072
- width: $h.width() - spacing,
1073
- height: $h.height()
1099
+ width: $cell.width() - spacing,
1100
+ height: $cell.height()
1074
1101
  })
1075
- .find(innr).width( $h.find(innr).width() );
1102
+ .find(innerHeader).width( $cell.find(innerHeader).width() );
1076
1103
  });
1077
1104
  };
1078
1105
  // fix clone ID, if it exists - fixes #271
@@ -1084,71 +1111,70 @@ ts.addWidget({
1084
1111
  $stickyTable.find('caption').remove();
1085
1112
  }
1086
1113
  // issue #172 - find td/th in sticky header
1087
- stkyCells = stkyHdr.children().children();
1114
+ $stickyCells = $stickyThead.children().children();
1088
1115
  $stickyTable.css({ height:0, width:0, padding:0, margin:0, border:0 });
1089
1116
  // remove resizable block
1090
- stkyCells.find('.tablesorter-resizer').remove();
1117
+ $stickyCells.find('.tablesorter-resizer').remove();
1091
1118
  // update sticky header class names to match real header after sorting
1092
- $t
1093
- .addClass('hasStickyHeaders')
1094
- .bind('sortEnd.tsSticky', function(){
1095
- hdrCells.filter(':visible').each(function(i){
1096
- var t = stkyCells.filter(':visible').eq(i);
1097
- t
1098
- .attr('class', $(this).attr('class'))
1099
- // remove processing icon
1100
- .removeClass(ts.css.processing + ' ' + c.cssProcessing);
1101
- if (c.cssIcon){
1102
- t
1103
- .find('.' + ts.css.icon)
1104
- .attr('class', $(this).find('.' + ts.css.icon).attr('class'));
1105
- }
1119
+ $table
1120
+ .addClass('hasStickyHeaders')
1121
+ .bind('sortEnd.tsSticky', function() {
1122
+ $header.filter(':visible').each(function(indx) {
1123
+ $cell = $stickyCells.filter(':visible').eq(indx)
1124
+ .attr('class', $(this).attr('class'))
1125
+ // remove processing icon
1126
+ .removeClass(ts.css.processing + ' ' + c.cssProcessing);
1127
+ if (c.cssIcon) {
1128
+ $cell
1129
+ .find('.' + ts.css.icon)
1130
+ .attr('class', $(this).find('.' + ts.css.icon).attr('class'));
1131
+ }
1132
+ });
1133
+ })
1134
+ .bind('pagerComplete.tsSticky', function() {
1135
+ resizeHeader();
1106
1136
  });
1107
- })
1108
- .bind('pagerComplete.tsSticky', function(){
1109
- resizeHdr();
1110
- });
1111
- // http://stackoverflow.com/questions/5312849/jquery-find-self;
1112
- hdrCells.find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ).each(function(i){
1113
- var t = $(this),
1114
- // clicking on sticky will trigger sort
1115
- $cell = stkyHdr.children('tr.tablesorter-headerRow').children().eq(i).bind('mouseup', function(e){
1116
- t.trigger(e, true); // external mouseup flag (click timer is ignored)
1137
+ // http://stackoverflow.com/questions/5312849/jquery-find-self;
1138
+ $header.find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ).each(function(indx) {
1139
+ var $header = $(this),
1140
+ // clicking on sticky will trigger sort
1141
+ $cell = $stickyThead.children('tr.tablesorter-headerRow').children().eq(indx).bind('mouseup', function(event) {
1142
+ $header.trigger(event, true); // external mouseup flag (click timer is ignored)
1143
+ });
1144
+ // prevent sticky header text selection
1145
+ if (c.cancelSelection) {
1146
+ $cell
1147
+ .attr('unselectable', 'on')
1148
+ .bind('selectstart', false)
1149
+ .css({
1150
+ 'user-select': 'none',
1151
+ 'MozUserSelect': 'none'
1152
+ });
1153
+ }
1117
1154
  });
1118
- // prevent sticky header text selection
1119
- if (c.cancelSelection) {
1120
- $cell
1121
- .attr('unselectable', 'on')
1122
- .bind('selectstart', false)
1123
- .css({
1124
- 'user-select': 'none',
1125
- 'MozUserSelect': 'none'
1126
- });
1127
- }
1128
- });
1129
1155
  // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned.
1130
- $t.after( $stickyTable );
1156
+ $table.after( $stickyTable );
1131
1157
  // make it sticky!
1132
- $win.bind('scroll.tsSticky resize.tsSticky', function(e){
1133
- if (!$t.is(':visible')) { return; } // fixes #278
1134
- var pre = 'tablesorter-sticky-',
1135
- offset = $t.offset(),
1136
- cap = (wo.stickyHeaders_includeCaption ? 0 : $t.find('caption').outerHeight(true)),
1137
- sTop = $win.scrollTop() + stickyOffset - cap,
1138
- tableHt = $t.height() - ($stickyTable.height() + (tfoot.height() || 0)),
1139
- vis = (sTop > offset.top) && (sTop < offset.top + tableHt) ? 'visible' : 'hidden';
1158
+ $win.bind('scroll.tsSticky resize.tsSticky', function(event) {
1159
+ if (!$table.is(':visible')) { return; } // fixes #278
1160
+ var prefix = 'tablesorter-sticky-',
1161
+ offset = $table.offset(),
1162
+ captionHeight = (wo.stickyHeaders_includeCaption ? 0 : $table.find('caption').outerHeight(true)),
1163
+ scrollTop = $win.scrollTop() + stickyOffset - captionHeight,
1164
+ tableHeight = $table.height() - ($stickyTable.height() + ($tfoot.height() || 0)),
1165
+ isVisible = (scrollTop > offset.top) && (scrollTop < offset.top + tableHeight) ? 'visible' : 'hidden';
1140
1166
  $stickyTable
1141
- .removeClass(pre + 'visible ' + pre + 'hidden')
1142
- .addClass(pre + vis)
1143
- .css({
1144
- // adjust when scrolling horizontally - fixes issue #143
1145
- left : header.offset().left - $win.scrollLeft() - spacing,
1146
- visibility : vis
1147
- });
1148
- if (vis !== laststate || e.type === 'resize'){
1167
+ .removeClass(prefix + 'visible ' + prefix + 'hidden')
1168
+ .addClass(prefix + isVisible)
1169
+ .css({
1170
+ // adjust when scrolling horizontally - fixes issue #143
1171
+ left : $thead.offset().left - $win.scrollLeft() - spacing,
1172
+ visibility : isVisible
1173
+ });
1174
+ if (isVisible !== laststate || event.type === 'resize') {
1149
1175
  // make sure the column widths match
1150
- resizeHdr();
1151
- laststate = vis;
1176
+ resizeHeader();
1177
+ laststate = isVisible;
1152
1178
  }
1153
1179
  });
1154
1180
  if (wo.stickyHeaders_addResizeEvent) {
@@ -1156,28 +1182,28 @@ ts.addWidget({
1156
1182
  }
1157
1183
 
1158
1184
  // look for filter widget
1159
- $t.bind('filterEnd', function(){
1160
- if (flag) { return; }
1161
- stkyHdr.find('.tablesorter-filter-row').children().each(function(i){
1162
- $(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(i).val() );
1185
+ $table.bind('filterEnd', function() {
1186
+ if (updatingStickyFilters) { return; }
1187
+ $stickyThead.find('.tablesorter-filter-row').children().each(function(indx) {
1188
+ $(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(indx).val() );
1163
1189
  });
1164
1190
  });
1165
- stkyCells.find(filterInputs).bind('keyup search change', function(e){
1191
+ $stickyCells.find(filterInputs).bind('keyup search change', function(event) {
1166
1192
  // ignore arrow and meta keys; allow backspace
1167
- if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; }
1168
- flag = true;
1169
- var $f = $(this), col = $f.attr('data-column');
1170
- c.$filters.find(filterInputs).eq(col)
1193
+ if ((event.which < 32 && event.which !== 8) || (event.which >= 37 && event.which <=40)) { return; }
1194
+ updatingStickyFilters = true;
1195
+ var $f = $(this), column = $f.attr('data-column');
1196
+ c.$filters.find(filterInputs).eq(column)
1171
1197
  .val( $f.val() )
1172
1198
  .trigger('search');
1173
- setTimeout(function(){
1174
- flag = false;
1199
+ setTimeout(function() {
1200
+ updatingStickyFilters = false;
1175
1201
  }, wo.filter_searchDelay);
1176
1202
  });
1177
- $t.trigger('stickyHeadersInit');
1203
+ $table.trigger('stickyHeadersInit');
1178
1204
 
1179
1205
  },
1180
- remove: function(table, c, wo){
1206
+ remove: function(table, c, wo) {
1181
1207
  c.$table
1182
1208
  .removeClass('hasStickyHeaders')
1183
1209
  .unbind('sortEnd.tsSticky pagerComplete.tsSticky')
@@ -1202,119 +1228,120 @@ ts.addWidget({
1202
1228
  resizable : true,
1203
1229
  resizable_addLastColumn : false
1204
1230
  },
1205
- format: function(table, c, wo){
1231
+ format: function(table, c, wo) {
1206
1232
  if (c.$table.hasClass('hasResizable')) { return; }
1207
1233
  c.$table.addClass('hasResizable');
1208
- var $t, t, i, j, s = {}, $c, $cols, w, tw,
1209
- $tbl = c.$table,
1210
- position = 0,
1234
+ var $rows, $columns, $column, column,
1235
+ storedSizes = {},
1236
+ $table = c.$table,
1237
+ mouseXPosition = 0,
1211
1238
  $target = null,
1212
1239
  $next = null,
1213
- fullWidth = Math.abs($tbl.parent().width() - $tbl.width()) < 20,
1214
- stopResize = function(){
1215
- if (ts.storage && $target){
1216
- s[$target.index()] = $target.width();
1217
- s[$next.index()] = $next.width();
1218
- $target.width( s[$target.index()] );
1219
- $next.width( s[$next.index()] );
1220
- if (wo.resizable !== false){
1221
- ts.storage(table, 'tablesorter-resizable', s);
1240
+ fullWidth = Math.abs($table.parent().width() - $table.width()) < 20,
1241
+ stopResize = function() {
1242
+ if (ts.storage && $target) {
1243
+ storedSizes[$target.index()] = $target.width();
1244
+ storedSizes[$next.index()] = $next.width();
1245
+ $target.width( storedSizes[$target.index()] );
1246
+ $next.width( storedSizes[$next.index()] );
1247
+ if (wo.resizable !== false) {
1248
+ ts.storage(table, 'tablesorter-resizable', storedSizes);
1222
1249
  }
1223
1250
  }
1224
- position = 0;
1251
+ mouseXPosition = 0;
1225
1252
  $target = $next = null;
1226
1253
  $(window).trigger('resize'); // will update stickyHeaders, just in case
1227
1254
  };
1228
- s = (ts.storage && wo.resizable !== false) ? ts.storage(table, 'tablesorter-resizable') : {};
1255
+ storedSizes = (ts.storage && wo.resizable !== false) ? ts.storage(table, 'tablesorter-resizable') : {};
1229
1256
  // process only if table ID or url match
1230
- if (s){
1231
- for (j in s){
1232
- if (!isNaN(j) && j < c.$headers.length){
1233
- c.$headers.eq(j).width(s[j]); // set saved resizable widths
1257
+ if (storedSizes) {
1258
+ for (column in storedSizes) {
1259
+ if (!isNaN(column) && column < c.$headers.length) {
1260
+ c.$headers.eq(column).width(storedSizes[column]); // set saved resizable widths
1234
1261
  }
1235
1262
  }
1236
1263
  }
1237
- $t = $tbl.children('thead:first').children('tr');
1264
+ $rows = $table.children('thead:first').children('tr');
1238
1265
  // add resizable-false class name to headers (across rows as needed)
1239
- $t.children().each(function(){
1240
- t = $(this);
1241
- i = t.attr('data-column');
1242
- j = ts.getData( t, c.headers[i], 'resizable') === "false";
1243
- $t.children().filter('[data-column="' + i + '"]').toggleClass('resizable-false', j);
1266
+ $rows.children().each(function() {
1267
+ var canResize,
1268
+ $column = $(this);
1269
+ column = $column.attr('data-column');
1270
+ canResize = ts.getData( $column, c.headers[column], 'resizable') === "false";
1271
+ $rows.children().filter('[data-column="' + column + '"]')[canResize ? 'addClass' : 'removeClass']('resizable-false');
1244
1272
  });
1245
1273
  // add wrapper inside each cell to allow for positioning of the resizable target block
1246
- $t.each(function(){
1247
- $c = $(this).children(':not(.resizable-false)');
1274
+ $rows.each(function() {
1275
+ $column = $(this).children().not('.resizable-false');
1248
1276
  if (!$(this).find('.tablesorter-wrapper').length) {
1249
1277
  // Firefox needs this inner div to position the resizer correctly
1250
- $c.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>');
1278
+ $column.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>');
1251
1279
  }
1252
1280
  // don't include the last column of the row
1253
- if (!wo.resizable_addLastColumn) { $c = $c.slice(0,-1); }
1254
- $cols = $cols ? $cols.add($c) : $c;
1281
+ if (!wo.resizable_addLastColumn) { $column = $column.slice(0,-1); }
1282
+ $columns = $columns ? $columns.add($column) : $column;
1255
1283
  });
1256
- $cols
1257
- .each(function(){
1258
- $t = $(this);
1259
- j = parseInt($t.css('padding-right'), 10) + 10; // 8 is 1/2 of the 16px wide resizer grip
1260
- t = '<div class="tablesorter-resizer" style="cursor:w-resize;position:absolute;z-index:1;right:-' + j +
1261
- 'px;top:0;height:100%;width:20px;"></div>';
1262
- $t
1284
+ $columns
1285
+ .each(function() {
1286
+ var $column = $(this),
1287
+ padding = parseInt($column.css('padding-right'), 10) + 10; // 10 is 1/2 of the 20px wide resizer grip
1288
+ $column
1263
1289
  .find('.tablesorter-wrapper')
1264
- .append(t);
1290
+ .append('<div class="tablesorter-resizer" style="cursor:w-resize;position:absolute;z-index:1;right:-' +
1291
+ padding + 'px;top:0;height:100%;width:20px;"></div>');
1265
1292
  })
1266
- .bind('mousemove.tsresize', function(e){
1293
+ .bind('mousemove.tsresize', function(event) {
1267
1294
  // ignore mousemove if no mousedown
1268
- if (position === 0 || !$target) { return; }
1295
+ if (mouseXPosition === 0 || !$target) { return; }
1269
1296
  // resize columns
1270
- w = e.pageX - position;
1271
- tw = $target.width();
1272
- $target.width( tw + w );
1273
- if ($target.width() !== tw && fullWidth){
1274
- $next.width( $next.width() - w );
1297
+ var leftEdge = event.pageX - mouseXPosition,
1298
+ targetWidth = $target.width();
1299
+ $target.width( targetWidth + leftEdge );
1300
+ if ($target.width() !== targetWidth && fullWidth) {
1301
+ $next.width( $next.width() - leftEdge );
1275
1302
  }
1276
- position = e.pageX;
1303
+ mouseXPosition = event.pageX;
1277
1304
  })
1278
- .bind('mouseup.tsresize', function(){
1305
+ .bind('mouseup.tsresize', function() {
1279
1306
  stopResize();
1280
1307
  })
1281
1308
  .find('.tablesorter-resizer,.tablesorter-resizer-grip')
1282
- .bind('mousedown', function(e){
1309
+ .bind('mousedown', function(event) {
1283
1310
  // save header cell and mouse position; closest() not supported by jQuery v1.2.6
1284
- $target = $(e.target).closest('th');
1285
- t = c.$headers.filter('[data-column="' + $target.attr('data-column') + '"]');
1286
- if (t.length > 1) { $target = $target.add(t); }
1311
+ $target = $(event.target).closest('th');
1312
+ var $header = c.$headers.filter('[data-column="' + $target.attr('data-column') + '"]');
1313
+ if ($header.length > 1) { $target = $target.add($header); }
1287
1314
  // if table is not as wide as it's parent, then resize the table
1288
- $next = e.shiftKey ? $target.parent().find('th:not(.resizable-false)').filter(':last') : $target.nextAll(':not(.resizable-false)').eq(0);
1289
- position = e.pageX;
1315
+ $next = event.shiftKey ? $target.parent().find('th').not('.resizable-false').filter(':last') : $target.nextAll(':not(.resizable-false)').eq(0);
1316
+ mouseXPosition = event.pageX;
1290
1317
  });
1291
- $tbl.find('thead:first')
1292
- .bind('mouseup.tsresize mouseleave.tsresize', function(){
1318
+ $table.find('thead:first')
1319
+ .bind('mouseup.tsresize mouseleave.tsresize', function() {
1293
1320
  stopResize();
1294
1321
  })
1295
1322
  // right click to reset columns to default widths
1296
- .bind('contextmenu.tsresize', function(){
1323
+ .bind('contextmenu.tsresize', function() {
1297
1324
  ts.resizableReset(table);
1298
- // $.isEmptyObject() needs jQuery 1.4+
1299
- var rtn = $.isEmptyObject ? $.isEmptyObject(s) : s === {}; // allow right click if already reset
1300
- s = {};
1301
- return rtn;
1325
+ // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset
1326
+ var allowClick = $.isEmptyObject ? $.isEmptyObject(storedSizes) : true;
1327
+ storedSizes = {};
1328
+ return allowClick;
1302
1329
  });
1303
1330
  },
1304
- remove: function(table, c, wo){
1331
+ remove: function(table, c) {
1305
1332
  c.$table
1306
1333
  .removeClass('hasResizable')
1307
- .find('thead')
1334
+ .children('thead')
1308
1335
  .unbind('mouseup.tsresize mouseleave.tsresize contextmenu.tsresize')
1309
- .find('tr').children()
1336
+ .children('tr').children()
1310
1337
  .unbind('mousemove.tsresize mouseup.tsresize')
1311
1338
  // don't remove "tablesorter-wrapper" as uitheme uses it too
1312
1339
  .find('.tablesorter-resizer,.tablesorter-resizer-grip').remove();
1313
1340
  ts.resizableReset(table);
1314
1341
  }
1315
1342
  });
1316
- ts.resizableReset = function(table){
1317
- table.config.$headers.filter(':not(.resizable-false)').css('width','');
1343
+ ts.resizableReset = function(table) {
1344
+ table.config.$headers.not('.resizable-false').css('width','');
1318
1345
  if (ts.storage) { ts.storage(table, 'tablesorter-resizable', {}); }
1319
1346
  };
1320
1347
 
@@ -1329,52 +1356,52 @@ ts.addWidget({
1329
1356
  options: {
1330
1357
  saveSort : true
1331
1358
  },
1332
- init: function(table, thisWidget, c, wo){
1359
+ init: function(table, thisWidget, c, wo) {
1333
1360
  // run widget format before all other widgets are applied to the table
1334
1361
  thisWidget.format(table, c, wo, true);
1335
1362
  },
1336
- format: function(table, c, wo, init){
1337
- var sl, time,
1338
- $t = c.$table,
1339
- ss = wo.saveSort !== false, // make saveSort active/inactive; default to true
1363
+ format: function(table, c, wo, init) {
1364
+ var stored, time,
1365
+ $table = c.$table,
1366
+ saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true
1340
1367
  sortList = { "sortList" : c.sortList };
1341
- if (c.debug){
1368
+ if (c.debug) {
1342
1369
  time = new Date();
1343
1370
  }
1344
- if ($t.hasClass('hasSaveSort')){
1345
- if (ss && table.hasInitialized && ts.storage){
1371
+ if ($table.hasClass('hasSaveSort')) {
1372
+ if (saveSort && table.hasInitialized && ts.storage) {
1346
1373
  ts.storage( table, 'tablesorter-savesort', sortList );
1347
- if (c.debug){
1374
+ if (c.debug) {
1348
1375
  ts.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time);
1349
1376
  }
1350
1377
  }
1351
1378
  } else {
1352
1379
  // set table sort on initial run of the widget
1353
- $t.addClass('hasSaveSort');
1380
+ $table.addClass('hasSaveSort');
1354
1381
  sortList = '';
1355
1382
  // get data
1356
- if (ts.storage){
1357
- sl = ts.storage( table, 'tablesorter-savesort' );
1358
- sortList = (sl && sl.hasOwnProperty('sortList') && $.isArray(sl.sortList)) ? sl.sortList : '';
1359
- if (c.debug){
1383
+ if (ts.storage) {
1384
+ stored = ts.storage( table, 'tablesorter-savesort' );
1385
+ sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : '';
1386
+ if (c.debug) {
1360
1387
  ts.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time);
1361
1388
  }
1362
- $t.bind('saveSortReset', function(e){
1363
- e.stopPropagation();
1389
+ $table.bind('saveSortReset', function(event) {
1390
+ event.stopPropagation();
1364
1391
  ts.storage( table, 'tablesorter-savesort', '' );
1365
1392
  });
1366
1393
  }
1367
1394
  // init is true when widget init is run, this will run this widget before all other widgets have initialized
1368
1395
  // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice.
1369
- if (init && sortList && sortList.length > 0){
1396
+ if (init && sortList && sortList.length > 0) {
1370
1397
  c.sortList = sortList;
1371
- } else if (table.hasInitialized && sortList && sortList.length > 0){
1398
+ } else if (table.hasInitialized && sortList && sortList.length > 0) {
1372
1399
  // update sort change
1373
- $t.trigger('sorton', [sortList]);
1400
+ $table.trigger('sorton', [sortList]);
1374
1401
  }
1375
1402
  }
1376
1403
  },
1377
- remove: function(table){
1404
+ remove: function(table) {
1378
1405
  // clear storage
1379
1406
  if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); }
1380
1407
  }