bootstrap-table-filter-control.js 20 KB

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