jquery-tablesorter 0.0.5 → 1.0.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.
@@ -0,0 +1,450 @@
1
+ /*! tableSorter 2.3 widgets - updated 5/19/2012
2
+ *
3
+ * jQuery UI Theme
4
+ * Column Styles
5
+ * Column Filters (not compatible with tablesorter v2.0.5)
6
+ * Sticky Header
7
+ * Column Resizing
8
+ * Save Sort
9
+ *
10
+ */
11
+ ;(function($){
12
+
13
+ // *** Store data in local storage, with a cookie fallback ***
14
+ /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json)
15
+ if you need it, then include https://github.com/douglascrockford/JSON-js
16
+
17
+ $.parseJSON is not available is jQuery versions older than 1.4.1, using older
18
+ versions will only allow storing information for one page at a time
19
+
20
+ // *** Save data (JSON format only) ***
21
+ // val must be valid JSON... use http://jsonlint.com/ to ensure it is valid
22
+ var val = { "mywidget" : "data1" }; // valid JSON uses double quotes
23
+ // $.tablesorter.storage(table, key, val);
24
+ $.tablesorter.storage(table, 'tablesorter-mywidget', val);
25
+
26
+ // *** Get data: $.tablesorter.storage(table, key); ***
27
+ v = $.tablesorter.storage(table, 'tablesorter-mywidget');
28
+ // val may be empty, so also check for your data
29
+ val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : '';
30
+ alert(val); // "data1" if saved, or "" if not
31
+ */
32
+ $.tablesorter.storage = function(table, key, val){
33
+ var d, k, ls = false, v = {},
34
+ id = table.id || $('.tablesorter').index( $(table) ),
35
+ url = window.location.pathname;
36
+ try { ls = !!(localStorage.getItem); } catch(e) {}
37
+ // *** get val ***
38
+ if ($.parseJSON) {
39
+ if (ls) {
40
+ v = $.parseJSON(localStorage[key]) || {};
41
+ } else {
42
+ k = document.cookie.split(/[;\s|=]/); // cookie
43
+ d = $.inArray(key, k) + 1; // add one to get from the key to the value
44
+ v = (d !== 0) ? $.parseJSON(k[d]) || {} : {};
45
+ }
46
+ }
47
+ if (val && JSON && JSON.hasOwnProperty('stringify')) {
48
+ // add unique identifiers = url pathname > table ID/index on page > data
49
+ if (v[url] && v[url][id]) {
50
+ v[url][id] = val;
51
+ } else {
52
+ if (v[url]) {
53
+ v[url][id] = val;
54
+ } else {
55
+ v[url] = {};
56
+ v[url][id] = val;
57
+ }
58
+ }
59
+ // *** set val ***
60
+ if (ls) {
61
+ localStorage[key] = JSON.stringify(v);
62
+ } else {
63
+ d = new Date();
64
+ d.setTime(d.getTime()+(31536e+6)); // 365 days
65
+ document.cookie = key + '=' + (JSON.stringify(v)).replace(/\"/g,'\"') + '; expires=' + d.toGMTString() + '; path=/';
66
+ }
67
+ } else {
68
+ return ( v && v.hasOwnProperty(url) && v[url].hasOwnProperty(id) ) ? v[url][id] : {};
69
+ }
70
+ };
71
+
72
+ // Widget: jQuery UI theme
73
+ // "uitheme" option in "widgetOptions"
74
+ // **************************
75
+ $.tablesorter.addWidget({
76
+ id: "uitheme",
77
+ format: function(table) {
78
+ var time, klass, rmv, $t, t, $table = $(table),
79
+ c = table.config, wo = c.widgetOptions,
80
+ // ["up/down arrow (cssHeaders, unsorted)", "down arrow (cssDesc, descending)", "up arrow (cssAsc, ascending)" ]
81
+ icons = ["ui-icon-arrowthick-2-n-s", "ui-icon-arrowthick-1-s", "ui-icon-arrowthick-1-n"];
82
+ // keep backwards compatibility, for now
83
+ icons = (c.widgetUitheme && c.widgetUitheme.hasOwnProperty('css')) ? c.widgetUitheme.css || icons :
84
+ (wo && wo.hasOwnProperty('uitheme')) ? wo.uitheme : icons;
85
+ rmv = icons.join(' ');
86
+ if (c.debug) {
87
+ time = new Date();
88
+ }
89
+ if (!$table.hasClass('ui-theme')) {
90
+ $table.addClass('ui-widget ui-widget-content ui-corner-all ui-theme');
91
+ $.each(c.headerList, function(){
92
+ $(this)
93
+ // using "ui-theme" class in case the user adds their own ui-icon using onRenderHeader
94
+ .addClass('ui-widget-header ui-corner-all ui-state-default')
95
+ .append('<span class="ui-icon"/>')
96
+ .wrapInner('<div class="tablesorter-inner"/>')
97
+ .hover(function(){
98
+ $(this).addClass('ui-state-hover');
99
+ }, function(){
100
+ $(this).removeClass('ui-state-hover');
101
+ });
102
+ });
103
+ }
104
+ $.each(c.headerList, function(i){
105
+ $t = $(this);
106
+ if (this.sortDisabled) {
107
+ // no sort arrows for disabled columns!
108
+ $t.find('span.ui-icon').removeClass(rmv + ' ui-icon');
109
+ } else {
110
+ klass = ($t.hasClass(c.cssAsc)) ? icons[1] : ($t.hasClass(c.cssDesc)) ? icons[2] : $t.hasClass(c.cssHeader) ? icons[0] : '';
111
+ t = ($table.hasClass('hasStickyHeaders')) ? $table.find('tr.' + (wo.stickyHeaders || 'tablesorter-stickyHeader')).find('th').eq(i).add($t) : $t;
112
+ t[klass === icons[0] ? 'removeClass' : 'addClass']('ui-state-active')
113
+ .find('span.ui-icon').removeClass(rmv).addClass(klass);
114
+ }
115
+ });
116
+ if (c.debug) {
117
+ $.tablesorter.benchmark("Applying uitheme widget", time);
118
+ }
119
+ }
120
+ });
121
+
122
+ // Widget: Column styles
123
+ // "columns" option in "widgetOptions"
124
+ // **************************
125
+ $.tablesorter.addWidget({
126
+ id: "columns",
127
+ format: function(table) {
128
+ var $tb, $tr, $td, $f, time, last, rmv, i, j, k, l,
129
+ c = table.config,
130
+ b = $(table).children('tbody:not(.' + c.cssInfoBlock + ')'),
131
+ list = c.sortList,
132
+ len = list.length,
133
+ css = [ "primary", "secondary", "tertiary" ]; // default options
134
+ // keep backwards compatibility, for now
135
+ css = (c.widgetColumns && c.widgetColumns.hasOwnProperty('css')) ? c.widgetColumns.css || css :
136
+ (c.widgetOptions && c.widgetOptions.hasOwnProperty('columns')) ? c.widgetOptions.columns || css : css;
137
+ last = css.length-1;
138
+ rmv = css.join(' ');
139
+ if (c.debug) {
140
+ time = new Date();
141
+ }
142
+ // check if there is a sort (on initialization there may not be one)
143
+ if (list && list[0]) {
144
+ for (k = 0; k < b.length; k++ ) {
145
+ $tb = $(b[k]);
146
+ $f = $(document.createDocumentFragment());
147
+ $tr = $tb.children('tr').appendTo($f);
148
+ l = $tr.length;
149
+ // loop through the visible rows
150
+ for (j = 0; j < l; j++) {
151
+ $td = $tr.eq(j).children().removeClass(rmv);
152
+ // primary sort column class
153
+ $td.eq(list[0][0]).addClass(css[0]);
154
+ if (len > 1) {
155
+ for (i = 1; i < len; i++){
156
+ // secondary, tertiary, etc sort column classes
157
+ $td.eq(list[i][0]).addClass( css[i] || css[last] );
158
+ }
159
+ }
160
+ }
161
+ $tb.append($tr);
162
+ }
163
+ } else {
164
+ // remove all column classes if sort is cleared (sortReset)
165
+ // $(table).find('td').removeClass(rmv); slow
166
+ $td = $(table).find('td');
167
+ len = $td.length;
168
+ rmv = new RegExp('(' + css.join('|') + ')');
169
+ for (i = 0; i < len; i++){
170
+ $td[i].className = $td[i].className.replace(rmv, '');
171
+ }
172
+ }
173
+ if (c.debug) {
174
+ $.tablesorter.benchmark("Applying Columns widget", time);
175
+ }
176
+ }
177
+ });
178
+
179
+ // Widget: Filter
180
+ // "filter_startsWith" & "filter_childRows" options in "widgetOptions"
181
+ // **************************
182
+ $.tablesorter.addWidget({
183
+ id: "filter",
184
+ format: function(table) {
185
+ if (!$(table).hasClass('hasFilters')) {
186
+ var i, v, r, t, x, cr, $td, c = table.config,
187
+ wo = c.widgetOptions,
188
+ css = wo.filter_cssFilter || 'tablesorter-filter',
189
+ $t = $(table).addClass('hasFilters'),
190
+ cols = c.parsers.length,
191
+ fr = '<tr class="' + css + '">',
192
+ time;
193
+ if (c.debug) {
194
+ time = new Date();
195
+ }
196
+ for (i=0; i < cols; i++){
197
+ fr += '<td><input type="search" data-col="' + i + '" class="' + css;
198
+ // use header option - headers: { 1: { filter: false } } OR add class="filter-false"
199
+ if ($.tablesorter.getData) {
200
+ // get data from jQuery data, metadata, headers option or header class name
201
+ fr += $.tablesorter.getData(c.headerList[i], c.headers[i], 'filter') === 'false' ? ' disabled" disabled' : '"';
202
+ } else {
203
+ // only class names and header options - keep this for compatibility with tablesorter v2.0.5
204
+ fr += ((c.headers[i] && c.headers[i].hasOwnProperty('filter') && c.headers[i].filter === false) || $(c.headerList[i]).is('.filter-false') ) ? ' disabled" disabled' : '"';
205
+ }
206
+ fr += '></td>';
207
+ }
208
+ $t
209
+ .find('thead').eq(0).append(fr += '</tr>')
210
+ .find('input.' + css).bind('keyup search', function(e){
211
+ v = $t.find('thead').eq(0).children('tr').find('input.' + css).map(function(){ return ($(this).val() || '').toLowerCase(); }).get();
212
+ if (v.join('') === '') {
213
+ $t.find('tr').show();
214
+ } else {
215
+ // *** to do *** optimize this to work better with very large tables
216
+ $t.children('tbody:not(.' + c.cssInfoBlock + ')').children('tr:not(.' + c.cssChildRow + ')').each(function(){
217
+ r = true;
218
+ cr = $(this).nextUntil('tr:not(.' + c.cssChildRow + ')');
219
+ // so, if "table.config.widgetOptions.filter_childRows" is true and there is
220
+ // a match anywhere in the child row, then it will make the row visible
221
+ // checked here so the option can be changed dynamically
222
+ t = (cr.length && (wo && wo.hasOwnProperty('filter_childRows') &&
223
+ typeof wo.filter_childRows !== 'undefined' ? wo.filter_childRows : true)) ? cr.text() : '';
224
+ $td = $(this).children('td');
225
+ for (i=0; i < cols; i++){
226
+ x = $.trim(($td.eq(i).text() + t)).toLowerCase().indexOf(v[i]);
227
+ if (v[i] !== '' && ( (!wo.filter_startsWith && x >= 0) || (wo.filter_startsWith && x === 0) ) ) {
228
+ r = (r) ? true : false;
229
+ } else if (v[i] !== '') {
230
+ r = false;
231
+ }
232
+ }
233
+ $(this)[r ? 'show' : 'hide']();
234
+ if (cr.length) { cr[r ? 'show' : 'hide'](); }
235
+ });
236
+ }
237
+ $t.trigger('applyWidgets'); // make sure zebra widget is applied
238
+ });
239
+ if (c.debug) {
240
+ $.tablesorter.benchmark("Applying Filter widget", time);
241
+ }
242
+ }
243
+ }
244
+ });
245
+
246
+ // Widget: Sticky headers
247
+ // based on this awesome article:
248
+ // http://css-tricks.com/13465-persistent-headers/
249
+ // **************************
250
+ $.tablesorter.addWidget({
251
+ id: "stickyHeaders",
252
+ format: function(table) {
253
+ if ($(table).hasClass('hasStickyHeaders')) { return; }
254
+ var $table = $(table).addClass('hasStickyHeaders'),
255
+ wo = table.config.widgetOptions,
256
+ win = $(window),
257
+ header = $(table).children('thead'),
258
+ hdrCells = header.children('tr:not(.sticky-false)').children(),
259
+ css = wo.stickyHeaders || 'tablesorter-stickyHeader',
260
+ innr = '.tablesorter-header-inner',
261
+ firstCell = hdrCells.eq(0),
262
+ tfoot = $table.find('tfoot'),
263
+ sticky = header.find('tr.tablesorter-header:not(.sticky-false)').clone()
264
+ .removeClass('tablesorter-header')
265
+ .addClass(css)
266
+ .css({
267
+ width : header.outerWidth(true),
268
+ position : 'fixed',
269
+ left : firstCell.offset().left,
270
+ margin : 0,
271
+ top : 0,
272
+ visibility : 'hidden',
273
+ zIndex : 10
274
+ }),
275
+ stkyCells = sticky.children(),
276
+ laststate = '';
277
+ // update sticky header class names to match real header after sorting
278
+ $table.bind('sortEnd', function(e,t){
279
+ var th = $(t).find('thead tr'),
280
+ sh = th.filter('.' + css).children();
281
+ th.filter(':not(.' + css + ')').children().each(function(i){
282
+ sh.eq(i).attr('class', $(this).attr('class'));
283
+ });
284
+ }).bind('pagerComplete', function(){
285
+ win.resize(); // trigger window resize to make sure column widths & position are correct
286
+ });
287
+ // set sticky header cell width and link clicks to real header
288
+ hdrCells.each(function(i){
289
+ var t = $(this),
290
+ s = stkyCells.eq(i)
291
+ // clicking on sticky will trigger sort
292
+ .bind('click', function(e){
293
+ t.trigger(e);
294
+ })
295
+ // prevent sticky header text selection
296
+ .bind('mousedown', function(){
297
+ this.onselectstart = function(){ return false; };
298
+ return false;
299
+ })
300
+ // set cell widths
301
+ .find(innr).width( t.find(innr).width() );
302
+ });
303
+ header.prepend( sticky );
304
+ // make it sticky!
305
+ win
306
+ .scroll(function(){
307
+ var offset = firstCell.offset(),
308
+ sTop = win.scrollTop(),
309
+ tableHt = $table.height() - (firstCell.height() + (tfoot.height() || 0)),
310
+ vis = (sTop > offset.top) && (sTop < offset.top + tableHt) ? 'visible' : 'hidden';
311
+ sticky.css({
312
+ left : offset.left - win.scrollLeft(),
313
+ visibility : vis
314
+ });
315
+ if (vis !== laststate) {
316
+ // trigger resize to make sure the column widths match
317
+ win.resize();
318
+ laststate = vis;
319
+ }
320
+ })
321
+ .resize(function(){
322
+ var ht = 0;
323
+ sticky.css({
324
+ left : firstCell.offset().left - win.scrollLeft(),
325
+ width: header.outerWidth()
326
+ }).each(function(i){
327
+ $(this).css('top', ht);
328
+ ht += header.find('tr').eq(i).outerHeight();
329
+ });
330
+ stkyCells.find(innr).each(function(i){
331
+ $(this).width( hdrCells.eq(i).find(innr).width() );
332
+ });
333
+ });
334
+ }
335
+ });
336
+
337
+ // Add Column resizing widget
338
+ // this widget saves the column widths if
339
+ // $.tablesorter.storage function is included
340
+ // **************************
341
+ $.tablesorter.addWidget({
342
+ id: "resizable",
343
+ format: function(table) {
344
+ if ($(table).hasClass('hasResizable')) { return; }
345
+ $(table).addClass('hasResizable');
346
+ var i, j, w, s, c = table.config,
347
+ $cols = $(c.headerList).filter(':gt(0)'),
348
+ position = 0,
349
+ $target = null,
350
+ $prev = null,
351
+ len = $cols.length,
352
+ stopResize = function(){
353
+ position = 0;
354
+ $target = $prev = null;
355
+ $(window).trigger('resize'); // will update stickyHeaders, just in case
356
+ };
357
+ s = ($.tablesorter.storage) ? $.tablesorter.storage(table, 'tablesorter-resizable') : '';
358
+ // process only if table ID or url match
359
+ if (s) {
360
+ for (j in s) {
361
+ if (!isNaN(j) && j < c.headerList.length) {
362
+ $(c.headerList[j]).width(s[j]); // set saved resizable widths
363
+ }
364
+ }
365
+ }
366
+ $cols
367
+ .each(function(){
368
+ $(this)
369
+ .append('<div class="tablesorter-resizer" style="cursor:w-resize;position:absolute;height:100%;width:20px;left:-20px;top:0;z-index:1;"></div>')
370
+ .wrapInner('<div style="position:relative;height:100%;width:100%"></div>');
371
+ })
372
+ .bind('mousemove', function(e){
373
+ // ignore mousemove if no mousedown
374
+ if (position === 0 || !$target) { return; }
375
+ var w = e.pageX - position,
376
+ n = $prev;
377
+ // make sure
378
+ if ( $target.width() < -w || ( $prev && $prev.width() <= w )) { return; }
379
+ // resize current column
380
+ $prev.width( $prev.width() + w );
381
+ position = e.pageX;
382
+ })
383
+ .bind('mouseup', function(){
384
+ if (s && $.tablesorter.storage && $target) {
385
+ s[$prev.index()] = $prev.width();
386
+ $.tablesorter.storage(table, 'tablesorter-resizable', s);
387
+ }
388
+ stopResize();
389
+ return false;
390
+ })
391
+ .find('.tablesorter-resizer')
392
+ .bind('mousedown', function(e){
393
+ // save header cell and mouse position
394
+ $target = $(e.target).closest('th');
395
+ $prev = $target.prev();
396
+ position = e.pageX;
397
+ });
398
+ $(table).find('thead').bind('mouseup mouseleave', function(){
399
+ stopResize();
400
+ });
401
+ }
402
+ });
403
+
404
+ // Save table sort widget
405
+ // this widget saves the last sort only if the
406
+ // $.tablesorter.storage function is included
407
+ // **************************
408
+ $.tablesorter.addWidget({
409
+ id: 'saveSort',
410
+ init: function(table, allWidgets, thisWidget){
411
+ // run widget format before all other widgets are applied to the table
412
+ thisWidget.format(table, true);
413
+ },
414
+ format: function(table, init) {
415
+ var n, d, k, sl, time, c = table.config, sortList = { "sortList" : c.sortList };
416
+ if (c.debug) {
417
+ time = new Date();
418
+ }
419
+ if ($(table).hasClass('hasSaveSort')) {
420
+ if (table.hasInitialized && $.tablesorter.storage) {
421
+ $.tablesorter.storage( table, 'tablesorter-savesort', sortList );
422
+ if (c.debug) {
423
+ $.tablesorter.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time);
424
+ }
425
+ }
426
+ } else {
427
+ // set table sort on initial run of the widget
428
+ $(table).addClass('hasSaveSort');
429
+ sortList = '';
430
+ // get data
431
+ if ($.tablesorter.storage) {
432
+ sl = $.tablesorter.storage( table, 'tablesorter-savesort' );
433
+ sortList = (sl && sl.hasOwnProperty('sortList') && $.isArray(sl.sortList)) ? sl.sortList : '';
434
+ if (c.debug) {
435
+ $.tablesorter.benchmark('saveSort: Last sort loaded: ' + sortList, time);
436
+ }
437
+ }
438
+ // init is true when widget init is run, this will run this widget before all other widgets have initialized
439
+ // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice.
440
+ if (init && sortList && sortList.length > 0) {
441
+ c.sortList = sortList;
442
+ } else if (table.hasInitialized && sortList && sortList.length > 0) {
443
+ // update sort change
444
+ $(table).trigger('sorton', [sortList]);
445
+ }
446
+ }
447
+ }
448
+ });
449
+
450
+ })(jQuery);
@@ -0,0 +1,3 @@
1
+ /*
2
+ * = require_tree ./blue
3
+ */