You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
6.6 KiB

  1. /*
  2. A simple, lightweight jQuery plugin for creating sortable tables.
  3. https://github.com/kylefox/jquery-tablesort
  4. Version 0.0.1
  5. */
  6. ;(function($) {
  7. $.tablesort = function ($table, settings) {
  8. var self = this;
  9. this.$table = $table;
  10. this.settings = $.extend({}, $.tablesort.defaults, settings);
  11. this.$table.find('thead th').bind('click.tablesort', function() {
  12. if( !$(this).hasClass('disabled') ) {
  13. self.sort($(this));
  14. }
  15. });
  16. this.index = null;
  17. this.$th = null;
  18. this.direction = [];
  19. };
  20. $.tablesort.prototype = {
  21. sort: function(th, direction) {
  22. var start = new Date(),
  23. self = this,
  24. table = this.$table,
  25. rows = table.find('tbody tr'),
  26. index = th.index(),
  27. cache = [],
  28. fragment = $('<div/>'),
  29. sortValueForCell = function(th, td, sorter) {
  30. var
  31. sortBy
  32. ;
  33. if(th.data().sortBy) {
  34. sortBy = th.data().sortBy;
  35. return (typeof sortBy === 'function')
  36. ? sortBy(th, td, sorter)
  37. : sortBy
  38. ;
  39. }
  40. return ( td.data('sort') )
  41. ? td.data('sort')
  42. : td.text()
  43. ;
  44. },
  45. naturalSort = function naturalSort (a, b) {
  46. var
  47. chunkRegExp = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
  48. stripRegExp = /(^[ ]*|[ ]*$)/g,
  49. dateRegExp = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
  50. numericRegExp = /^0x[0-9a-f]+$/i,
  51. oRegExp = /^0/,
  52. cLoc = 0,
  53. useInsensitive = function(string) {
  54. return ('' + string).toLowerCase().replace(',', '');
  55. },
  56. // convert all to strings strip whitespace
  57. x = useInsensitive(a).replace(stripRegExp, '') || '',
  58. y = useInsensitive(b).replace(stripRegExp, '') || '',
  59. // chunk/tokenize
  60. xChunked = x.replace(chunkRegExp, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  61. yChunked = y.replace(chunkRegExp, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  62. chunkLength = Math.max(xChunked.length, yChunked.length),
  63. // numeric, hex or date detection
  64. xDate = parseInt(x.match(numericRegExp), 10) || (xChunked.length != 1 && x.match(dateRegExp) && Date.parse(x)),
  65. yDate = parseInt(y.match(numericRegExp), 10) || xDate && y.match(dateRegExp) && Date.parse(y) || null,
  66. xHexValue,
  67. yHexValue,
  68. index
  69. ;
  70. // first try and sort Hex codes or Dates
  71. if (yDate) {
  72. if( xDate < yDate ) {
  73. return -1;
  74. }
  75. else if ( xDate > yDate ) {
  76. return 1;
  77. }
  78. }
  79. // natural sorting through split numeric strings and default strings
  80. for(index = 0; index < chunkLength; index++) {
  81. // find floats not starting with '0', string or 0 if not defined (Clint Priest)
  82. xHexValue = !(xChunked[index] || '').match(oRegExp) && parseFloat(xChunked[index]) || xChunked[index] || 0;
  83. yHexValue = !(yChunked[index] || '').match(oRegExp) && parseFloat(yChunked[index]) || yChunked[index] || 0;
  84. // handle numeric vs string comparison - number < string - (Kyle Adams)
  85. if (isNaN(xHexValue) !== isNaN(yHexValue)) {
  86. return ( isNaN(xHexValue) )
  87. ? 1
  88. : -1
  89. ;
  90. }
  91. // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
  92. else if (typeof xHexValue !== typeof yHexValue) {
  93. xHexValue += '';
  94. yHexValue += '';
  95. }
  96. if (xHexValue < yHexValue) {
  97. return -1;
  98. }
  99. if (xHexValue > yHexValue) {
  100. return 1;
  101. }
  102. }
  103. return 0;
  104. }
  105. ;
  106. if(rows.length === 0) {
  107. return;
  108. }
  109. self.$table.find('thead th').removeClass(self.settings.asc + ' ' + self.settings.desc);
  110. this.$th = th;
  111. if(this.index != index) {
  112. this.direction[index] = 'desc';
  113. }
  114. else if(direction !== 'asc' && direction !== 'desc') {
  115. this.direction[index] = this.direction[index] === 'desc' ? 'asc' : 'desc';
  116. }
  117. else {
  118. this.direction[index] = direction;
  119. }
  120. this.index = index;
  121. direction = this.direction[index] == 'asc' ? 1 : -1;
  122. self.$table.trigger('tablesort:start', [self]);
  123. self.log("Sorting by " + this.index + ' ' + this.direction[index]);
  124. rows.sort(function(a, b) {
  125. var aRow = $(a);
  126. var bRow = $(b);
  127. var aIndex = aRow.index();
  128. var bIndex = bRow.index();
  129. // Sort value A
  130. if(cache[aIndex]) {
  131. a = cache[aIndex];
  132. }
  133. else {
  134. a = sortValueForCell(th, self.cellToSort(a), self);
  135. cache[aIndex] = a;
  136. }
  137. // Sort Value B
  138. if(cache[bIndex]) {
  139. b = cache[bIndex];
  140. }
  141. else {
  142. b = sortValueForCell(th, self.cellToSort(b), self);
  143. cache[bIndex]= b;
  144. }
  145. return (naturalSort(a, b) * direction);
  146. });
  147. rows.each(function(i, tr) {
  148. fragment.append(tr);
  149. });
  150. table.append(fragment.html());
  151. th.addClass(self.settings[self.direction[index]]);
  152. self.log('Sort finished in ' + ((new Date()).getTime() - start.getTime()) + 'ms');
  153. self.$table.trigger('tablesort:complete', [self]);
  154. },
  155. cellToSort: function(row) {
  156. return $($(row).find('td').get(this.index));
  157. },
  158. log: function(msg) {
  159. if(($.tablesort.DEBUG || this.settings.debug) && console && console.log) {
  160. console.log('[tablesort] ' + msg);
  161. }
  162. },
  163. destroy: function() {
  164. this.$table.find('thead th').unbind('click.tablesort');
  165. this.$table.data('tablesort', null);
  166. return null;
  167. }
  168. };
  169. $.tablesort.DEBUG = false;
  170. $.tablesort.defaults = {
  171. debug: $.tablesort.DEBUG,
  172. asc: 'sorted ascending',
  173. desc: 'sorted descending'
  174. };
  175. $.fn.tablesort = function(settings) {
  176. var table, sortable, previous;
  177. return this.each(function() {
  178. table = $(this);
  179. previous = table.data('tablesort');
  180. if(previous) {
  181. previous.destroy();
  182. }
  183. table.data('tablesort', new $.tablesort(table, settings));
  184. });
  185. };
  186. })(jQuery);