bootstrap-table-multiple-sort.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /**
  2. * @author Nadim Basalamah <dimbslmh@gmail.com>
  3. * @version: v1.0.0
  4. * https://github.com/dimbslmh/bootstrap-table/tree/master/src/extensions/multiple-sort/bootstrap-table-multiple-sort.js
  5. */
  6. (function($) {
  7. 'use strict';
  8. var isSingleSort = false;
  9. var sort_order = {
  10. asc: 'Ascending',
  11. desc: 'Descending'
  12. },
  13. arrowAsc = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ' +
  14. '0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBd' +
  15. 'qEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVo' +
  16. 'AADeemwtPcZI2wAAAABJRU5ErkJggg==',
  17. arrowDesc = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWj' +
  18. 'YBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJ' +
  19. 'zcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ';
  20. var showSortModal = function(that) {
  21. if (!$("#sortModal").hasClass("modal")) {
  22. var sModal = ' <div class="modal fade" id="sortModal" tabindex="-1" role="dialog" aria-labelledby="sortModalLabel" aria-hidden="true">';
  23. sModal += ' <div class="modal-dialog">';
  24. sModal += ' <div class="modal-content">';
  25. sModal += ' <div class="modal-header">';
  26. sModal += ' <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>';
  27. sModal += ' <h4 class="modal-title" id="sortModalLabel">' + that.options.formatMultipleSort() + '</h4>';
  28. sModal += ' </div>';
  29. sModal += ' <div class="modal-body">';
  30. sModal += ' <div class="bootstrap-table">';
  31. sModal += ' <div class="fixed-table-toolbar">';
  32. sModal += ' <div class="bars">';
  33. sModal += ' <div id="toolbar">';
  34. sModal += ' <button id="add" type="button" class="btn btn-default"><i class="' + that.options.iconsPrefix + ' ' + that.options.icons.plus + '"></i> ' + that.options.formatAddLevel() + '</button>';
  35. sModal += ' <button id="delete" type="button" class="btn btn-default" disabled><i class="' + that.options.iconsPrefix + ' ' + that.options.icons.minus + '"></i> ' + that.options.formatDeleteLevel() + '</button>';
  36. sModal += ' </div>';
  37. sModal += ' </div>';
  38. sModal += ' </div>';
  39. sModal += ' <div class="fixed-table-container">';
  40. sModal += ' <table id="multi-sort" class="table">';
  41. sModal += ' <thead>';
  42. sModal += ' <tr>';
  43. sModal += ' <th></th>';
  44. sModal += ' <th><div class="th-inner">' + that.options.formatColumn() + '</div></th>';
  45. sModal += ' <th><div class="th-inner">' + that.options.formatOrder() + '</div></th>';
  46. sModal += ' </tr>';
  47. sModal += ' </thead>';
  48. sModal += ' <tbody></tbody>';
  49. sModal += ' </table>';
  50. sModal += ' </div>';
  51. sModal += ' </div>';
  52. sModal += ' </div>';
  53. sModal += ' <div class="modal-footer">';
  54. sModal += ' <button type="button" class="btn btn-default" data-dismiss="modal">' + that.options.formatCancel() + '</button>';
  55. sModal += ' <button type="button" class="btn btn-primary">' + that.options.formatSort() + '</button>';
  56. sModal += ' </div>';
  57. sModal += ' </div>';
  58. sModal += ' </div>';
  59. sModal += ' </div>';
  60. $("body").append($(sModal));
  61. var $sortModal = $('#sortModal'),
  62. $rows = $sortModal.find("tbody > tr");
  63. $sortModal.off('click', '#add').on('click', '#add', function() {
  64. var total = $sortModal.find('.multi-sort-name:first option').length,
  65. current = $sortModal.find('tbody tr').length;
  66. if (current < total) {
  67. current++;
  68. that.addLevel();
  69. that.setButtonStates();
  70. }
  71. });
  72. $sortModal.off('click', '#delete').on('click', '#delete', function() {
  73. var total = $sortModal.find('.multi-sort-name:first option').length,
  74. current = $sortModal.find('tbody tr').length;
  75. if (current > 1 && current <= total) {
  76. current--;
  77. $sortModal.find('tbody tr:last').remove();
  78. that.setButtonStates();
  79. }
  80. });
  81. $sortModal.off('click', '.btn-primary').on('click', '.btn-primary', function() {
  82. var $rows = $sortModal.find("tbody > tr"),
  83. $alert = $sortModal.find('div.alert'),
  84. fields = [],
  85. results = [];
  86. that.options.sortPriority = $.map($rows, function(row) {
  87. var $row = $(row),
  88. name = $row.find('.multi-sort-name').val(),
  89. order = $row.find('.multi-sort-order').val();
  90. fields.push(name);
  91. return {
  92. sortName: name,
  93. sortOrder: order
  94. };
  95. });
  96. var sorted_fields = fields.sort();
  97. for (var i = 0; i < fields.length - 1; i++) {
  98. if (sorted_fields[i + 1] == sorted_fields[i]) {
  99. results.push(sorted_fields[i]);
  100. }
  101. }
  102. if (results.length > 0) {
  103. if ($alert.length === 0) {
  104. $alert = '<div class="alert alert-danger" role="alert"><strong>' + that.options.formatDuplicateAlertTitle() + '</strong> ' + that.options.formatDuplicateAlertDescription() + '</div>';
  105. $($alert).insertBefore($sortModal.find('.bars'));
  106. }
  107. } else {
  108. if ($alert.length === 1) {
  109. $($alert).remove();
  110. }
  111. that.onMultipleSort();
  112. $sortModal.modal('hide');
  113. }
  114. });
  115. if (that.options.sortPriority === null) {
  116. if (that.options.sortName) {
  117. that.options.sortPriority = [{
  118. sortName: that.options.sortName,
  119. sortOrder: that.options.sortOrder
  120. }];
  121. }
  122. }
  123. if ($rows.length < that.options.sortPriority.length && typeof that.options.sortPriority === 'object') {
  124. for (var i = 0; i < that.options.sortPriority.length; i++) {
  125. that.addLevel(i, that.options.sortPriority[i]);
  126. }
  127. } else {
  128. that.addLevel(0);
  129. }
  130. that.setButtonStates();
  131. }
  132. };
  133. $.extend($.fn.bootstrapTable.defaults, {
  134. showMultiSort: false,
  135. sortPriority: null,
  136. onMultipleSort: function() {
  137. return false;
  138. }
  139. });
  140. $.extend($.fn.bootstrapTable.defaults.icons, {
  141. sort: 'glyphicon-sort',
  142. plus: 'glyphicon-plus',
  143. minus: 'glyphicon-minus'
  144. });
  145. $.extend($.fn.bootstrapTable.Constructor.EVENTS, {
  146. 'multiple-sort.bs.table': 'onMultipleSort'
  147. });
  148. $.extend($.fn.bootstrapTable.locales, {
  149. formatMultipleSort: function() {
  150. return 'Multiple Sort';
  151. },
  152. formatAddLevel: function() {
  153. return "Add Level";
  154. },
  155. formatDeleteLevel: function() {
  156. return "Delete Level";
  157. },
  158. formatColumn: function() {
  159. return "Column";
  160. },
  161. formatOrder: function() {
  162. return "Order";
  163. },
  164. formatSortBy: function() {
  165. return "Sort by";
  166. },
  167. formatThenBy: function() {
  168. return "Then by";
  169. },
  170. formatSort: function() {
  171. return "Sort";
  172. },
  173. formatCancel: function() {
  174. return "Cancel";
  175. },
  176. formatDuplicateAlertTitle: function() {
  177. return "Duplicate(s) detected!";
  178. },
  179. formatDuplicateAlertDescription: function() {
  180. return "Please remove or change any duplicate column.";
  181. }
  182. });
  183. $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales);
  184. var BootstrapTable = $.fn.bootstrapTable.Constructor,
  185. _initToolbar = BootstrapTable.prototype.initToolbar;
  186. BootstrapTable.prototype.initToolbar = function() {
  187. this.showToolbar = true;
  188. var that = this;
  189. _initToolbar.apply(this, Array.prototype.slice.apply(arguments));
  190. if (this.options.showMultiSort) {
  191. var $btnGroup = this.$toolbar.find('>.btn-group'),
  192. $multiSortBtn = $btnGroup.find('div.multi-sort');
  193. if (!$multiSortBtn.length) {
  194. $multiSortBtn = ' <button class="multi-sort btn btn-default' + (this.options.iconSize === undefined ? '' : ' btn-' + this.options.iconSize) + '" type="button" data-toggle="modal" data-target="#sortModal" title="' + this.options.formatMultipleSort() + '">';
  195. $multiSortBtn += ' <i class="' + this.options.iconsPrefix + ' ' + this.options.icons.sort + '"></i>';
  196. $multiSortBtn += '</button>';
  197. $btnGroup.append($multiSortBtn);
  198. showSortModal(that);
  199. }
  200. this.$el.one('sort.bs.table', function() {
  201. isSingleSort = true;
  202. });
  203. this.$el.on('multiple-sort.bs.table', function() {
  204. isSingleSort = false;
  205. });
  206. this.$el.on('load-success.bs.table', function() {
  207. if (!isSingleSort && that.options.sortPriority !== null && typeof that.options.sortPriority === 'object') {
  208. that.onMultipleSort();
  209. }
  210. });
  211. this.$el.on('column-switch.bs.table', function() {
  212. that.options.sortPriority = null;
  213. $('#sortModal').remove();
  214. showSortModal(that);
  215. });
  216. }
  217. };
  218. BootstrapTable.prototype.onMultipleSort = function() {
  219. var that = this;
  220. var cmp = function(x, y) {
  221. return x > y ? 1 : x < y ? -1 : 0;
  222. };
  223. var arrayCmp = function(a, b) {
  224. var arr1 = [],
  225. arr2 = [];
  226. for (var i = 0; i < that.options.sortPriority.length; i++) {
  227. var order = that.options.sortPriority[i].sortOrder === 'desc' ? -1 : 1,
  228. aa = a[that.options.sortPriority[i].sortName],
  229. bb = b[that.options.sortPriority[i].sortName];
  230. if (aa === undefined || aa === null) {
  231. aa = '';
  232. }
  233. if (bb === undefined || bb === null) {
  234. bb = '';
  235. }
  236. if ($.isNumeric(aa) && $.isNumeric(bb)) {
  237. aa = parseFloat(aa);
  238. bb = parseFloat(bb);
  239. }
  240. if (typeof aa !== 'string') {
  241. aa = aa.toString();
  242. }
  243. arr1.push(
  244. order * cmp(aa, bb));
  245. arr2.push(
  246. order * cmp(bb, aa));
  247. }
  248. return cmp(arr1, arr2);
  249. };
  250. this.data.sort(function(a, b) {
  251. return arrayCmp(a, b);
  252. });
  253. this.initBody();
  254. this.assignSortableArrows();
  255. this.trigger('multiple-sort');
  256. };
  257. BootstrapTable.prototype.addLevel = function(index, sortPriority) {
  258. var $sortModal = $("#sortModal"),
  259. text = index === 0 ? this.options.formatSortBy() : this.options.formatThenBy();
  260. $sortModal.find('tbody')
  261. .append($('<tr>')
  262. .append($('<td>').text(text))
  263. .append($('<td>').append($('<select class="form-control multi-sort-name">')))
  264. .append($('<td>').append($('<select class="form-control multi-sort-order">')))
  265. );
  266. var $multiSortName = $sortModal.find('.multi-sort-name').last(),
  267. $multiSortOrder = $sortModal.find('.multi-sort-order').last();
  268. this.options.columns.forEach(function(column) {
  269. if (column.sortable === false || column.visible === false) {
  270. return true;
  271. }
  272. $multiSortName.append('<option value="' + column.field + '">' + column.title + '</option>');
  273. });
  274. $.each(sort_order, function(value, order) {
  275. $multiSortOrder.append('<option value="' + value + '">' + order + '</option>');
  276. });
  277. if (sortPriority !== undefined) {
  278. $multiSortName.find('option[value="' + sortPriority.sortName + '"]').attr("selected", true);
  279. $multiSortOrder.find('option[value="' + sortPriority.sortOrder + '"]').attr("selected", true);
  280. }
  281. };
  282. BootstrapTable.prototype.assignSortableArrows = function() {
  283. var that = this,
  284. headers = that.$header.find('th');
  285. for (var i = 0; i < headers.length; i++) {
  286. for (var c = 0; c < that.options.sortPriority.length; c++) {
  287. if ($(headers[i]).data('field') === that.options.sortPriority[c].sortName) {
  288. $(headers[i]).find('.sortable').css('background-image', 'url(' + (that.options.sortPriority[c].sortOrder === 'desc' ? arrowDesc : arrowAsc) + ')');
  289. }
  290. }
  291. }
  292. };
  293. BootstrapTable.prototype.setButtonStates = function() {
  294. var $sortModal = $('#sortModal'),
  295. total = $sortModal.find('.multi-sort-name:first option').length,
  296. current = $sortModal.find('tbody tr').length;
  297. if (current == total) {
  298. $sortModal.find('#add').attr('disabled', 'disabled');
  299. }
  300. if (current > 1) {
  301. $sortModal.find('#delete').removeAttr('disabled');
  302. }
  303. if (current < total) {
  304. $sortModal.find('#add').removeAttr('disabled');
  305. }
  306. if (current == 1) {
  307. $sortModal.find('#delete').attr('disabled', 'disabled');
  308. }
  309. };
  310. })(jQuery);