bootstrap-table-filter-control.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. /**
  2. * @author: Dennis Hernández
  3. * @webSite: http://djhvscf.github.io/Blog
  4. * @version: v1.0.0
  5. */
  6. !function ($) {
  7. 'use strict';
  8. var sprintf = $.fn.bootstrapTable.utils.sprintf;
  9. $.extend($.fn.bootstrapTable.defaults.icons, {
  10. clear: 'glyphicon-trash icon-clear'
  11. });
  12. var addOptionToSelectControl = function (selectControl, value, text) {
  13. selectControl = $(selectControl.get(selectControl.length - 1));
  14. if (existsOptionInSelectControl(selectControl, value)) {
  15. selectControl.append($("<option></option>")
  16. .attr("value", value)
  17. .text($('<div />').html(text).text()));
  18. // Sort it. Not overly efficient to do this here
  19. var $opts = selectControl.find('option:gt(0)');
  20. $opts.sort(function (a, b) {
  21. a = $(a).text().toLowerCase();
  22. b = $(b).text().toLowerCase();
  23. if ($.isNumeric(a) && $.isNumeric(b)) {
  24. // Convert numerical values from string to float.
  25. a = parseFloat(a);
  26. b = parseFloat(b);
  27. }
  28. return a > b ? 1 : a < b ? -1 : 0;
  29. });
  30. selectControl.find('option:gt(0)').remove();
  31. selectControl.append($opts);
  32. }
  33. };
  34. var existsOptionInSelectControl = function (selectControl, value) {
  35. var options = selectControl.get(selectControl.length - 1).options;
  36. for (var i = 0; i < options.length; i++) {
  37. if (options[i].value === value.toString()) {
  38. //The value is nor valid to add
  39. return false;
  40. }
  41. }
  42. //If we get here, the value is valid to add
  43. return true;
  44. };
  45. var fixHeaderCSS = function (that) {
  46. that.$tableHeader.css('height', '77px');
  47. };
  48. var getCurrentHeader = function (that) {
  49. var header = that.$header;
  50. if (that.options.height) {
  51. header = that.$tableHeader;
  52. }
  53. return header;
  54. };
  55. var getCurrentSearchControls = function (that) {
  56. var searchControls = 'select, input';
  57. if (that.options.height) {
  58. searchControls = 'table select, table input';
  59. }
  60. return searchControls;
  61. };
  62. var copyValues = function (that) {
  63. var header = getCurrentHeader(that),
  64. searchControls = getCurrentSearchControls(that);
  65. that.options.values = [];
  66. header.find(searchControls).each(function () {
  67. that.options.values.push(
  68. {
  69. field: $(this).closest('[data-field]').data('field'),
  70. value: $(this).val()
  71. });
  72. });
  73. };
  74. var setValues = function(that) {
  75. var field = null,
  76. result = [],
  77. header = getCurrentHeader(that),
  78. searchControls = getCurrentSearchControls(that);
  79. if (that.options.values.length > 0) {
  80. header.find(searchControls).each(function (index, ele) {
  81. field = $(this).closest('[data-field]').data('field');
  82. result = $.grep(that.options.values, function (valueObj) {
  83. return valueObj.field === field;
  84. });
  85. if (result.length > 0) {
  86. $(this).val(result[0].value);
  87. }
  88. });
  89. }
  90. };
  91. var createControls = function (that, header) {
  92. var addedFilterControl = false,
  93. isVisible,
  94. html,
  95. timeoutId = 0;
  96. $.each(that.columns, function (i, column) {
  97. isVisible = 'hidden';
  98. html = [];
  99. if (!column.visible) {
  100. return;
  101. }
  102. if (!column.filterControl) {
  103. html.push('<div style="height: 34px;"></div>');
  104. } else {
  105. html.push('<div style="margin: 0 2px 2px 2px;" class="filterControl">');
  106. var nameControl = column.filterControl.toLowerCase();
  107. if (column.searchable && that.options.filterTemplate[nameControl]) {
  108. addedFilterControl = true;
  109. isVisible = 'visible';
  110. html.push(that.options.filterTemplate[nameControl](column.field, isVisible));
  111. }
  112. }
  113. $.each(header.children().children(), function (i, tr) {
  114. tr = $(tr);
  115. if (tr.data('field') === column.field) {
  116. tr.find('.fht-cell').append(html.join(''));
  117. return false;
  118. }
  119. });
  120. if (column.filterData !== undefined && column.filterData.toLowerCase() !== 'column') {
  121. var filterDataType = column.filterData.substring(0, 3);
  122. var filterDataSource = column.filterData.substring(4, column.filterData.length);
  123. var selectControl = $('.' + column.field);
  124. addOptionToSelectControl(selectControl, '', '');
  125. switch (filterDataType) {
  126. case 'url':
  127. $.ajax({
  128. url: filterDataSource,
  129. dataType: 'json',
  130. success: function (data) {
  131. $.each(data, function (key, value) {
  132. addOptionToSelectControl(selectControl, key, value);
  133. });
  134. }
  135. });
  136. break;
  137. case 'var':
  138. var variableValues = window[filterDataSource];
  139. for (var key in variableValues) {
  140. addOptionToSelectControl(selectControl, key, variableValues[key]);
  141. }
  142. break;
  143. }
  144. }
  145. });
  146. if (addedFilterControl) {
  147. header.off('keyup', 'input').on('keyup', 'input', function (event) {
  148. clearTimeout(timeoutId);
  149. timeoutId = setTimeout(function () {
  150. that.onColumnSearch(event);
  151. }, that.options.searchTimeOut);
  152. });
  153. header.off('change', 'select').on('change', 'select', function (event) {
  154. clearTimeout(timeoutId);
  155. timeoutId = setTimeout(function () {
  156. that.onColumnSearch(event);
  157. }, that.options.searchTimeOut);
  158. });
  159. header.off('mouseup', 'input').on('mouseup', 'input', function (event) {
  160. var $input = $(this),
  161. oldValue = $input.val();
  162. if (oldValue === "") {
  163. return;
  164. }
  165. setTimeout(function(){
  166. var newValue = $input.val();
  167. if (newValue === "") {
  168. clearTimeout(timeoutId);
  169. timeoutId = setTimeout(function () {
  170. that.onColumnSearch(event);
  171. }, that.options.searchTimeOut);
  172. }
  173. }, 1);
  174. });
  175. if (header.find('.date-filter-control').length > 0) {
  176. $.each(that.columns, function (i, column) {
  177. if (column.filterControl !== undefined && column.filterControl.toLowerCase() === 'datepicker') {
  178. header.find('.date-filter-control.' + column.field).datepicker(column.filterDatepickerOptions)
  179. .on('changeDate', function (e) {
  180. //Fired the keyup event
  181. $(e.currentTarget).keyup();
  182. });
  183. }
  184. });
  185. }
  186. } else {
  187. header.find('.filterControl').hide();
  188. }
  189. };
  190. $.extend($.fn.bootstrapTable.defaults, {
  191. filterControl: false,
  192. onColumnSearch: function (field, text) {
  193. return false;
  194. },
  195. filterShowClear: false,
  196. filterLocal: true,
  197. //internal variables
  198. values: [],
  199. filterTemplate: {
  200. input: function (field, isVisible) {
  201. return sprintf('<input type="text" class="form-control %s" style="width: 100%; visibility: %s">', field, isVisible);
  202. },
  203. select: function (field, isVisible) {
  204. return sprintf('<select class="%s form-control" style="width: 100%; visibility: %s"></select>', field, isVisible);
  205. },
  206. datepicker: function (field, isVisible) {
  207. return sprintf('<input type="text" class="date-filter-control %s form-control" style="width: 100%; visibility: %s">', field, isVisible);
  208. }
  209. }
  210. });
  211. $.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
  212. filterControl: undefined,
  213. filterData: undefined,
  214. filterDatepickerOptions: undefined,
  215. filterStrictSearch: false
  216. });
  217. $.extend($.fn.bootstrapTable.Constructor.EVENTS, {
  218. 'column-search.bs.table': 'onColumnSearch'
  219. });
  220. var BootstrapTable = $.fn.bootstrapTable.Constructor,
  221. _init = BootstrapTable.prototype.init,
  222. _initToolbar = BootstrapTable.prototype.initToolbar,
  223. _initHeader = BootstrapTable.prototype.initHeader,
  224. _initBody = BootstrapTable.prototype.initBody,
  225. _initSearch = BootstrapTable.prototype.initSearch;
  226. BootstrapTable.prototype.init = function () {
  227. //Make sure that the filterControl option is set
  228. if (this.options.filterControl) {
  229. var that = this;
  230. //Make sure that the internal variables are set correctly
  231. this.options.values = [];
  232. this.$el.on('reset-view.bs.table', function () {
  233. //Create controls on $tableHeader if the height is set
  234. if (!that.options.height) {
  235. return;
  236. }
  237. //Avoid recreate the controls
  238. if (that.$tableHeader.find('select').length > 0 || that.$tableHeader.find('input').length > 0) {
  239. return;
  240. }
  241. createControls(that, that.$tableHeader);
  242. }).on('post-header.bs.table', function () {
  243. setValues(that);
  244. }).on('post-body.bs.table', function () {
  245. if (that.options.height) {
  246. fixHeaderCSS(that);
  247. }
  248. }).on('column-switch.bs.table', function() {
  249. setValues(that);
  250. });
  251. }
  252. _init.apply(this, Array.prototype.slice.apply(arguments));
  253. };
  254. BootstrapTable.prototype.initToolbar = function () {
  255. if ((!this.showToolbar) && (this.options.filterControl)) {
  256. this.showToolbar = this.options.filterControl;
  257. }
  258. _initToolbar.apply(this, Array.prototype.slice.apply(arguments));
  259. if (this.options.filterControl && this.options.filterShowClear) {
  260. var $btnGroup = this.$toolbar.find('>.btn-group'),
  261. $btnClear = $btnGroup.find('div.export');
  262. if (!$btnClear.length) {
  263. $btnClear = $([
  264. '<button class="btn btn-default " ' +
  265. 'type="button">',
  266. sprintf('<i class="%s %s"></i> ', this.options.iconsPrefix, this.options.icons.clear),
  267. '</button>',
  268. '</ul>'].join('')).appendTo($btnGroup);
  269. $btnClear.off('click').on('click', $.proxy(this.clearFilterControl, this));
  270. }
  271. }
  272. };
  273. BootstrapTable.prototype.initHeader = function () {
  274. _initHeader.apply(this, Array.prototype.slice.apply(arguments));
  275. if (!this.options.filterControl) {
  276. return;
  277. }
  278. createControls(this, this.$header);
  279. };
  280. BootstrapTable.prototype.initBody = function () {
  281. _initBody.apply(this, Array.prototype.slice.apply(arguments));
  282. var that = this,
  283. data = this.options.data,
  284. pageTo = this.pageTo < this.options.data.length ? this.options.data.length : this.pageTo;
  285. for (var i = this.pageFrom - 1; i < pageTo; i++) {
  286. var item = data[i];
  287. $.each(this.header.fields, function (j, field) {
  288. var value = item[field],
  289. column = that.columns[$.fn.bootstrapTable.utils.getFieldIndex(that.columns, field)];
  290. value = $.fn.bootstrapTable.utils.calculateObjectValue(that.header, that.header.formatters[j], [value, item, i], value);
  291. if ((!column.checkbox) || (!column.radio)) {
  292. if (column.filterControl !== undefined && column.filterControl.toLowerCase() === 'select' && column.searchable) {
  293. if (column.filterData === undefined || column.filterData.toLowerCase() === 'column') {
  294. var selectControl = $('.' + column.field);
  295. if (selectControl !== undefined && selectControl.length > 0) {
  296. if (selectControl.get(selectControl.length - 1).options.length === 0) {
  297. //Added the default option
  298. addOptionToSelectControl(selectControl, '', '');
  299. }
  300. //Added a new value
  301. addOptionToSelectControl(selectControl, value, value);
  302. }
  303. }
  304. }
  305. }
  306. });
  307. }
  308. };
  309. BootstrapTable.prototype.initSearch = function () {
  310. _initSearch.apply(this, Array.prototype.slice.apply(arguments));
  311. if (! this.options.filterLocal) {
  312. return;
  313. }
  314. var that = this;
  315. var fp = $.isEmptyObject(this.filterColumnsPartial) ? null : this.filterColumnsPartial;
  316. //Check partial column filter
  317. this.data = fp ? $.grep(this.data, function (item, i) {
  318. for (var key in fp) {
  319. var thisColumn = that.columns[$.fn.bootstrapTable.utils.getFieldIndex(that.columns, key)];
  320. var fval = fp[key].toLowerCase();
  321. var value = item[key];
  322. value = $.fn.bootstrapTable.utils.calculateObjectValue(that.header,
  323. that.header.formatters[$.inArray(key, that.header.fields)],
  324. [value, item, i], value);
  325. if(thisColumn.filterStrictSearch){
  326. if (!($.inArray(key, that.header.fields) !== -1 &&
  327. (typeof value === 'string' || typeof value === 'number') &&
  328. value.toString().toLowerCase() === fval.toString().toLowerCase())) {
  329. return false;
  330. }
  331. }
  332. else{
  333. if (!($.inArray(key, that.header.fields) !== -1 &&
  334. (typeof value === 'string' || typeof value === 'number') &&
  335. (value + '').toLowerCase().indexOf(fval) !== -1)) {
  336. return false;
  337. }
  338. };
  339. }
  340. return true;
  341. }) : this.data;
  342. };
  343. BootstrapTable.prototype.onColumnSearch = function (event) {
  344. copyValues(this);
  345. var text = $.trim($(event.currentTarget).val());
  346. var $field = $(event.currentTarget).closest('[data-field]').data('field');
  347. if ($.isEmptyObject(this.filterColumnsPartial)) {
  348. this.filterColumnsPartial = {};
  349. }
  350. if (text) {
  351. this.filterColumnsPartial[$field] = text;
  352. } else {
  353. delete this.filterColumnsPartial[$field];
  354. }
  355. this.options.pageNumber = 1;
  356. this.onSearch(event);
  357. this.updatePagination();
  358. this.trigger('column-search', $field, text);
  359. };
  360. BootstrapTable.prototype.clearFilterControl = function () {
  361. if (this.options.filterControl && this.options.filterShowClear) {
  362. $.each(this.options.values, function (i, item) {
  363. item.value = '';
  364. });
  365. setValues(this);
  366. var controls = getCurrentHeader(this).find(getCurrentSearchControls(this)),
  367. timeoutId = 0;
  368. if (controls.length > 0) {
  369. this.filterColumnsPartial = {};
  370. clearTimeout(timeoutId);
  371. timeoutId = setTimeout(function () {
  372. $(controls[0]).trigger(controls[0].tagName === 'INPUT' ? 'keyup' : 'change');
  373. }, this.options.searchTimeOut);
  374. }
  375. }
  376. };
  377. }(jQuery);