bootstrap-table-filter-control.js 18 KB

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