/*! tablesorter Grouping widget - updated 10/18/2013 * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ "use strict"; var ts = $.tablesorter; ts.grouping = { number : function(c, $col, txt, num, group){ var t, w; if (num > 1 && txt !== '') { if ($col.hasClass(ts.css.sortAsc)) { t = Math.floor(parseFloat(txt)/num) * num; return t > parseFloat(group || 0) ? t : parseFloat(group || 0); } else { t = Math.ceil(parseFloat(txt)/num) * num; return t < parseFloat(group || num) - t ? parseFloat(group || num) - t : t; } } else { w = (txt + '').match(/\d+/g); return w && w.length >= num ? w[num - 1] : txt || ''; } }, separator : function(c, $col, txt, num){ var wo = c.widgetOptions, w = (txt + '').split(wo.group_separator); return $.trim(w && num > 0 && w.length >= num ? w[(num || 1) - 1] : ''); }, word : function(c, $col, txt, num){ var w = (txt + ' ').match(/\w+/g); return w && w.length >= num ? w[num - 1] : txt || ''; }, letter : function(c, $col, txt, num){ return txt ? (txt + ' ').substring(0, num) : ''; }, date : function(c, $col, txt, part){ var wo = c.widgetOptions, t = new Date(txt || ''), t2 = t.getHours(); return part === 'year' ? t.getFullYear() : part === 'month' ? wo.group_months[t.getMonth()] : part === 'day' ? wo.group_months[t.getMonth()] + ' ' + t.getDate() : part === 'week' ? wo.group_week[t.getDay()] : part === 'time' ? ('00' + (t2 > 12 ? t2 - 12 : t2 === 0 ? t2 + 12 : t2)).slice(-2) + ':' + ('00' + t.getMinutes()).slice(-2) + ' ' + ('00' + wo.group_time[t2 >= 12 ? 1 : 0]).slice(-2) : t.toString(); } }; ts.addWidget({ id: 'group', // run AFTER the zebra widget, so the header rows do not get zebra striping priority: 100, options: { group_collapsible : true, // make the group header clickable and collapse the rows below it. group_collapsed : false, // start with all groups collapsed group_count : ' ({num})', // if not false, the "{num}" string is replaced with the number of rows in the group group_separator : '-', // group name separator; used when group-separator-# class is used. group_formatter : null, // function(txt, col, table, c, wo) { return txt; } group_callback : null, // function($cell, $rows, column, table){}, callback allowing modification of the group header labels group_complete : 'groupingComplete', // event triggered on the table when the grouping widget has finished work // change these default date names based on your language preferences group_months : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ], group_week : [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ], group_time : [ 'AM', 'PM' ] }, init: function(table, thisWidget, c, wo){ if (wo.group_collapsible) { // .on() requires jQuery 1.7+ c.$table.on('click toggleGroup', 'tr.group-header', function(e){ // use shift-click to toggle ALL groups if (e.type === 'click' && e.shiftKey) { $(this).siblings('.group-header').trigger('toggleGroup'); } $(this).toggleClass('collapsed'); // nextUntil requires jQuery 1.4+ $(this).nextUntil('tr.group-header').toggleClass('group-hidden', $(this).hasClass('collapsed') ); e.stopPropagation(); }); } }, format: function(table, c, wo) { var j, k, curr, $tr, t, t2, time, n, group = '', col = c.sortList[0] ? c.sortList[0][0] : -1; c.$table .find('tr.group-hidden').removeClass('group-hidden').end() .find('tr.group-header').remove(); if (wo.group_collapsible) { // clear pager saved spacer height (in case the rows are collapsed) $.data(table, 'pagerSavedHeight', 0); } if (col >= 0 && !c.$headers.eq(col).hasClass('group-false')) { if (c.debug){ time = new Date(); } for (k = 0; k < c.$tbodies.length; k++) { n = c.cache[k].normalized; group = ''; // clear grouping across tbodies $tr = c.$tbodies.eq(k).children('tr').not('.' + c.cssChildRow ); if (wo.group_collapsed && wo.group_collapsible) { $tr.addClass('group-hidden'); } for (j = 0; j < $tr.length; j++) { if ( $tr.eq(j).is(':visible') ) { t = (c.$headers.eq(col).attr('class') || '').match(/(group-\w+(-\w+)?)/g); // group-{type}-{number/date} t2 = t ? t[0].split('-') : ['','letter',1]; // default to letter 1 curr = n[j] ? ts.grouping[t2[1]]( c, c.$headers.eq(col), c.cache[k].normalized[j][col], /date/.test(t) ? t2[2] : parseInt(t2[2] || 1, 10) || 1, group ) : curr; if (group !== curr) { group = curr; // show range if number > 1 if (t2[1] === 'number' && t2[2] > 1 && curr !== '') { curr += ' - ' + (parseInt(curr, 10) + ((parseInt(t2[2],10) - 1) * (c.$headers.eq(col).hasClass(ts.css.sortAsc) ? 1 : -1))); } if ($.isFunction(wo.group_formatter)) { curr = wo.group_formatter((curr || '').toString(), col, table, c, wo) || curr; } $tr.eq(j).before('' + (wo.group_collapsible ? '' : '') + '' + curr + ''); } } } } $tr = c.$table.find('tr.group-header').bind('selectstart', false); if (wo.group_count || $.isFunction(wo.group_callback)) { $tr.each(function(){ var $rows, $row = $(this), $label = $row.find('.group-count'); if ($label.length) { $rows = $(this).nextUntil('tr.group-header').filter(':visible'); if (wo.group_count) { $label.html( wo.group_count.replace(/\{num\}/g, $rows.length) ); } if ($.isFunction(wo.group_callback)) { wo.group_callback($row.find('td'), $rows, col, table); } } }); } c.$table.trigger(wo.group_complete); if (c.debug) { $.tablesorter.benchmark("Applying groups widget: ", time); } } }, remove : function(table, c, wo){ c.$table .off('click', 'tr.group-header') .find('.group-hidden').removeClass('group-hidden').end() .find('tr.group-header').remove(); } }); })(jQuery);