bootstrap-table-filter-control.js 16 KB

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