/* A simple, lightweight jQuery plugin for creating sortable tables. https://github.com/kylefox/jquery-tablesort Version 0.0.1 */ ;(function($) { $.tablesort = function ($table, settings) { var self = this; this.$table = $table; this.settings = $.extend({}, $.tablesort.defaults, settings); this.$table.find('thead th').bind('click.tablesort', function() { if( !$(this).hasClass('disabled') ) { self.sort($(this)); } }); this.index = null; this.$th = null; this.direction = []; }; $.tablesort.prototype = { sort: function(th, direction) { var start = new Date(), self = this, table = this.$table, rows = table.find('tbody tr'), index = th.index(), cache = [], fragment = $('
'), sortValueForCell = function(th, td, sorter) { var sortBy ; if(th.data().sortBy) { sortBy = th.data().sortBy; return (typeof sortBy === 'function') ? sortBy(th, td, sorter) : sortBy ; } return ( td.data('sort') ) ? td.data('sort') : td.text() ; }, naturalSort = function naturalSort (a, b) { var chunkRegExp = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi, stripRegExp = /(^[ ]*|[ ]*$)/g, dateRegExp = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, numericRegExp = /^0x[0-9a-f]+$/i, oRegExp = /^0/, cLoc = 0, useInsensitive = function(string) { return ('' + string).toLowerCase().replace(',', ''); }, // convert all to strings strip whitespace x = useInsensitive(a).replace(stripRegExp, '') || '', y = useInsensitive(b).replace(stripRegExp, '') || '', // chunk/tokenize xChunked = x.replace(chunkRegExp, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), yChunked = y.replace(chunkRegExp, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), chunkLength = Math.max(xChunked.length, yChunked.length), // numeric, hex or date detection xDate = parseInt(x.match(numericRegExp), 10) || (xChunked.length != 1 && x.match(dateRegExp) && Date.parse(x)), yDate = parseInt(y.match(numericRegExp), 10) || xDate && y.match(dateRegExp) && Date.parse(y) || null, xHexValue, yHexValue, index ; // first try and sort Hex codes or Dates if (yDate) { if( xDate < yDate ) { return -1; } else if ( xDate > yDate ) { return 1; } } // natural sorting through split numeric strings and default strings for(index = 0; index < chunkLength; index++) { // find floats not starting with '0', string or 0 if not defined (Clint Priest) xHexValue = !(xChunked[index] || '').match(oRegExp) && parseFloat(xChunked[index]) || xChunked[index] || 0; yHexValue = !(yChunked[index] || '').match(oRegExp) && parseFloat(yChunked[index]) || yChunked[index] || 0; // handle numeric vs string comparison - number < string - (Kyle Adams) if (isNaN(xHexValue) !== isNaN(yHexValue)) { return ( isNaN(xHexValue) ) ? 1 : -1 ; } // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' else if (typeof xHexValue !== typeof yHexValue) { xHexValue += ''; yHexValue += ''; } if (xHexValue < yHexValue) { return -1; } if (xHexValue > yHexValue) { return 1; } } return 0; } ; if(rows.length === 0) { return; } self.$table.find('thead th').removeClass(self.settings.asc + ' ' + self.settings.desc); this.$th = th; if(this.index != index) { this.direction[index] = 'desc'; } else if(direction !== 'asc' && direction !== 'desc') { this.direction[index] = this.direction[index] === 'desc' ? 'asc' : 'desc'; } else { this.direction[index] = direction; } this.index = index; direction = this.direction[index] == 'asc' ? 1 : -1; self.$table.trigger('tablesort:start', [self]); self.log("Sorting by " + this.index + ' ' + this.direction[index]); rows.sort(function(a, b) { var aRow = $(a); var bRow = $(b); var aIndex = aRow.index(); var bIndex = bRow.index(); // Sort value A if(cache[aIndex]) { a = cache[aIndex]; } else { a = sortValueForCell(th, self.cellToSort(a), self); cache[aIndex] = a; } // Sort Value B if(cache[bIndex]) { b = cache[bIndex]; } else { b = sortValueForCell(th, self.cellToSort(b), self); cache[bIndex]= b; } return (naturalSort(a, b) * direction); }); rows.each(function(i, tr) { fragment.append(tr); }); table.append(fragment.html()); th.addClass(self.settings[self.direction[index]]); self.log('Sort finished in ' + ((new Date()).getTime() - start.getTime()) + 'ms'); self.$table.trigger('tablesort:complete', [self]); }, cellToSort: function(row) { return $($(row).find('td').get(this.index)); }, log: function(msg) { if(($.tablesort.DEBUG || this.settings.debug) && console && console.log) { console.log('[tablesort] ' + msg); } }, destroy: function() { this.$table.find('thead th').unbind('click.tablesort'); this.$table.data('tablesort', null); return null; } }; $.tablesort.DEBUG = false; $.tablesort.defaults = { debug: $.tablesort.DEBUG, asc: 'sorted ascending', desc: 'sorted descending' }; $.fn.tablesort = function(settings) { var table, sortable, previous; return this.each(function() { table = $(this); previous = table.data('tablesort'); if(previous) { previous.destroy(); } table.data('tablesort', new $.tablesort(table, settings)); }); }; })(jQuery);