bootstrap-table-multiple-sort.js 17 KB

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