jquery-tablesorter 1.4.1 → 1.5.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 +7 -0
- data/MIT-LICENSE +1 -1
- data/README.markdown +3 -3
- data/lib/jquery-tablesorter/version.rb +1 -1
- data/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +177 -115
- data/vendor/assets/javascripts/jquery-tablesorter/jquery.metadata.js +1 -1
- data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +461 -376
- data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +924 -0
- data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +499 -270
- data/vendor/assets/stylesheets/jquery-tablesorter/filter.formatter.css +183 -0
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +12 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +12 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +17 -2
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +12 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +14 -2
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +19 -9
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +12 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +12 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +12 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +12 -1
- metadata +55 -56
@@ -1,4 +1,4 @@
|
|
1
|
-
/*! tableSorter 2.
|
1
|
+
/*! tableSorter 2.8+ widgets - updated 6/4/2013
|
2
2
|
*
|
3
3
|
* Column Styles
|
4
4
|
* Column Filters
|
@@ -6,15 +6,15 @@
|
|
6
6
|
* Sticky Header
|
7
7
|
* UI Theme (generalized)
|
8
8
|
* Save Sort
|
9
|
-
* ["
|
9
|
+
* [ "columns", "filter", "resizable", "stickyHeaders", "uitheme", "saveSort" ]
|
10
10
|
*/
|
11
11
|
/*jshint browser:true, jquery:true, unused:false, loopfunc:true */
|
12
12
|
/*global jQuery: false, localStorage: false, navigator: false */
|
13
13
|
;(function($){
|
14
14
|
"use strict";
|
15
|
-
$.tablesorter = $.tablesorter || {};
|
15
|
+
var ts = $.tablesorter = $.tablesorter || {};
|
16
16
|
|
17
|
-
|
17
|
+
ts.themes = {
|
18
18
|
"bootstrap" : {
|
19
19
|
table : 'table table-bordered table-striped',
|
20
20
|
header : 'bootstrap-header', // give the header a gradient background
|
@@ -66,19 +66,26 @@ $.tablesorter.themes = {
|
|
66
66
|
val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : '';
|
67
67
|
alert(val); // "data1" if saved, or "" if not
|
68
68
|
*/
|
69
|
-
|
69
|
+
ts.storage = function(table, key, val){
|
70
70
|
var d, k, ls = false, v = {},
|
71
71
|
id = table.id || $('.tablesorter').index( $(table) ),
|
72
72
|
url = window.location.pathname;
|
73
|
-
|
73
|
+
// https://gist.github.com/paulirish/5558557
|
74
|
+
if ("localStorage" in window) {
|
75
|
+
try {
|
76
|
+
window.localStorage.setItem('_tmptest', 'temp');
|
77
|
+
ls = true;
|
78
|
+
window.localStorage.removeItem('_tmptest');
|
79
|
+
} catch(e) {}
|
80
|
+
}
|
74
81
|
// *** get val ***
|
75
82
|
if ($.parseJSON){
|
76
83
|
if (ls){
|
77
|
-
v = $.parseJSON(localStorage[key]
|
84
|
+
v = $.parseJSON(localStorage[key] || '{}');
|
78
85
|
} else {
|
79
86
|
k = document.cookie.split(/[;\s|=]/); // cookie
|
80
87
|
d = $.inArray(key, k) + 1; // add one to get from the key to the value
|
81
|
-
v = (d !== 0) ? $.parseJSON(k[d]
|
88
|
+
v = (d !== 0) ? $.parseJSON(k[d] || '{}') : {};
|
82
89
|
}
|
83
90
|
}
|
84
91
|
// allow val to be an empty string to
|
@@ -101,20 +108,61 @@ $.tablesorter.storage = function(table, key, val){
|
|
101
108
|
}
|
102
109
|
};
|
103
110
|
|
111
|
+
// Add a resize event to table headers
|
112
|
+
// **************************
|
113
|
+
ts.addHeaderResizeEvent = function(table, disable, options){
|
114
|
+
var defaults = {
|
115
|
+
timer : 250
|
116
|
+
},
|
117
|
+
o = $.extend({}, defaults, options),
|
118
|
+
c = table.config,
|
119
|
+
wo = c.widgetOptions,
|
120
|
+
headers,
|
121
|
+
checkSizes = function(){
|
122
|
+
wo.resize_flag = true;
|
123
|
+
headers = [];
|
124
|
+
c.$headers.each(function(){
|
125
|
+
var d = $.data(this, 'savedSizes'),
|
126
|
+
w = this.offsetWidth,
|
127
|
+
h = this.offsetHeight;
|
128
|
+
if (w !== d[0] || h !== d[1]) {
|
129
|
+
$.data(this, 'savedSizes', [ w, h ]);
|
130
|
+
headers.push(this);
|
131
|
+
}
|
132
|
+
});
|
133
|
+
if (headers.length) { c.$table.trigger('resize', [ headers ]); }
|
134
|
+
wo.resize_flag = false;
|
135
|
+
};
|
136
|
+
clearInterval(wo.resize_timer);
|
137
|
+
if (disable) {
|
138
|
+
wo.resize_flag = false;
|
139
|
+
return false;
|
140
|
+
}
|
141
|
+
c.$headers.each(function(){
|
142
|
+
$.data(this, 'savedSizes', [ this.offsetWidth, this.offsetHeight ]);
|
143
|
+
});
|
144
|
+
wo.resize_timer = setInterval(function(){
|
145
|
+
if (wo.resize_flag) { return; }
|
146
|
+
checkSizes();
|
147
|
+
}, o.timer);
|
148
|
+
};
|
149
|
+
|
104
150
|
// Widget: General UI theme
|
105
151
|
// "uitheme" option in "widgetOptions"
|
106
152
|
// **************************
|
107
|
-
|
153
|
+
ts.addWidget({
|
108
154
|
id: "uitheme",
|
109
|
-
|
155
|
+
priority: 10,
|
156
|
+
options: {
|
157
|
+
uitheme : 'jui'
|
158
|
+
},
|
159
|
+
format: function(table, c, wo){
|
110
160
|
var time, klass, $el, $tar,
|
111
|
-
t =
|
112
|
-
$t =
|
113
|
-
|
114
|
-
wo = c.widgetOptions,
|
115
|
-
theme = c.theme !== 'default' ? c.theme : wo.uitheme || 'jui', // default uitheme is 'jui'
|
161
|
+
t = ts.themes,
|
162
|
+
$t = c.$table,
|
163
|
+
theme = c.theme !== 'default' ? c.theme : wo.uitheme || 'jui',
|
116
164
|
o = t[ t[theme] ? theme : t[wo.uitheme] ? wo.uitheme : 'jui'],
|
117
|
-
$h =
|
165
|
+
$h = c.$headers,
|
118
166
|
sh = 'tr.' + (wo.stickyHeaders || 'tablesorter-stickyHeader'),
|
119
167
|
rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc;
|
120
168
|
if (c.debug) { time = new Date(); }
|
@@ -137,10 +185,9 @@ $.tablesorter.addWidget({
|
|
137
185
|
$h
|
138
186
|
.addClass(o.header)
|
139
187
|
.filter(':not(.sorter-false)')
|
140
|
-
.
|
141
|
-
|
142
|
-
|
143
|
-
$(this).removeClass(o.hover);
|
188
|
+
.bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(e){
|
189
|
+
// toggleClass with switch added in jQuery 1.3
|
190
|
+
$(this)[ e.type === 'mouseenter' ? 'addClass' : 'removeClass' ](o.hover);
|
144
191
|
});
|
145
192
|
if (!$h.find('.tablesorter-wrapper').length) {
|
146
193
|
// Firefox needs this inner div to position the resizer correctly
|
@@ -169,20 +216,20 @@ $.tablesorter.addWidget({
|
|
169
216
|
}
|
170
217
|
});
|
171
218
|
if (c.debug){
|
172
|
-
|
219
|
+
ts.benchmark("Applying " + theme + " theme", time);
|
173
220
|
}
|
174
221
|
},
|
175
222
|
remove: function(table, c, wo){
|
176
|
-
var $t =
|
223
|
+
var $t = c.$table,
|
177
224
|
theme = typeof wo.uitheme === 'object' ? 'jui' : wo.uitheme || 'jui',
|
178
|
-
o = typeof wo.uitheme === 'object' ? wo.uitheme :
|
225
|
+
o = typeof wo.uitheme === 'object' ? wo.uitheme : ts.themes[ ts.themes.hasOwnProperty(theme) ? theme : 'jui'],
|
179
226
|
$h = $t.children('thead').children(),
|
180
227
|
rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc;
|
181
228
|
$t
|
182
229
|
.removeClass('tablesorter-' + theme + ' ' + o.table)
|
183
230
|
.find(c.cssHeader).removeClass(o.header);
|
184
231
|
$h
|
185
|
-
.unbind('mouseenter mouseleave') // remove hover
|
232
|
+
.unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover
|
186
233
|
.removeClass(o.hover + ' ' + rmv + ' ' + o.active)
|
187
234
|
.find('.tablesorter-filter-row').removeClass(o.filterRow);
|
188
235
|
$h.find('.tablesorter-icon').removeClass(o.icons);
|
@@ -193,17 +240,18 @@ $.tablesorter.addWidget({
|
|
193
240
|
// "columns", "columns_thead" (true) and
|
194
241
|
// "columns_tfoot" (true) options in "widgetOptions"
|
195
242
|
// **************************
|
196
|
-
|
243
|
+
ts.addWidget({
|
197
244
|
id: "columns",
|
198
|
-
|
245
|
+
priority: 30,
|
246
|
+
options : {
|
247
|
+
columns : [ "primary", "secondary", "tertiary" ]
|
248
|
+
},
|
249
|
+
format: function(table, c, wo){
|
199
250
|
var $tb, $tr, $td, $t, time, last, rmv, i, k, l,
|
200
|
-
$tbl =
|
201
|
-
c = table.config,
|
202
|
-
wo = c.widgetOptions,
|
251
|
+
$tbl = c.$table,
|
203
252
|
b = c.$tbodies,
|
204
253
|
list = c.sortList,
|
205
254
|
len = list.length,
|
206
|
-
css = [ "primary", "secondary", "tertiary" ]; // default options
|
207
255
|
// keep backwards compatibility, for now
|
208
256
|
css = (c.widgetColumns && c.widgetColumns.hasOwnProperty('css')) ? c.widgetColumns.css || css :
|
209
257
|
(wo && wo.hasOwnProperty('columns')) ? wo.columns || css : css;
|
@@ -214,7 +262,7 @@ $.tablesorter.addWidget({
|
|
214
262
|
}
|
215
263
|
// check if there is a sort (on initialization there may not be one)
|
216
264
|
for (k = 0; k < b.length; k++ ){
|
217
|
-
$tb =
|
265
|
+
$tb = ts.processTbody(table, b.eq(k), true); // detach tbody
|
218
266
|
$tr = $tb.children('tr');
|
219
267
|
l = $tr.length;
|
220
268
|
// loop through the visible rows
|
@@ -236,7 +284,7 @@ $.tablesorter.addWidget({
|
|
236
284
|
}
|
237
285
|
}
|
238
286
|
});
|
239
|
-
|
287
|
+
ts.processTbody(table, $tb, false);
|
240
288
|
}
|
241
289
|
// add classes to thead and tfoot
|
242
290
|
$tr = wo.columns_thead !== false ? 'thead tr' : '';
|
@@ -257,83 +305,82 @@ $.tablesorter.addWidget({
|
|
257
305
|
}
|
258
306
|
}
|
259
307
|
if (c.debug){
|
260
|
-
|
308
|
+
ts.benchmark("Applying Columns widget", time);
|
261
309
|
}
|
262
310
|
},
|
263
311
|
remove: function(table, c, wo){
|
264
312
|
var k, $tb,
|
265
313
|
b = c.$tbodies,
|
266
|
-
rmv = (
|
314
|
+
rmv = (wo.columns || [ "primary", "secondary", "tertiary" ]).join(' ');
|
267
315
|
c.$headers.removeClass(rmv);
|
268
|
-
|
316
|
+
c.$table.children('tfoot').children('tr').children('th, td').removeClass(rmv);
|
269
317
|
for (k = 0; k < b.length; k++ ){
|
270
|
-
$tb =
|
318
|
+
$tb = ts.processTbody(table, b.eq(k), true); // remove tbody
|
271
319
|
$tb.children('tr').each(function(){
|
272
320
|
$(this).children().removeClass(rmv);
|
273
321
|
});
|
274
|
-
|
322
|
+
ts.processTbody(table, $tb, false); // restore tbody
|
275
323
|
}
|
276
324
|
}
|
277
325
|
});
|
278
326
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
filter_columnFilters : true // if true, a filter will be added to the top of each table column
|
283
|
-
filter_cssFilter : 'tablesorter-filter' // css class name added to the filter row & each input in the row
|
284
|
-
filter_functions : null // add custom filter functions using this option
|
285
|
-
filter_hideFilters : false // collapse filter row when mouse leaves the area
|
286
|
-
filter_ignoreCase : true // if true, make all searches case-insensitive
|
287
|
-
filter_reset : null // jQuery selector string of an element used to reset the filters
|
288
|
-
filter_searchDelay : 300 // typing delay in milliseconds before starting a search
|
289
|
-
filter_startsWith : false // if true, filter start from the beginning of the cell contents
|
290
|
-
filter_useParsedData : false // filter all data using parsed content
|
291
|
-
filter_serversideFiltering : false // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used.
|
292
|
-
**************************/
|
293
|
-
$.tablesorter.addWidget({
|
327
|
+
// Widget: filter
|
328
|
+
// **************************
|
329
|
+
ts.addWidget({
|
294
330
|
id: "filter",
|
295
|
-
|
296
|
-
|
331
|
+
priority: 50,
|
332
|
+
options : {
|
333
|
+
filter_childRows : false, // if true, filter includes child row content in the search
|
334
|
+
filter_columnFilters : true, // if true, a filter will be added to the top of each table column
|
335
|
+
filter_cssFilter : 'tablesorter-filter', // css class name added to the filter row & each input in the row
|
336
|
+
filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin
|
337
|
+
filter_formatter : null, // add custom filter elements to the filter row
|
338
|
+
filter_functions : null, // add custom filter functions using this option
|
339
|
+
filter_hideFilters : false, // collapse filter row when mouse leaves the area
|
340
|
+
filter_ignoreCase : true, // if true, make all searches case-insensitive
|
341
|
+
filter_liveSearch : true, // if true, search column content while the user types (with a delay)
|
342
|
+
filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available (visible) options within the drop down
|
343
|
+
filter_reset : null, // jQuery selector string of an element used to reset the filters
|
344
|
+
filter_searchDelay : 300, // typing delay in milliseconds before starting a search
|
345
|
+
filter_startsWith : false, // if true, filter start from the beginning of the cell contents
|
346
|
+
filter_useParsedData : false, // filter all data using parsed content
|
347
|
+
filter_serversideFiltering : false, // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used.
|
348
|
+
filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value
|
349
|
+
|
350
|
+
// regex used in filter "check" functions - not for general use and not documented
|
351
|
+
filter_regex : {
|
352
|
+
"regex" : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex
|
353
|
+
"child" : /tablesorter-childRow/, // child row class name; this gets updated in the script
|
354
|
+
"filtered" : /filtered/, // filtered (hidden) row class name; updated in the script
|
355
|
+
"type" : /undefined|number/, // check type
|
356
|
+
"exact" : /(^[\"|\'|=])|([\"|\'|=]$)/g, // exact match
|
357
|
+
"nondigit" : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser)
|
358
|
+
"operators" : /[<>=]/g // replace operators
|
359
|
+
}
|
360
|
+
},
|
361
|
+
format: function(table, c, wo){
|
362
|
+
if (c.parsers && !c.$table.hasClass('hasFilters')){
|
297
363
|
var i, j, k, l, val, ff, x, xi, st, sel, str,
|
298
364
|
ft, ft2, $th, rg, s, t, dis, col,
|
365
|
+
fmt = ts.formatFloat,
|
299
366
|
last = '', // save last filter search
|
300
|
-
|
301
|
-
|
302
|
-
$
|
303
|
-
|
304
|
-
css = wo.filter_cssFilter || 'tablesorter-filter',
|
305
|
-
$t = $(table).addClass('hasFilters'),
|
306
|
-
b = c.$tbodies,
|
367
|
+
$ths = c.$headers,
|
368
|
+
css = wo.filter_cssFilter,
|
369
|
+
$t = c.$table.addClass('hasFilters'),
|
370
|
+
b = $t.find('tbody'),
|
307
371
|
cols = c.parsers.length,
|
308
|
-
|
309
|
-
/^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // 0 = regex to test for regex
|
310
|
-
new RegExp(c.cssChildRow), // 1 = child row
|
311
|
-
/undefined|number/, // 2 = check type
|
312
|
-
/(^[\"|\'|=])|([\"|\'|=]$)/, // 3 = exact match
|
313
|
-
/[\"\'=]/g, // 4 = replace exact match flags
|
314
|
-
/[^\w,. \-()]/g, // 5 = replace non-digits (from digit & currency parser)
|
315
|
-
/[<>=]/g // 6 = replace operators
|
316
|
-
],
|
317
|
-
parsed = $ths.map(function(i){
|
318
|
-
return (ts.getData) ? ts.getData($ths.filter('[data-column="' + i + '"]:last'), c.headers[i], 'filter') === 'parsed' : $(this).hasClass('filter-parsed');
|
319
|
-
}).get(),
|
320
|
-
time, timer,
|
372
|
+
parsed, time, timer,
|
321
373
|
|
322
374
|
// dig fer gold
|
323
375
|
checkFilters = function(filter){
|
324
376
|
var arry = $.isArray(filter),
|
325
|
-
|
326
|
-
v = (arry) ? filter : $inpts.map(function(){
|
327
|
-
return $(this).val() || '';
|
328
|
-
}).get(),
|
377
|
+
v = (arry) ? filter : ts.getFilters(table),
|
329
378
|
cv = (v || []).join(''); // combined filter values
|
330
379
|
// add filter array back into inputs
|
331
380
|
if (arry) {
|
332
|
-
|
333
|
-
$(el).val(filter[i] || '');
|
334
|
-
});
|
381
|
+
ts.setFilters( $t, v );
|
335
382
|
}
|
336
|
-
if (wo.filter_hideFilters
|
383
|
+
if (wo.filter_hideFilters){
|
337
384
|
// show/hide filter row as needed
|
338
385
|
$t.find('.tablesorter-filter-row').trigger( cv === '' ? 'mouseleave' : 'mouseenter' );
|
339
386
|
}
|
@@ -353,27 +400,36 @@ $.tablesorter.addWidget({
|
|
353
400
|
}
|
354
401
|
},
|
355
402
|
findRows = function(filter, v, cv){
|
356
|
-
var $tb, $tr, $td, cr, r, l, ff, time,
|
403
|
+
var $tb, $tr, $td, cr, r, l, ff, time, r1, r2, searchFiltered;
|
357
404
|
if (c.debug) { time = new Date(); }
|
358
|
-
|
359
405
|
for (k = 0; k < b.length; k++ ){
|
360
|
-
|
361
|
-
$
|
406
|
+
if (b.eq(k).hasClass(c.cssInfoBlock)) { continue; } // ignore info blocks, issue #264
|
407
|
+
$tb = ts.processTbody(table, b.eq(k), true);
|
408
|
+
$tr = $tb.children('tr:not(.' + c.cssChildRow + ')');
|
362
409
|
l = $tr.length;
|
363
410
|
if (cv === '' || wo.filter_serversideFiltering){
|
364
|
-
$
|
411
|
+
$tb.children().show().removeClass(wo.filter_filteredRow);
|
365
412
|
} else {
|
413
|
+
// optimize searching only through already filtered rows - see #313
|
414
|
+
searchFiltered = true;
|
415
|
+
r = $t.data('lastSearch') || [];
|
416
|
+
$.each(v, function(i,val){
|
417
|
+
// check for changes from beginning of filter; but ignore if there is a logical "or" in the string
|
418
|
+
searchFiltered = (val || '').indexOf(r[i] || '') === 0 && searchFiltered && !/(\s+or\s+|\|)/g.test(val || '');
|
419
|
+
});
|
420
|
+
// can't search when all rows are hidden - this happens when looking for exact matches
|
421
|
+
if (searchFiltered && $tr.filter(':visible').length === 0) { searchFiltered = false; }
|
366
422
|
// loop through the rows
|
367
423
|
for (j = 0; j < l; j++){
|
368
|
-
|
369
|
-
|
424
|
+
r = $tr[j].className;
|
425
|
+
// skip child rows & already filtered rows
|
426
|
+
if ( wo.filter_regex.child.test(r) || (searchFiltered && wo.filter_regex.filtered.test(r)) ) { continue; }
|
370
427
|
r = true;
|
371
428
|
cr = $tr.eq(j).nextUntil('tr:not(.' + c.cssChildRow + ')');
|
372
429
|
// so, if "table.config.widgetOptions.filter_childRows" is true and there is
|
373
430
|
// a match anywhere in the child row, then it will make the row visible
|
374
431
|
// checked here so the option can be changed dynamically
|
375
|
-
t = (cr.length &&
|
376
|
-
typeof wo.filter_childRows !== 'undefined' ? wo.filter_childRows : true)) ? cr.text() : '';
|
432
|
+
t = (cr.length && wo.filter_childRows) ? cr.text() : '';
|
377
433
|
t = wo.filter_ignoreCase ? t.toLocaleLowerCase() : t;
|
378
434
|
$td = $tr.eq(j).children('td');
|
379
435
|
for (i = 0; i < cols; i++){
|
@@ -386,7 +442,7 @@ $.tablesorter.addWidget({
|
|
386
442
|
// using older or original tablesorter
|
387
443
|
x = $.trim($td.eq(i).text());
|
388
444
|
}
|
389
|
-
xi = !
|
445
|
+
xi = !wo.filter_regex.type.test(typeof x) && wo.filter_ignoreCase ? x.toLocaleLowerCase() : x;
|
390
446
|
ff = r; // if r is true, show that row
|
391
447
|
// val = case insensitive, v[i] = case sensitive
|
392
448
|
val = wo.filter_ignoreCase ? v[i].toLocaleLowerCase() : v[i];
|
@@ -402,15 +458,16 @@ $.tablesorter.addWidget({
|
|
402
458
|
ff = wo.filter_functions[i][v[i]](x, c.cache[k].normalized[j][i], v[i], i);
|
403
459
|
}
|
404
460
|
// Look for regex
|
405
|
-
} else if (
|
406
|
-
rg =
|
461
|
+
} else if (wo.filter_regex.regex.test(val)){
|
462
|
+
rg = wo.filter_regex.regex.exec(val);
|
407
463
|
try {
|
408
464
|
ff = new RegExp(rg[1], rg[2]).test(xi);
|
409
465
|
} catch (err){
|
410
466
|
ff = false;
|
411
467
|
}
|
412
|
-
// Look for quotes or equals to get an exact match
|
413
|
-
|
468
|
+
// Look for quotes or equals to get an exact match; ignore type since xi could be numeric
|
469
|
+
/*jshint eqeqeq:false */
|
470
|
+
} else if (val.replace(wo.filter_regex.exact, '') == xi){
|
414
471
|
ff = true;
|
415
472
|
// Look for a not match
|
416
473
|
} else if (/^\!/.test(val)){
|
@@ -419,14 +476,52 @@ $.tablesorter.addWidget({
|
|
419
476
|
ff = val === '' ? true : !(wo.filter_startsWith ? s === 0 : s >= 0);
|
420
477
|
// Look for operators >, >=, < or <=
|
421
478
|
} else if (/^[<>]=?/.test(val)){
|
422
|
-
|
423
|
-
|
424
|
-
|
479
|
+
s = fmt(val.replace(wo.filter_regex.nondigit, '').replace(wo.filter_regex.operators,''), table);
|
480
|
+
// parse filter value in case we're comparing numbers (dates)
|
481
|
+
if (parsed[i] || c.parsers[i].type === 'numeric') {
|
482
|
+
rg = c.parsers[i].format('' + val.replace(wo.filter_regex.operators,''), table, $ths.eq(i), i);
|
483
|
+
s = (rg !== '' && !isNaN(rg)) ? rg : s;
|
484
|
+
}
|
485
|
+
// xi may be numeric - see issue #149;
|
486
|
+
// check if c.cache[k].normalized[j] is defined, because sometimes j goes out of range? (numeric columns)
|
487
|
+
rg = ( parsed[i] || c.parsers[i].type === 'numeric' ) && !isNaN(s) && c.cache[k].normalized[j] ? c.cache[k].normalized[j][i] :
|
488
|
+
isNaN(xi) ? fmt(xi.replace(wo.filter_regex.nondigit, ''), table) : fmt(xi, table);
|
425
489
|
if (/>/.test(val)) { ff = />=/.test(val) ? rg >= s : rg > s; }
|
426
490
|
if (/</.test(val)) { ff = /<=/.test(val) ? rg <= s : rg < s; }
|
427
|
-
|
428
|
-
|
429
|
-
|
491
|
+
if (s === '') { ff = true; } // keep showing all rows if nothing follows the operator
|
492
|
+
// Look for an AND or && operator (logical and)
|
493
|
+
} else if (/\s+(AND|&&)\s+/g.test(v[i])) {
|
494
|
+
s = val.split(/(?:\s+(?:and|&&)\s+)/g);
|
495
|
+
ff = xi.search($.trim(s[0])) >= 0;
|
496
|
+
r1 = s.length - 1;
|
497
|
+
while (ff && r1) {
|
498
|
+
ff = ff && xi.search($.trim(s[r1])) >= 0;
|
499
|
+
r1--;
|
500
|
+
}
|
501
|
+
// Look for a range (using " to " or " - ") - see issue #166; thanks matzhu!
|
502
|
+
} else if (/\s+(-|to)\s+/.test(val)){
|
503
|
+
s = val.split(/(?: - | to )/); // make sure the dash is for a range and not indicating a negative number
|
504
|
+
r1 = fmt(s[0].replace(wo.filter_regex.nondigit, ''), table);
|
505
|
+
r2 = fmt(s[1].replace(wo.filter_regex.nondigit, ''), table);
|
506
|
+
// parse filter value in case we're comparing numbers (dates)
|
507
|
+
if (parsed[i] || c.parsers[i].type === 'numeric') {
|
508
|
+
rg = c.parsers[i].format('' + s[0], table, $ths.eq(i), i);
|
509
|
+
r1 = (rg !== '' && !isNaN(rg)) ? rg : r1;
|
510
|
+
rg = c.parsers[i].format('' + s[1], table, $ths.eq(i), i);
|
511
|
+
r2 = (rg !== '' && !isNaN(rg)) ? rg : r2;
|
512
|
+
}
|
513
|
+
rg = ( parsed[i] || c.parsers[i].type === 'numeric' ) && !isNaN(r1) && !isNaN(r2) ? c.cache[k].normalized[j][i] :
|
514
|
+
isNaN(xi) ? fmt(xi.replace(wo.filter_regex.nondigit, ''), table) : fmt(xi, table);
|
515
|
+
if (r1 > r2) { ff = r1; r1 = r2; r2 = ff; } // swap
|
516
|
+
ff = (rg >= r1 && rg <= r2) || (r1 === '' || r2 === '') ? true : false;
|
517
|
+
// Look for wild card: ? = single, * = multiple, or | = logical OR
|
518
|
+
} else if ( /[\?|\*]/.test(val) || /\s+OR\s+/.test(v[i]) ){
|
519
|
+
s = val.replace(/\s+OR\s+/gi,"|");
|
520
|
+
// look for an exact match with the "or" unless the "filter-match" class is found
|
521
|
+
if (!$ths.filter('[data-column="' + i + '"]:last').hasClass('filter-match') && /\|/.test(s)) {
|
522
|
+
s = '^(' + s + ')$';
|
523
|
+
}
|
524
|
+
ff = new RegExp( s.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(xi);
|
430
525
|
// Look for match, and add child row data for matching
|
431
526
|
} else {
|
432
527
|
x = (xi + t).indexOf(val);
|
@@ -436,28 +531,32 @@ $.tablesorter.addWidget({
|
|
436
531
|
}
|
437
532
|
}
|
438
533
|
$tr[j].style.display = (r ? '' : 'none');
|
439
|
-
$tr.eq(j)[r ? 'removeClass' : 'addClass'](
|
534
|
+
$tr.eq(j)[r ? 'removeClass' : 'addClass'](wo.filter_filteredRow);
|
440
535
|
if (cr.length) { cr[r ? 'show' : 'hide'](); }
|
441
536
|
}
|
442
537
|
}
|
443
|
-
|
538
|
+
ts.processTbody(table, $tb, false);
|
444
539
|
}
|
445
|
-
|
446
540
|
last = cv; // save last search
|
541
|
+
$t.data('lastSearch', v);
|
447
542
|
if (c.debug){
|
448
543
|
ts.benchmark("Completed filter widget search", time);
|
449
544
|
}
|
450
545
|
$t.trigger('applyWidgets'); // make sure zebra widget is applied
|
451
546
|
$t.trigger('filterEnd');
|
452
547
|
},
|
453
|
-
buildSelect = function(i, updating){
|
454
|
-
var o, arry = [];
|
548
|
+
buildSelect = function(i, updating, onlyavail){
|
549
|
+
var o, t, arry = [], currentVal;
|
455
550
|
i = parseInt(i, 10);
|
456
|
-
|
551
|
+
t = $ths.filter('[data-column="' + i + '"]:last');
|
552
|
+
// t.data('placeholder') won't work in jQuery older than 1.4.3
|
553
|
+
o = '<option value="">' + (t.data('placeholder') || t.attr('data-placeholder') || '') + '</option>';
|
457
554
|
for (k = 0; k < b.length; k++ ){
|
458
555
|
l = c.cache[k].row.length;
|
459
556
|
// loop through the rows
|
460
557
|
for (j = 0; j < l; j++){
|
558
|
+
// check if has class filtered
|
559
|
+
if (onlyavail && c.cache[k].row[j][0].className.match(wo.filter_filteredRow)) { continue; }
|
461
560
|
// get non-normalized cell content
|
462
561
|
if (wo.filter_useParsedData){
|
463
562
|
arry.push( '' + c.cache[k].normalized[j][i] );
|
@@ -474,13 +573,18 @@ $.tablesorter.addWidget({
|
|
474
573
|
// if $.tablesorter.sortText exists (not in the original tablesorter),
|
475
574
|
// then natural sort the list otherwise use a basic sort
|
476
575
|
arry = $.grep(arry, function(v, k){
|
477
|
-
return $.inArray(v
|
576
|
+
return $.inArray(v, arry) === k;
|
478
577
|
});
|
479
|
-
arry = (ts.sortText) ? arry.sort(function(a,b){ return ts.sortText(table, a, b, i); }) : arry.sort(true);
|
578
|
+
arry = (ts.sortText) ? arry.sort(function(a, b){ return ts.sortText(table, a, b, i); }) : arry.sort(true);
|
579
|
+
|
580
|
+
// Get curent filter value
|
581
|
+
currentVal = $t.find('thead').find('select.' + css + '[data-column="' + i + '"]').val();
|
480
582
|
|
481
583
|
// build option list
|
482
584
|
for (k = 0; k < arry.length; k++){
|
483
|
-
|
585
|
+
t = arry[k].replace(/\"/g, """);
|
586
|
+
// replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346
|
587
|
+
o += arry[k] !== '' ? '<option value="' + t + '"' + (currentVal === t ? ' selected="selected"' : '') +'>' + arry[k] + '</option>' : '';
|
484
588
|
}
|
485
589
|
$t.find('thead').find('select.' + css + '[data-column="' + i + '"]')[ updating ? 'html' : 'append' ](o);
|
486
590
|
},
|
@@ -492,73 +596,114 @@ $.tablesorter.addWidget({
|
|
492
596
|
if ((t.hasClass('filter-select') || wo.filter_functions && wo.filter_functions[i] === true) && !t.hasClass('filter-false')){
|
493
597
|
if (!wo.filter_functions) { wo.filter_functions = {}; }
|
494
598
|
wo.filter_functions[i] = true; // make sure this select gets processed by filter_functions
|
495
|
-
buildSelect(i, updating);
|
599
|
+
buildSelect(i, updating, t.hasClass(wo.filter_onlyAvail));
|
496
600
|
}
|
497
601
|
}
|
602
|
+
},
|
603
|
+
searching = function(filter){
|
604
|
+
if (typeof filter === 'undefined' || filter === true){
|
605
|
+
// delay filtering
|
606
|
+
clearTimeout(timer);
|
607
|
+
timer = setTimeout(function(){
|
608
|
+
checkFilters(filter);
|
609
|
+
}, wo.filter_liveSearch ? wo.filter_searchDelay : 10);
|
610
|
+
} else {
|
611
|
+
// skip delay
|
612
|
+
checkFilters(filter);
|
613
|
+
}
|
498
614
|
};
|
499
|
-
|
500
615
|
if (c.debug){
|
501
616
|
time = new Date();
|
502
617
|
}
|
503
|
-
wo.
|
504
|
-
wo.
|
618
|
+
wo.filter_regex.child = new RegExp(c.cssChildRow);
|
619
|
+
wo.filter_regex.filtered = new RegExp(wo.filter_filteredRow);
|
505
620
|
// don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156
|
506
621
|
if (wo.filter_columnFilters !== false && $ths.filter('.filter-false').length !== $ths.length){
|
507
|
-
|
622
|
+
// build filter row
|
623
|
+
t = '<tr class="tablesorter-filter-row">';
|
624
|
+
for (i = 0; i < cols; i++){
|
625
|
+
t += '<td></td>';
|
626
|
+
}
|
627
|
+
c.$filters = $(t += '</tr>').appendTo( $t.find('thead').eq(0) ).find('td');
|
628
|
+
// build each filter input
|
508
629
|
for (i = 0; i < cols; i++){
|
509
630
|
dis = false;
|
510
631
|
$th = $ths.filter('[data-column="' + i + '"]:last'); // assuming last cell of a column is the main column
|
511
632
|
sel = (wo.filter_functions && wo.filter_functions[i] && typeof wo.filter_functions[i] !== 'function') || $th.hasClass('filter-select');
|
512
|
-
t += '<td>';
|
513
|
-
if (sel){
|
514
|
-
t += '<select data-column="' + i + '" class="' + css;
|
515
|
-
} else {
|
516
|
-
t += '<input type="search" placeholder="' + ($th.attr('data-placeholder') || "") + '" data-column="' + i + '" class="' + css;
|
517
|
-
}
|
518
633
|
// use header option - headers: { 1: { filter: false } } OR add class="filter-false"
|
519
634
|
if (ts.getData){
|
520
|
-
dis = ts.getData($th[0], c.headers[i], 'filter') === 'false';
|
521
635
|
// get data from jQuery data, metadata, headers option or header class name
|
522
|
-
|
636
|
+
dis = ts.getData($th[0], c.headers[i], 'filter') === 'false';
|
523
637
|
} else {
|
524
|
-
dis = (c.headers[i] && c.headers[i].hasOwnProperty('filter') && c.headers[i].filter === false) || $th.hasClass('filter-false');
|
525
638
|
// only class names and header options - keep this for compatibility with tablesorter v2.0.5
|
526
|
-
|
639
|
+
dis = (c.headers[i] && c.headers[i].hasOwnProperty('filter') && c.headers[i].filter === false) || $th.hasClass('filter-false');
|
640
|
+
}
|
641
|
+
|
642
|
+
if (sel){
|
643
|
+
t = $('<select>').appendTo( c.$filters.eq(i) );
|
644
|
+
} else {
|
645
|
+
if (wo.filter_formatter && $.isFunction(wo.filter_formatter[i])) {
|
646
|
+
t = wo.filter_formatter[i]( c.$filters.eq(i), i );
|
647
|
+
// no element returned, so lets go find it
|
648
|
+
if (t && t.length === 0) { t = c.$filters.eq(i).children('input'); }
|
649
|
+
// element not in DOM, so lets attach it
|
650
|
+
if (t && (t.parent().length === 0 || (t.parent().length && t.parent()[0] !== c.$filters[i]))) {
|
651
|
+
c.$filters.eq(i).append(t);
|
652
|
+
}
|
653
|
+
} else {
|
654
|
+
t = $('<input type="search">').appendTo( c.$filters.eq(i) );
|
655
|
+
}
|
656
|
+
if (t) {
|
657
|
+
t.attr('placeholder', $th.data('placeholder') || $th.attr('data-placeholder') || '');
|
658
|
+
}
|
659
|
+
}
|
660
|
+
if (t) {
|
661
|
+
t.addClass(css).attr('data-column', i);
|
662
|
+
if (dis) {
|
663
|
+
t.addClass('disabled')[0].disabled = true; // disabled!
|
664
|
+
}
|
527
665
|
}
|
528
|
-
t += (sel ? '></select>' : '>') + '</td>';
|
529
666
|
}
|
530
|
-
$t.find('thead').eq(0).append(t += '</tr>');
|
531
667
|
}
|
532
668
|
$t
|
533
|
-
|
534
|
-
|
535
|
-
|
669
|
+
.bind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join('.tsfilter '), function(e, filter){
|
670
|
+
if (!/(search|filterReset|filterEnd)/.test(e.type)){
|
671
|
+
e.stopPropagation();
|
672
|
+
buildDefault(true);
|
673
|
+
}
|
674
|
+
if (e.type === 'filterReset') {
|
675
|
+
$t.find('.' + css).val('');
|
676
|
+
}
|
677
|
+
if (e.type === 'filterEnd') {
|
536
678
|
buildDefault(true);
|
679
|
+
} else {
|
680
|
+
// send false argument to force a new search; otherwise if the filter hasn't changed, it will return
|
681
|
+
filter = e.type === 'search' ? filter : e.type === 'updateComplete' ? $t.data('lastSearch') : '';
|
682
|
+
searching(filter);
|
537
683
|
}
|
538
|
-
checkFilters(e.type === 'search' ? filter : '');
|
539
684
|
return false;
|
540
685
|
})
|
541
686
|
.find('input.' + css).bind('keyup search', function(e, filter){
|
542
|
-
//
|
543
|
-
if (
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
687
|
+
// emulate what webkit does.... escape clears the filter
|
688
|
+
if (e.which === 27) {
|
689
|
+
this.value = '';
|
690
|
+
// liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace
|
691
|
+
} else if ( (typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch && this.value !== '') || ( e.type === 'keyup' &&
|
692
|
+
( (e.which < 32 && e.which !== 8 && wo.filter_liveSearch === true && e.which !== 13) || (e.which >= 37 && e.which <=40) || (e.which !== 13 && wo.filter_liveSearch === false) ) ) ) {
|
693
|
+
return;
|
548
694
|
}
|
549
|
-
|
550
|
-
clearTimeout(timer);
|
551
|
-
timer = setTimeout(function(){
|
552
|
-
checkFilters();
|
553
|
-
}, wo.filter_searchDelay || 300);
|
695
|
+
searching(filter);
|
554
696
|
});
|
555
697
|
|
698
|
+
// parse columns after formatter, in case the class is added at that point
|
699
|
+
parsed = $ths.map(function(i){
|
700
|
+
return (ts.getData) ? ts.getData($ths.filter('[data-column="' + i + '"]:last'), c.headers[i], 'filter') === 'parsed' : $(this).hasClass('filter-parsed');
|
701
|
+
}).get();
|
702
|
+
|
556
703
|
// reset button/link
|
557
704
|
if (wo.filter_reset && $(wo.filter_reset).length){
|
558
|
-
$(wo.filter_reset).bind('click', function(){
|
559
|
-
$t.
|
560
|
-
checkFilters();
|
561
|
-
return false;
|
705
|
+
$(wo.filter_reset).bind('click.tsfilter', function(){
|
706
|
+
$t.trigger('filterReset');
|
562
707
|
});
|
563
708
|
}
|
564
709
|
if (wo.filter_functions){
|
@@ -573,7 +718,7 @@ $.tablesorter.addWidget({
|
|
573
718
|
// add custom drop down list
|
574
719
|
for (str in wo.filter_functions[col]){
|
575
720
|
if (typeof str === 'string'){
|
576
|
-
ff += ff === '' ? '<option value="">' + (t.attr('data-placeholder') ||
|
721
|
+
ff += ff === '' ? '<option value="">' + (t.data('placeholder') || t.attr('data-placeholder') || '') + '</option>' : '';
|
577
722
|
ff += '<option value="' + str + '">' + str + '</option>';
|
578
723
|
}
|
579
724
|
}
|
@@ -586,11 +731,11 @@ $.tablesorter.addWidget({
|
|
586
731
|
// it would append the same options twice.
|
587
732
|
buildDefault(true);
|
588
733
|
|
589
|
-
$t.find('select.' + css).bind('change search', function(){
|
590
|
-
checkFilters();
|
734
|
+
$t.find('select.' + css).bind('change search', function(e, filter){
|
735
|
+
checkFilters(filter);
|
591
736
|
});
|
592
737
|
|
593
|
-
if (wo.filter_hideFilters
|
738
|
+
if (wo.filter_hideFilters){
|
594
739
|
$t
|
595
740
|
.find('.tablesorter-filter-row')
|
596
741
|
.addClass('hideme')
|
@@ -607,7 +752,7 @@ $.tablesorter.addWidget({
|
|
607
752
|
// $(':focus') needs jQuery 1.6+
|
608
753
|
if ($(document.activeElement).closest('tr')[0] !== ft[0]){
|
609
754
|
// get all filter values
|
610
|
-
all = $t.find('.' +
|
755
|
+
all = $t.find('.' + wo.filter_cssFilter).map(function(){
|
611
756
|
return $(this).val() || '';
|
612
757
|
}).get().join('');
|
613
758
|
// don't hide row if any filter has a value
|
@@ -623,7 +768,7 @@ $.tablesorter.addWidget({
|
|
623
768
|
clearTimeout(st);
|
624
769
|
st = setTimeout(function(){
|
625
770
|
// don't hide row if any filter has a value
|
626
|
-
if ($t.find('.' +
|
771
|
+
if ($t.find('.' + wo.filter_cssFilter).map(function(){ return $(this).val() || ''; }).get().join('') === ''){
|
627
772
|
ft2[ e.type === 'focus' ? 'removeClass' : 'addClass']('hideme');
|
628
773
|
}
|
629
774
|
}, 200);
|
@@ -632,7 +777,7 @@ $.tablesorter.addWidget({
|
|
632
777
|
|
633
778
|
// show processing icon
|
634
779
|
if (c.showProcessing) {
|
635
|
-
$t.bind('filterStart filterEnd', function(e, v) {
|
780
|
+
$t.bind('filterStart.tsfilter filterEnd.tsfilter', function(e, v) {
|
636
781
|
var fc = (v) ? $t.find('.' + c.cssHeader).filter('[data-column]').filter(function(){
|
637
782
|
return v[$(this).data('column')] !== '';
|
638
783
|
}) : '';
|
@@ -643,103 +788,143 @@ $.tablesorter.addWidget({
|
|
643
788
|
if (c.debug){
|
644
789
|
ts.benchmark("Applying Filter widget", time);
|
645
790
|
}
|
791
|
+
// add default values
|
792
|
+
$t.bind('tablesorter-initialized', function(){
|
793
|
+
ff = ts.getFilters(table);
|
794
|
+
for (i = 0; i < ff.length; i++) {
|
795
|
+
ff[i] = $ths.filter('[data-column="' + i + '"]:last').attr(wo.filter_defaultAttrib) || ff[i];
|
796
|
+
}
|
797
|
+
ts.setFilters(table, ff, true);
|
798
|
+
});
|
646
799
|
// filter widget initialized
|
647
800
|
$t.trigger('filterInit');
|
801
|
+
checkFilters();
|
648
802
|
}
|
649
803
|
},
|
650
804
|
remove: function(table, c, wo){
|
651
805
|
var k, $tb,
|
652
|
-
$t =
|
806
|
+
$t = c.$table,
|
653
807
|
b = c.$tbodies;
|
654
808
|
$t
|
655
809
|
.removeClass('hasFilters')
|
656
810
|
// add .tsfilter namespace to all BUT search
|
657
|
-
.unbind('addRows updateCell update appendCache search'.split(' ').join('.tsfilter'))
|
811
|
+
.unbind('addRows updateCell update updateComplete appendCache search filterStart filterEnd '.split(' ').join('.tsfilter '))
|
658
812
|
.find('.tablesorter-filter-row').remove();
|
659
813
|
for (k = 0; k < b.length; k++ ){
|
660
|
-
$tb =
|
661
|
-
$tb.children().removeClass(
|
662
|
-
|
814
|
+
$tb = ts.processTbody(table, b.eq(k), true); // remove tbody
|
815
|
+
$tb.children().removeClass(wo.filter_filteredRow).show();
|
816
|
+
ts.processTbody(table, $tb, false); // restore tbody
|
663
817
|
}
|
664
|
-
if (wo.filterreset) { $(wo.filter_reset).unbind('click'); }
|
818
|
+
if (wo.filterreset) { $(wo.filter_reset).unbind('click.tsfilter'); }
|
665
819
|
}
|
666
820
|
});
|
821
|
+
ts.getFilters = function(table) {
|
822
|
+
var c = table ? $(table)[0].config : {};
|
823
|
+
if (c && c.widgetOptions && !c.widgetOptions.filter_columnFilters) { return $(table).data('lastSearch'); }
|
824
|
+
return c && c.$filters ? c.$filters.find('.' + c.widgetOptions.filter_cssFilter).map(function(i, el) {
|
825
|
+
return $(el).val();
|
826
|
+
}).get() || [] : false;
|
827
|
+
};
|
828
|
+
ts.setFilters = function(table, filter, apply) {
|
829
|
+
var $t = $(table),
|
830
|
+
c = $t.length ? $t[0].config : {},
|
831
|
+
valid = c && c.$filters ? c.$filters.find('.' + c.widgetOptions.filter_cssFilter).each(function(i, el) {
|
832
|
+
$(el).val(filter[i] || '');
|
833
|
+
}).trigger('change.tsfilter') || false : false;
|
834
|
+
if (apply) { $t.trigger('search', [filter, false]); }
|
835
|
+
return !!valid;
|
836
|
+
};
|
667
837
|
|
668
838
|
// Widget: Sticky headers
|
669
839
|
// based on this awesome article:
|
670
840
|
// http://css-tricks.com/13465-persistent-headers/
|
671
841
|
// and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech
|
672
842
|
// **************************
|
673
|
-
|
843
|
+
ts.addWidget({
|
674
844
|
id: "stickyHeaders",
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
845
|
+
priority: 60,
|
846
|
+
options: {
|
847
|
+
stickyHeaders : 'tablesorter-stickyHeader',
|
848
|
+
stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element
|
849
|
+
stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists
|
850
|
+
stickyHeaders_addResizeEvent : true, // trigger "resize" event on headers
|
851
|
+
stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header
|
852
|
+
stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs
|
853
|
+
},
|
854
|
+
format: function(table, c, wo){
|
855
|
+
if (c.$table.hasClass('hasStickyHeaders')) { return; }
|
856
|
+
var $t = c.$table,
|
857
|
+
$win = $(window),
|
858
|
+
header = $t.children('thead:first'),
|
682
859
|
hdrCells = header.children('tr:not(.sticky-false)').children(),
|
683
|
-
css = wo.stickyHeaders || 'tablesorter-stickyHeader',
|
684
860
|
innr = '.tablesorter-header-inner',
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
861
|
+
tfoot = $t.find('tfoot'),
|
862
|
+
filterInputs = '.' + (wo.filter_cssFilter || 'tablesorter-filter'),
|
863
|
+
$stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '',
|
864
|
+
stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0,
|
865
|
+
stickyzIndex = wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2,
|
866
|
+
$stickyTable = wo.$sticky = $t.clone()
|
867
|
+
.addClass('containsStickyHeaders')
|
691
868
|
.css({
|
692
|
-
width : header.outerWidth(true),
|
693
869
|
position : 'fixed',
|
694
870
|
margin : 0,
|
695
|
-
top :
|
871
|
+
top : stickyOffset,
|
696
872
|
visibility : 'hidden',
|
697
|
-
zIndex :
|
873
|
+
zIndex : stickyzIndex
|
698
874
|
}),
|
699
|
-
|
875
|
+
stkyHdr = $stickyTable.children('thead:first').addClass(wo.stickyHeaders),
|
876
|
+
stkyCells,
|
700
877
|
laststate = '',
|
701
878
|
spacing = 0,
|
879
|
+
flag = false,
|
702
880
|
resizeHdr = function(){
|
881
|
+
stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0;
|
703
882
|
var bwsr = navigator.userAgent;
|
704
883
|
spacing = 0;
|
705
884
|
// yes, I dislike browser sniffing, but it really is needed here :(
|
706
885
|
// webkit automatically compensates for border spacing
|
707
|
-
if ($
|
886
|
+
if ($t.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(bwsr)) {
|
708
887
|
// Firefox & Opera use the border-spacing
|
709
888
|
// update border-spacing here because of demos that switch themes
|
710
889
|
spacing = parseInt(hdrCells.eq(0).css('border-left-width'), 10) * 2;
|
711
890
|
}
|
712
|
-
|
713
|
-
left : header.offset().left - win.scrollLeft() - spacing,
|
714
|
-
width:
|
891
|
+
$stickyTable.css({
|
892
|
+
left : header.offset().left - $win.scrollLeft() - spacing,
|
893
|
+
width: $t.width()
|
715
894
|
});
|
716
|
-
stkyCells
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
.find(innr).each(function(i){
|
725
|
-
var hi = hdrCells.eq(i).find(innr),
|
726
|
-
w = hi.width(); // - ( parseInt(hi.css('padding-left'), 10) + parseInt(hi.css('padding-right'), 10) );
|
727
|
-
$(this).width(w);
|
895
|
+
stkyCells.filter(':visible').each(function(i){
|
896
|
+
var $h = hdrCells.filter(':visible').eq(i);
|
897
|
+
$(this)
|
898
|
+
.css({
|
899
|
+
width: $h.width() - spacing,
|
900
|
+
height: $h.height()
|
901
|
+
})
|
902
|
+
.find(innr).width( $h.find(innr).width() );
|
728
903
|
});
|
729
904
|
};
|
905
|
+
// fix clone ID, if it exists - fixes #271
|
906
|
+
if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; }
|
730
907
|
// clear out cloned table, except for sticky header
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
908
|
+
// include caption & filter row (fixes #126 & #249)
|
909
|
+
$stickyTable.find('thead:gt(0), tr.sticky-false, tbody, tfoot').remove();
|
910
|
+
if (!wo.stickyHeaders_includeCaption) {
|
911
|
+
$stickyTable.find('caption').remove();
|
912
|
+
}
|
913
|
+
// issue #172 - find td/th in sticky header
|
914
|
+
stkyCells = stkyHdr.children().children();
|
915
|
+
$stickyTable.css({ height:0, width:0, padding:0, margin:0, border:0 });
|
735
916
|
// remove resizable block
|
736
917
|
stkyCells.find('.tablesorter-resizer').remove();
|
737
918
|
// update sticky header class names to match real header after sorting
|
738
|
-
$
|
919
|
+
$t
|
920
|
+
.addClass('hasStickyHeaders')
|
739
921
|
.bind('sortEnd.tsSticky', function(){
|
740
|
-
hdrCells.each(function(i){
|
741
|
-
var t = stkyCells.eq(i);
|
742
|
-
t
|
922
|
+
hdrCells.filter(':visible').each(function(i){
|
923
|
+
var t = stkyCells.filter(':visible').eq(i);
|
924
|
+
t
|
925
|
+
.attr('class', $(this).attr('class'))
|
926
|
+
// remove processing icon
|
927
|
+
.removeClass(c.cssProcessing);
|
743
928
|
if (c.cssIcon){
|
744
929
|
t
|
745
930
|
.find('.' + c.cssIcon)
|
@@ -750,54 +935,86 @@ $.tablesorter.addWidget({
|
|
750
935
|
.bind('pagerComplete.tsSticky', function(){
|
751
936
|
resizeHdr();
|
752
937
|
});
|
753
|
-
//
|
754
|
-
hdrCells.find(
|
755
|
-
var t = $(this)
|
756
|
-
stkyCells.eq(i)
|
938
|
+
// http://stackoverflow.com/questions/5312849/jquery-find-self;
|
939
|
+
hdrCells.find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ).each(function(i){
|
940
|
+
var t = $(this),
|
757
941
|
// clicking on sticky will trigger sort
|
758
|
-
.bind('mouseup', function(e){
|
942
|
+
$cell = stkyHdr.children('tr.tablesorter-headerRow').children().eq(i).bind('mouseup', function(e){
|
759
943
|
t.trigger(e, true); // external mouseup flag (click timer is ignored)
|
760
|
-
})
|
761
|
-
// prevent sticky header text selection
|
762
|
-
.bind('mousedown', function(){
|
763
|
-
this.onselectstart = function(){ return false; };
|
764
|
-
return false;
|
765
944
|
});
|
945
|
+
// prevent sticky header text selection
|
946
|
+
if (c.cancelSelection) {
|
947
|
+
$cell
|
948
|
+
.attr('unselectable', 'on')
|
949
|
+
.bind('selectstart', false)
|
950
|
+
.css({
|
951
|
+
'user-select': 'none',
|
952
|
+
'MozUserSelect': 'none'
|
953
|
+
});
|
954
|
+
}
|
766
955
|
});
|
767
956
|
// add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned.
|
768
|
-
$
|
957
|
+
$t.after( $stickyTable );
|
769
958
|
// make it sticky!
|
770
|
-
win
|
771
|
-
|
772
|
-
var
|
773
|
-
|
774
|
-
|
959
|
+
$win.bind('scroll.tsSticky resize.tsSticky', function(e){
|
960
|
+
if (!$t.is(':visible')) { return; } // fixes #278
|
961
|
+
var pre = 'tablesorter-sticky-',
|
962
|
+
offset = $t.offset(),
|
963
|
+
cap = (wo.stickyHeaders_includeCaption ? 0 : $t.find('caption').outerHeight(true)),
|
964
|
+
sTop = $win.scrollTop() + stickyOffset - cap,
|
965
|
+
tableHt = $t.height() - ($stickyTable.height() + (tfoot.height() || 0)),
|
775
966
|
vis = (sTop > offset.top) && (sTop < offset.top + tableHt) ? 'visible' : 'hidden';
|
776
|
-
|
967
|
+
$stickyTable
|
968
|
+
.removeClass(pre + 'visible ' + pre + 'hidden')
|
969
|
+
.addClass(pre + vis)
|
777
970
|
.css({
|
778
971
|
// adjust when scrolling horizontally - fixes issue #143
|
779
|
-
left : header.offset().left - win.scrollLeft() - spacing,
|
972
|
+
left : header.offset().left - $win.scrollLeft() - spacing,
|
780
973
|
visibility : vis
|
781
974
|
});
|
782
|
-
if (vis !== laststate){
|
975
|
+
if (vis !== laststate || e.type === 'resize'){
|
783
976
|
// make sure the column widths match
|
784
977
|
resizeHdr();
|
785
978
|
laststate = vis;
|
786
979
|
}
|
787
|
-
})
|
788
|
-
.bind('resize.tsSticky', function(){
|
789
|
-
resizeHdr();
|
790
980
|
});
|
981
|
+
if (wo.stickyHeaders_addResizeEvent) {
|
982
|
+
ts.addHeaderResizeEvent(table);
|
983
|
+
}
|
984
|
+
|
985
|
+
// look for filter widget
|
986
|
+
$t.bind('filterEnd', function(){
|
987
|
+
if (flag) { return; }
|
988
|
+
stkyHdr.find('.tablesorter-filter-row').children().each(function(i){
|
989
|
+
$(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(i).val() );
|
990
|
+
});
|
991
|
+
});
|
992
|
+
stkyCells.find(filterInputs).bind('keyup search change', function(e){
|
993
|
+
// ignore arrow and meta keys; allow backspace
|
994
|
+
if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; }
|
995
|
+
flag = true;
|
996
|
+
var $f = $(this), col = $f.attr('data-column');
|
997
|
+
c.$filters.find(filterInputs).eq(col)
|
998
|
+
.val( $f.val() )
|
999
|
+
.trigger('search');
|
1000
|
+
setTimeout(function(){
|
1001
|
+
flag = false;
|
1002
|
+
}, wo.filter_searchDelay);
|
1003
|
+
});
|
1004
|
+
$t.trigger('stickyHeadersInit');
|
1005
|
+
|
791
1006
|
},
|
792
1007
|
remove: function(table, c, wo){
|
793
|
-
|
794
|
-
css = wo.stickyHeaders || 'tablesorter-stickyHeader';
|
795
|
-
$t
|
1008
|
+
c.$table
|
796
1009
|
.removeClass('hasStickyHeaders')
|
797
1010
|
.unbind('sortEnd.tsSticky pagerComplete.tsSticky')
|
798
|
-
.find('.' +
|
799
|
-
if (wo.$sticky) { wo.$sticky.remove(); } // remove cloned
|
800
|
-
|
1011
|
+
.find('.' + wo.stickyHeaders).remove();
|
1012
|
+
if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table
|
1013
|
+
// don't unbind if any table on the page still has stickyheaders applied
|
1014
|
+
if (!$('.hasStickyHeaders').length) {
|
1015
|
+
$(window).unbind('scroll.tsSticky resize.tsSticky');
|
1016
|
+
}
|
1017
|
+
ts.addHeaderResizeEvent(table, false);
|
801
1018
|
}
|
802
1019
|
});
|
803
1020
|
|
@@ -805,39 +1022,42 @@ $.tablesorter.addWidget({
|
|
805
1022
|
// this widget saves the column widths if
|
806
1023
|
// $.tablesorter.storage function is included
|
807
1024
|
// **************************
|
808
|
-
|
1025
|
+
ts.addWidget({
|
809
1026
|
id: "resizable",
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
1027
|
+
priority: 40,
|
1028
|
+
options: {
|
1029
|
+
resizable : true,
|
1030
|
+
resizable_addLastColumn : false
|
1031
|
+
},
|
1032
|
+
format: function(table, c, wo){
|
1033
|
+
if (c.$table.hasClass('hasResizable')) { return; }
|
1034
|
+
c.$table.addClass('hasResizable');
|
1035
|
+
var $t, t, i, j, s = {}, $c, $cols, w, tw,
|
1036
|
+
$tbl = c.$table,
|
817
1037
|
position = 0,
|
818
1038
|
$target = null,
|
819
1039
|
$next = null,
|
820
1040
|
fullWidth = Math.abs($tbl.parent().width() - $tbl.width()) < 20,
|
821
1041
|
stopResize = function(){
|
822
|
-
if (
|
1042
|
+
if (ts.storage && $target){
|
823
1043
|
s[$target.index()] = $target.width();
|
824
1044
|
s[$next.index()] = $next.width();
|
825
1045
|
$target.width( s[$target.index()] );
|
826
1046
|
$next.width( s[$next.index()] );
|
827
1047
|
if (wo.resizable !== false){
|
828
|
-
|
1048
|
+
ts.storage(table, 'tablesorter-resizable', s);
|
829
1049
|
}
|
830
1050
|
}
|
831
1051
|
position = 0;
|
832
1052
|
$target = $next = null;
|
833
1053
|
$(window).trigger('resize'); // will update stickyHeaders, just in case
|
834
1054
|
};
|
835
|
-
s = (
|
1055
|
+
s = (ts.storage && wo.resizable !== false) ? ts.storage(table, 'tablesorter-resizable') : {};
|
836
1056
|
// process only if table ID or url match
|
837
1057
|
if (s){
|
838
1058
|
for (j in s){
|
839
|
-
if (!isNaN(j) && j < c.
|
840
|
-
|
1059
|
+
if (!isNaN(j) && j < c.$headers.length){
|
1060
|
+
c.$headers.eq(j).width(s[j]); // set saved resizable widths
|
841
1061
|
}
|
842
1062
|
}
|
843
1063
|
}
|
@@ -846,7 +1066,7 @@ $.tablesorter.addWidget({
|
|
846
1066
|
$t.children().each(function(){
|
847
1067
|
t = $(this);
|
848
1068
|
i = t.attr('data-column');
|
849
|
-
j =
|
1069
|
+
j = ts.getData( t, c.headers[i], 'resizable') === "false";
|
850
1070
|
$t.children().filter('[data-column="' + i + '"]').toggleClass('resizable-false', j);
|
851
1071
|
});
|
852
1072
|
// add wrapper inside each cell to allow for positioning of the resizable target block
|
@@ -856,7 +1076,8 @@ $.tablesorter.addWidget({
|
|
856
1076
|
// Firefox needs this inner div to position the resizer correctly
|
857
1077
|
$c.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>');
|
858
1078
|
}
|
859
|
-
|
1079
|
+
// don't include the last column of the row
|
1080
|
+
if (!wo.resizable_addLastColumn) { $c = $c.slice(0,-1); }
|
860
1081
|
$cols = $cols ? $cols.add($c) : $c;
|
861
1082
|
});
|
862
1083
|
$cols
|
@@ -895,12 +1116,12 @@ $.tablesorter.addWidget({
|
|
895
1116
|
position = e.pageX;
|
896
1117
|
});
|
897
1118
|
$tbl.find('thead:first')
|
898
|
-
.bind('mouseup.tsresize mouseleave.tsresize', function(
|
1119
|
+
.bind('mouseup.tsresize mouseleave.tsresize', function(){
|
899
1120
|
stopResize();
|
900
1121
|
})
|
901
1122
|
// right click to reset columns to default widths
|
902
1123
|
.bind('contextmenu.tsresize', function(){
|
903
|
-
|
1124
|
+
ts.resizableReset(table);
|
904
1125
|
// $.isEmptyObject() needs jQuery 1.4+
|
905
1126
|
var rtn = $.isEmptyObject ? $.isEmptyObject(s) : s === {}; // allow right click if already reset
|
906
1127
|
s = {};
|
@@ -908,7 +1129,7 @@ $.tablesorter.addWidget({
|
|
908
1129
|
});
|
909
1130
|
},
|
910
1131
|
remove: function(table, c, wo){
|
911
|
-
|
1132
|
+
c.$table
|
912
1133
|
.removeClass('hasResizable')
|
913
1134
|
.find('thead')
|
914
1135
|
.unbind('mouseup.tsresize mouseleave.tsresize contextmenu.tsresize')
|
@@ -916,12 +1137,12 @@ $.tablesorter.addWidget({
|
|
916
1137
|
.unbind('mousemove.tsresize mouseup.tsresize')
|
917
1138
|
// don't remove "tablesorter-wrapper" as uitheme uses it too
|
918
1139
|
.find('.tablesorter-resizer,.tablesorter-resizer-grip').remove();
|
919
|
-
|
1140
|
+
ts.resizableReset(table);
|
920
1141
|
}
|
921
1142
|
});
|
922
|
-
|
923
|
-
|
924
|
-
if (
|
1143
|
+
ts.resizableReset = function(table){
|
1144
|
+
table.config.$headers.filter(':not(.resizable-false)').css('width','');
|
1145
|
+
if (ts.storage) { ts.storage(table, 'tablesorter-resizable', {}); }
|
925
1146
|
};
|
926
1147
|
|
927
1148
|
// Save table sort widget
|
@@ -929,38 +1150,46 @@ $.tablesorter.resizableReset = function(table){
|
|
929
1150
|
// saveSort widget option is true AND the
|
930
1151
|
// $.tablesorter.storage function is included
|
931
1152
|
// **************************
|
932
|
-
|
1153
|
+
ts.addWidget({
|
933
1154
|
id: 'saveSort',
|
934
|
-
|
1155
|
+
priority: 20,
|
1156
|
+
options: {
|
1157
|
+
saveSort : true
|
1158
|
+
},
|
1159
|
+
init: function(table, thisWidget, c, wo){
|
935
1160
|
// run widget format before all other widgets are applied to the table
|
936
|
-
thisWidget.format(table, true);
|
1161
|
+
thisWidget.format(table, c, wo, true);
|
937
1162
|
},
|
938
|
-
format: function(table, init){
|
939
|
-
var sl, time,
|
940
|
-
|
1163
|
+
format: function(table, c, wo, init){
|
1164
|
+
var sl, time,
|
1165
|
+
$t = c.$table,
|
941
1166
|
ss = wo.saveSort !== false, // make saveSort active/inactive; default to true
|
942
1167
|
sortList = { "sortList" : c.sortList };
|
943
1168
|
if (c.debug){
|
944
1169
|
time = new Date();
|
945
1170
|
}
|
946
|
-
if ($
|
947
|
-
if (ss && table.hasInitialized &&
|
948
|
-
|
1171
|
+
if ($t.hasClass('hasSaveSort')){
|
1172
|
+
if (ss && table.hasInitialized && ts.storage){
|
1173
|
+
ts.storage( table, 'tablesorter-savesort', sortList );
|
949
1174
|
if (c.debug){
|
950
|
-
|
1175
|
+
ts.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time);
|
951
1176
|
}
|
952
1177
|
}
|
953
1178
|
} else {
|
954
1179
|
// set table sort on initial run of the widget
|
955
|
-
$
|
1180
|
+
$t.addClass('hasSaveSort');
|
956
1181
|
sortList = '';
|
957
1182
|
// get data
|
958
|
-
if (
|
959
|
-
sl =
|
1183
|
+
if (ts.storage){
|
1184
|
+
sl = ts.storage( table, 'tablesorter-savesort' );
|
960
1185
|
sortList = (sl && sl.hasOwnProperty('sortList') && $.isArray(sl.sortList)) ? sl.sortList : '';
|
961
1186
|
if (c.debug){
|
962
|
-
|
1187
|
+
ts.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time);
|
963
1188
|
}
|
1189
|
+
$t.bind('saveSortReset', function(e){
|
1190
|
+
e.stopPropagation();
|
1191
|
+
ts.storage( table, 'tablesorter-savesort', '' );
|
1192
|
+
});
|
964
1193
|
}
|
965
1194
|
// init is true when widget init is run, this will run this widget before all other widgets have initialized
|
966
1195
|
// this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice.
|
@@ -968,13 +1197,13 @@ $.tablesorter.addWidget({
|
|
968
1197
|
c.sortList = sortList;
|
969
1198
|
} else if (table.hasInitialized && sortList && sortList.length > 0){
|
970
1199
|
// update sort change
|
971
|
-
$
|
1200
|
+
$t.trigger('sorton', [sortList]);
|
972
1201
|
}
|
973
1202
|
}
|
974
1203
|
},
|
975
|
-
remove: function(table
|
1204
|
+
remove: function(table){
|
976
1205
|
// clear storage
|
977
|
-
if (
|
1206
|
+
if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); }
|
978
1207
|
}
|
979
1208
|
});
|
980
1209
|
|