bootstrap-table-filter-control.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. /**
  2. * @author: Dennis Hernández
  3. * @webSite: http://djhvscf.github.io/Blog
  4. * @version: v2.2.0
  5. */
  6. const Utils = $.fn.bootstrapTable.utils
  7. const UtilsFilterControl = {
  8. getOptionsFromSelectControl (selectControl) {
  9. return selectControl.get(selectControl.length - 1).options
  10. },
  11. hideUnusedSelectOptions (selectControl, uniqueValues) {
  12. const options = UtilsFilterControl.getOptionsFromSelectControl(
  13. selectControl
  14. )
  15. for (let i = 0; i < options.length; i++) {
  16. if (options[i].value !== '') {
  17. if (!uniqueValues.hasOwnProperty(options[i].value)) {
  18. selectControl
  19. .find(Utils.sprintf('option[value=\'%s\']', options[i].value))
  20. .hide()
  21. } else {
  22. selectControl
  23. .find(Utils.sprintf('option[value=\'%s\']', options[i].value))
  24. .show()
  25. }
  26. }
  27. }
  28. },
  29. addOptionToSelectControl (selectControl, _value, text, selected) {
  30. const value = $.trim(_value)
  31. const $selectControl = $(selectControl.get(selectControl.length - 1))
  32. if (
  33. !UtilsFilterControl.existOptionInSelectControl(selectControl, value)
  34. ) {
  35. const option = $(
  36. $('<option></option>')
  37. .attr('value', value)
  38. .text(
  39. $('<div />')
  40. .html(text)
  41. .text()
  42. )
  43. )
  44. if (value === selected) {
  45. option.attr('selected', true)
  46. }
  47. $selectControl.append(option)
  48. }
  49. },
  50. sortSelectControl (selectControl, orderBy) {
  51. const $selectControl = $(selectControl.get(selectControl.length - 1))
  52. const $opts = $selectControl.find('option:gt(0)')
  53. $opts.sort((a, b) => {
  54. return Utils.sort(a.textContent, b.textContent, orderBy === 'desc' ? -1 : 1)
  55. })
  56. $selectControl.find('option:gt(0)').remove()
  57. $selectControl.append($opts)
  58. },
  59. existOptionInSelectControl (selectControl, value) {
  60. const options = UtilsFilterControl.getOptionsFromSelectControl(
  61. selectControl
  62. )
  63. for (let i = 0; i < options.length; i++) {
  64. if (options[i].value === value.toString()) {
  65. // The value is not valid to add
  66. return true
  67. }
  68. }
  69. // If we get here, the value is valid to add
  70. return false
  71. },
  72. fixHeaderCSS ({ $tableHeader }) {
  73. $tableHeader.css('height', '77px')
  74. },
  75. getCurrentHeader ({ $header, options, $tableHeader }) {
  76. let header = $header
  77. if (options.height) {
  78. header = $tableHeader
  79. }
  80. return header
  81. },
  82. getCurrentSearchControls ({ options }) {
  83. let searchControls = 'select, input'
  84. if (options.height) {
  85. searchControls = 'table select, table input'
  86. }
  87. return searchControls
  88. },
  89. getCursorPosition (el) {
  90. if (Utils.isIEBrowser()) {
  91. if ($(el).is('input[type=text]')) {
  92. let pos = 0
  93. if ('selectionStart' in el) {
  94. pos = el.selectionStart
  95. } else if ('selection' in document) {
  96. el.focus()
  97. const Sel = document.selection.createRange()
  98. const SelLength = document.selection.createRange().text.length
  99. Sel.moveStart('character', -el.value.length)
  100. pos = Sel.text.length - SelLength
  101. }
  102. return pos
  103. }
  104. return -1
  105. }
  106. return -1
  107. },
  108. setCursorPosition (el) {
  109. $(el).val(el.value)
  110. },
  111. copyValues (that) {
  112. const header = UtilsFilterControl.getCurrentHeader(that)
  113. const searchControls = UtilsFilterControl.getCurrentSearchControls(that)
  114. that.options.valuesFilterControl = []
  115. header.find(searchControls).each(function () {
  116. that.options.valuesFilterControl.push({
  117. field: $(this)
  118. .closest('[data-field]')
  119. .data('field'),
  120. value: $(this).val(),
  121. position: UtilsFilterControl.getCursorPosition($(this).get(0)),
  122. hasFocus: $(this).is(':focus')
  123. })
  124. })
  125. },
  126. setValues (that) {
  127. let field = null
  128. let result = []
  129. const header = UtilsFilterControl.getCurrentHeader(that)
  130. const searchControls = UtilsFilterControl.getCurrentSearchControls(that)
  131. if (that.options.valuesFilterControl.length > 0) {
  132. // Callback to apply after settings fields values
  133. let fieldToFocusCallback = null
  134. header.find(searchControls).each(function (index, ele) {
  135. field = $(this)
  136. .closest('[data-field]')
  137. .data('field')
  138. result = that.options.valuesFilterControl.filter(valueObj => valueObj.field === field)
  139. if (result.length > 0) {
  140. $(this).val(result[0].value)
  141. if (result[0].hasFocus) {
  142. // set callback if the field had the focus.
  143. fieldToFocusCallback = ((fieldToFocus, carretPosition) => {
  144. // Closure here to capture the field and cursor position
  145. const closedCallback = () => {
  146. fieldToFocus.focus()
  147. UtilsFilterControl.setCursorPosition(fieldToFocus, carretPosition)
  148. }
  149. return closedCallback
  150. })($(this).get(0), result[0].position)
  151. }
  152. }
  153. })
  154. // Callback call.
  155. if (fieldToFocusCallback !== null) {
  156. fieldToFocusCallback()
  157. }
  158. }
  159. },
  160. collectBootstrapCookies () {
  161. const cookies = []
  162. const foundCookies = document.cookie.match(/(?:bs.table.)(\w*)/g)
  163. if (foundCookies) {
  164. $.each(foundCookies, (i, _cookie) => {
  165. let cookie = _cookie
  166. if (/./.test(cookie)) {
  167. cookie = cookie.split('.').pop()
  168. }
  169. if ($.inArray(cookie, cookies) === -1) {
  170. cookies.push(cookie)
  171. }
  172. })
  173. return cookies
  174. }
  175. },
  176. escapeID (id) {
  177. return String(id).replace(/(:|\.|\[|\]|,)/g, '\\$1')
  178. },
  179. isColumnSearchableViaSelect ({ filterControl, searchable }) {
  180. return filterControl &&
  181. filterControl.toLowerCase() === 'select' &&
  182. searchable
  183. },
  184. isFilterDataNotGiven ({ filterData }) {
  185. return filterData === undefined ||
  186. filterData.toLowerCase() === 'column'
  187. },
  188. hasSelectControlElement (selectControl) {
  189. return selectControl && selectControl.length > 0
  190. },
  191. initFilterSelectControls (that) {
  192. const data = that.data
  193. const itemsPerPage = that.pageTo < that.options.data.length ? that.options.data.length : that.pageTo
  194. const z = that.options.pagination
  195. ? that.options.sidePagination === 'server'
  196. ? that.pageTo
  197. : that.options.totalRows
  198. : that.pageTo
  199. $.each(that.header.fields, (j, field) => {
  200. const column = that.columns[that.fieldsColumnsIndex[field]]
  201. const selectControl = that.$tableBody.find(`.bootstrap-table-filter-control-${UtilsFilterControl.escapeID(column.field)}`)
  202. if (
  203. UtilsFilterControl.isColumnSearchableViaSelect(column) &&
  204. UtilsFilterControl.isFilterDataNotGiven(column) &&
  205. UtilsFilterControl.hasSelectControlElement(selectControl)
  206. ) {
  207. if (selectControl.get(selectControl.length - 1).options.length === 0) {
  208. // Added the default option
  209. UtilsFilterControl.addOptionToSelectControl(selectControl, '', column.filterControlPlaceholder, column.filterDefault)
  210. }
  211. const uniqueValues = {}
  212. for (let i = 0; i < z; i++) {
  213. // Added a new value
  214. const fieldValue = data[i][field]
  215. let formattedValue = Utils.calculateObjectValue(that.header, that.header.formatters[j], [fieldValue, data[i], i], fieldValue)
  216. if (column.filterDataCollector) {
  217. formattedValue = Utils.calculateObjectValue(that.header, column.filterDataCollector, [fieldValue, data[i], formattedValue], formattedValue)
  218. }
  219. if (typeof formattedValue === 'object' && formattedValue !== null) {
  220. formattedValue.forEach((value) => {
  221. UtilsFilterControl.addOptionToSelectControl(selectControl, value, value, column.filterDefault)
  222. })
  223. continue
  224. }
  225. UtilsFilterControl.addOptionToSelectControl(selectControl, formattedValue, formattedValue, column.filterDefault)
  226. }
  227. UtilsFilterControl.sortSelectControl(selectControl, column.filterOrderBy)
  228. if (that.options.hideUnusedSelectOptions) {
  229. UtilsFilterControl.hideUnusedSelectOptions(selectControl, uniqueValues)
  230. }
  231. }
  232. })
  233. that.trigger('created-controls')
  234. },
  235. getFilterDataMethod (objFilterDataMethod, searchTerm) {
  236. const keys = Object.keys(objFilterDataMethod)
  237. for (let i = 0; i < keys.length; i++) {
  238. if (keys[i] === searchTerm) {
  239. return objFilterDataMethod[searchTerm]
  240. }
  241. }
  242. return null
  243. },
  244. createControls (that, header) {
  245. let addedFilterControl = false
  246. let isVisible
  247. let html
  248. $.each(that.columns, (i, column) => {
  249. isVisible = 'hidden'
  250. html = []
  251. if (!column.visible) {
  252. return
  253. }
  254. if (!column.filterControl) {
  255. html.push('<div class="no-filter-control"></div>')
  256. } else {
  257. html.push('<div class="filter-control">')
  258. const nameControl = column.filterControl.toLowerCase()
  259. if (column.searchable && that.options.filterTemplate[nameControl]) {
  260. addedFilterControl = true
  261. isVisible = 'visible'
  262. html.push(
  263. that.options.filterTemplate[nameControl](
  264. that,
  265. column.field,
  266. isVisible,
  267. column.filterControlPlaceholder
  268. ? column.filterControlPlaceholder
  269. : '',
  270. column.filterDefault
  271. )
  272. )
  273. if ('' !== column.filterDefault && 'undefined' !== typeof column.filterDefault) {
  274. if ($.isEmptyObject(that.filterColumnsPartial)) {
  275. that.filterColumnsPartial = {}
  276. }
  277. that.filterColumnsPartial[column.field] = column.filterDefault
  278. }
  279. }
  280. }
  281. $.each(header.children().children(), (i, tr) => {
  282. const $tr = $(tr)
  283. if ($tr.data('field') === column.field) {
  284. $tr.find('.fht-cell').append(html.join(''))
  285. return false
  286. }
  287. })
  288. if (
  289. column.filterData !== undefined &&
  290. column.filterData.toLowerCase() !== 'column'
  291. ) {
  292. const filterDataType = UtilsFilterControl.getFilterDataMethod(
  293. /* eslint-disable no-use-before-define */
  294. filterDataMethods,
  295. column.filterData.substring(0, column.filterData.indexOf(':'))
  296. )
  297. let filterDataSource
  298. let selectControl
  299. if (filterDataType !== null) {
  300. filterDataSource = column.filterData.substring(
  301. column.filterData.indexOf(':') + 1,
  302. column.filterData.length
  303. )
  304. selectControl = that.$tableBody.find(`.bootstrap-table-filter-control-${UtilsFilterControl.escapeID(column.field)}`)
  305. UtilsFilterControl.addOptionToSelectControl(selectControl, '', column.filterControlPlaceholder, column.filterDefault)
  306. filterDataType(filterDataSource, selectControl, column.filterDefault)
  307. } else {
  308. throw new SyntaxError(
  309. 'Error. You should use any of these allowed filter data methods: var, json, url.' +
  310. ' Use like this: var: {key: "value"}'
  311. )
  312. }
  313. let variableValues
  314. let key
  315. // eslint-disable-next-line default-case
  316. switch (filterDataType) {
  317. case 'url':
  318. $.ajax({
  319. url: filterDataSource,
  320. dataType: 'json',
  321. success (data) {
  322. // eslint-disable-next-line guard-for-in
  323. for (const key in data) {
  324. UtilsFilterControl.addOptionToSelectControl(selectControl, key, data[key], column.filterDefault)
  325. }
  326. UtilsFilterControl.sortSelectControl(selectControl, column.filterOrderBy)
  327. }
  328. })
  329. break
  330. case 'var':
  331. variableValues = window[filterDataSource]
  332. // eslint-disable-next-line guard-for-in
  333. for (key in variableValues) {
  334. UtilsFilterControl.addOptionToSelectControl(selectControl, key, variableValues[key], column.filterDefault)
  335. }
  336. UtilsFilterControl.sortSelectControl(selectControl, column.filterOrderBy)
  337. break
  338. case 'jso':
  339. variableValues = JSON.parse(filterDataSource)
  340. // eslint-disable-next-line guard-for-in
  341. for (key in variableValues) {
  342. UtilsFilterControl.addOptionToSelectControl(selectControl, key, variableValues[key], column.filterDefault)
  343. }
  344. UtilsFilterControl.sortSelectControl(selectControl, column.filterOrderBy)
  345. break
  346. }
  347. }
  348. })
  349. if (addedFilterControl) {
  350. header.off('keyup', 'input').on('keyup', 'input', (event, obj) => {
  351. // Simulate enter key action from clear button
  352. event.keyCode = obj ? obj.keyCode : event.keyCode
  353. if (that.options.searchOnEnterKey && event.keyCode !== 13) {
  354. return
  355. }
  356. if ($.inArray(event.keyCode, [37, 38, 39, 40]) > -1) {
  357. return
  358. }
  359. const $currentTarget = $(event.currentTarget)
  360. if ($currentTarget.is(':checkbox') || $currentTarget.is(':radio')) {
  361. return
  362. }
  363. clearTimeout(event.currentTarget.timeoutId || 0)
  364. event.currentTarget.timeoutId = setTimeout(() => {
  365. that.onColumnSearch(event)
  366. }, that.options.searchTimeOut)
  367. })
  368. header.off('change', 'select').on('change', 'select', event => {
  369. if (that.options.searchOnEnterKey && event.keyCode !== 13) {
  370. return
  371. }
  372. if ($.inArray(event.keyCode, [37, 38, 39, 40]) > -1) {
  373. return
  374. }
  375. clearTimeout(event.currentTarget.timeoutId || 0)
  376. event.currentTarget.timeoutId = setTimeout(() => {
  377. that.onColumnSearch(event)
  378. }, that.options.searchTimeOut)
  379. })
  380. header.off('mouseup', 'input').on('mouseup', 'input', function (event) {
  381. const $input = $(this)
  382. const oldValue = $input.val()
  383. if (oldValue === '') {
  384. return
  385. }
  386. setTimeout(() => {
  387. const newValue = $input.val()
  388. if (newValue === '') {
  389. clearTimeout(event.currentTarget.timeoutId || 0)
  390. event.currentTarget.timeoutId = setTimeout(() => {
  391. that.onColumnSearch(event)
  392. }, that.options.searchTimeOut)
  393. }
  394. }, 1)
  395. })
  396. if (header.find('.date-filter-control').length > 0) {
  397. $.each(that.columns, (i, { filterControl, field, filterDatepickerOptions }) => {
  398. if (
  399. filterControl !== undefined &&
  400. filterControl.toLowerCase() === 'datepicker'
  401. ) {
  402. header
  403. .find(
  404. `.date-filter-control.bootstrap-table-filter-control-${field}`
  405. )
  406. .datepicker(filterDatepickerOptions)
  407. .on('changeDate', (event) => {
  408. clearTimeout(event.currentTarget.timeoutId || 0)
  409. event.currentTarget.timeoutId = setTimeout(() => {
  410. that.onColumnSearch(event)
  411. }, that.options.searchTimeOut)
  412. })
  413. }
  414. })
  415. }
  416. if (that.options.sidePagination !== 'server') {
  417. header.find('[class*=\'bootstrap-table-filter-control\']').each((k, input) => {
  418. $(input).trigger('change')
  419. })
  420. }
  421. } else {
  422. header.find('.filterControl').hide()
  423. }
  424. },
  425. getDirectionOfSelectOptions (_alignment) {
  426. const alignment = _alignment === undefined ? 'left' : _alignment.toLowerCase()
  427. switch (alignment) {
  428. case 'left':
  429. return 'ltr'
  430. case 'right':
  431. return 'rtl'
  432. case 'auto':
  433. return 'auto'
  434. default:
  435. return 'ltr'
  436. }
  437. }
  438. }
  439. const filterDataMethods = {
  440. var (filterDataSource, selectControl, filterOrderBy, selected) {
  441. const variableValues = window[filterDataSource]
  442. // eslint-disable-next-line guard-for-in
  443. for (const key in variableValues) {
  444. UtilsFilterControl.addOptionToSelectControl(selectControl, key, variableValues[key], selected)
  445. }
  446. UtilsFilterControl.sortSelectControl(selectControl, filterOrderBy)
  447. },
  448. url (filterDataSource, selectControl, filterOrderBy, selected) {
  449. $.ajax({
  450. url: filterDataSource,
  451. dataType: 'json',
  452. success (data) {
  453. // eslint-disable-next-line guard-for-in
  454. for (const key in data) {
  455. UtilsFilterControl.addOptionToSelectControl(selectControl, key, data[key], selected)
  456. }
  457. UtilsFilterControl.sortSelectControl(selectControl, filterOrderBy)
  458. }
  459. })
  460. },
  461. json (filterDataSource, selectControl, filterOrderBy, selected) {
  462. const variableValues = JSON.parse(filterDataSource)
  463. // eslint-disable-next-line guard-for-in
  464. for (const key in variableValues) {
  465. UtilsFilterControl.addOptionToSelectControl(selectControl, key, variableValues[key], selected)
  466. }
  467. UtilsFilterControl.sortSelectControl(selectControl, filterOrderBy)
  468. }
  469. }
  470. $.extend($.fn.bootstrapTable.defaults, {
  471. filterControl: false,
  472. onColumnSearch (field, text) {
  473. return false
  474. },
  475. onCreatedControls () {
  476. return true
  477. },
  478. alignmentSelectControlOptions: undefined,
  479. filterTemplate: {
  480. input (that, field, isVisible, placeholder, value) {
  481. return Utils.sprintf(
  482. '<input type="text" class="form-control bootstrap-table-filter-control-%s" style="width: 100%; visibility: %s" placeholder="%s" value="%s">',
  483. field,
  484. isVisible,
  485. 'undefined' === typeof placeholder ? '' : placeholder,
  486. 'undefined' === typeof value ? '' : value
  487. )
  488. },
  489. select ({ options }, field, isVisible) {
  490. return Utils.sprintf(
  491. '<select class="form-control bootstrap-table-filter-control-%s" style="width: 100%; visibility: %s" dir="%s"></select>',
  492. field,
  493. isVisible,
  494. UtilsFilterControl.getDirectionOfSelectOptions(
  495. options.alignmentSelectControlOptions
  496. )
  497. )
  498. },
  499. datepicker (that, field, isVisible, value) {
  500. return Utils.sprintf(
  501. '<input type="text" class="form-control date-filter-control bootstrap-table-filter-control-%s" style="width: 100%; visibility: %s" value="%s">',
  502. field,
  503. isVisible,
  504. 'undefined' === typeof value ? '' : value
  505. )
  506. }
  507. },
  508. disableControlWhenSearch: false,
  509. searchOnEnterKey: false,
  510. // internal variables
  511. valuesFilterControl: []
  512. })
  513. $.extend($.fn.bootstrapTable.columnDefaults, {
  514. filterControl: undefined,
  515. filterDataCollector: undefined,
  516. filterData: undefined,
  517. filterDatepickerOptions: undefined,
  518. filterStrictSearch: false,
  519. filterStartsWithSearch: false,
  520. filterControlPlaceholder: '',
  521. filterDefault: '',
  522. filterOrderBy: 'asc' // asc || desc
  523. })
  524. $.extend($.fn.bootstrapTable.Constructor.EVENTS, {
  525. 'column-search.bs.table': 'onColumnSearch',
  526. 'created-controls.bs.table': 'onCreatedControls'
  527. })
  528. $.extend($.fn.bootstrapTable.defaults.icons, {
  529. clear: {
  530. bootstrap3: 'glyphicon-trash icon-clear'
  531. }[$.fn.bootstrapTable.theme] || 'fa-trash'
  532. })
  533. $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
  534. $.extend($.fn.bootstrapTable.defaults, {
  535. formatClearSearch () {
  536. return 'Clear filters'
  537. }
  538. })
  539. $.fn.bootstrapTable.methods.push('triggerSearch')
  540. $.fn.bootstrapTable.methods.push('clearFilterControl')
  541. $.BootstrapTable = class extends $.BootstrapTable {
  542. init () {
  543. // Make sure that the filterControl option is set
  544. if (this.options.filterControl) {
  545. const that = this
  546. // Make sure that the internal variables are set correctly
  547. this.options.valuesFilterControl = []
  548. this.$el
  549. .on('reset-view.bs.table', () => {
  550. // Create controls on $tableHeader if the height is set
  551. if (!that.options.height) {
  552. return
  553. }
  554. // Avoid recreate the controls
  555. if (
  556. that.$tableHeader.find('select').length > 0 ||
  557. that.$tableHeader.find('input').length > 0
  558. ) {
  559. return
  560. }
  561. UtilsFilterControl.createControls(that, that.$tableHeader)
  562. })
  563. .on('post-header.bs.table', () => {
  564. UtilsFilterControl.setValues(that)
  565. })
  566. .on('post-body.bs.table', () => {
  567. if (that.options.height) {
  568. UtilsFilterControl.fixHeaderCSS(that)
  569. }
  570. this.$tableLoading.css('top', this.$header.outerHeight() + 1)
  571. })
  572. .on('column-switch.bs.table', () => {
  573. UtilsFilterControl.setValues(that)
  574. })
  575. .on('load-success.bs.table', () => {
  576. that.EnableControls(true)
  577. })
  578. .on('load-error.bs.table', () => {
  579. that.EnableControls(true)
  580. })
  581. }
  582. super.init()
  583. }
  584. initHeader () {
  585. super.initHeader()
  586. if (!this.options.filterControl) {
  587. return
  588. }
  589. UtilsFilterControl.createControls(this, this.$header)
  590. }
  591. initBody () {
  592. super.initBody()
  593. UtilsFilterControl.initFilterSelectControls(this)
  594. }
  595. initSearch () {
  596. const that = this
  597. const fp = $.isEmptyObject(that.filterColumnsPartial)
  598. ? null
  599. : that.filterColumnsPartial
  600. if (fp === null || Object.keys(fp).length <= 1) {
  601. super.initSearch()
  602. }
  603. if (this.options.sidePagination === 'server') {
  604. return
  605. }
  606. if (fp === null) {
  607. return
  608. }
  609. // Check partial column filter
  610. that.data = fp
  611. ? that.options.data.filter((item, i) => {
  612. const itemIsExpected = []
  613. Object.keys(item).forEach((x, index) => {
  614. const key = that.header.fields[index]
  615. const thisColumn = that.columns[that.fieldsColumnsIndex[key]]
  616. const fval = (fp[key] || '').toLowerCase()
  617. let value = Utils.getItemField(item, key, false)
  618. if (fval === '') {
  619. itemIsExpected.push(true)
  620. } else {
  621. // Fix #142: search use formated data
  622. if (thisColumn && thisColumn.searchFormatter) {
  623. value = $.fn.bootstrapTable.utils.calculateObjectValue(
  624. that.header,
  625. that.header.formatters[$.inArray(key, that.header.fields)],
  626. [value, item, i],
  627. value
  628. )
  629. }
  630. if ($.inArray(key, that.header.fields) !== -1) {
  631. if (value === undefined || value === null) {
  632. itemIsExpected.push(false)
  633. } else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
  634. if (thisColumn.filterStrictSearch) {
  635. itemIsExpected.push(value.toString().toLowerCase() === fval.toString().toLowerCase())
  636. } else if (thisColumn.filterStartsWithSearch) {
  637. itemIsExpected.push((`${value}`).toLowerCase().indexOf(fval) === 0)
  638. } else {
  639. itemIsExpected.push((`${value}`).toLowerCase().includes(fval))
  640. }
  641. }
  642. }
  643. }
  644. })
  645. return !itemIsExpected.includes(false)
  646. })
  647. : that.data
  648. }
  649. initColumnSearch (filterColumnsDefaults) {
  650. UtilsFilterControl.copyValues(this)
  651. if (filterColumnsDefaults) {
  652. this.filterColumnsPartial = filterColumnsDefaults
  653. this.updatePagination()
  654. // eslint-disable-next-line guard-for-in
  655. for (const filter in filterColumnsDefaults) {
  656. this.trigger('column-search', filter, filterColumnsDefaults[filter])
  657. }
  658. }
  659. }
  660. onColumnSearch (event) {
  661. if ($.inArray(event.keyCode, [37, 38, 39, 40]) > -1) {
  662. return
  663. }
  664. UtilsFilterControl.copyValues(this)
  665. const text = $.trim($(event.currentTarget).val())
  666. const $field = $(event.currentTarget)
  667. .closest('[data-field]')
  668. .data('field')
  669. if ($.isEmptyObject(this.filterColumnsPartial)) {
  670. this.filterColumnsPartial = {}
  671. }
  672. if (text) {
  673. this.filterColumnsPartial[$field] = text
  674. } else {
  675. delete this.filterColumnsPartial[$field]
  676. }
  677. this.options.pageNumber = 1
  678. this.EnableControls(false)
  679. this.onSearch(event, false)
  680. this.trigger('column-search', $field, text)
  681. }
  682. initToolbar () {
  683. this.showSearchClearButton = this.options.filterControl && this.options.showSearchClearButton
  684. super.initToolbar()
  685. }
  686. resetSearch () {
  687. if (this.options.filterControl && this.options.showSearchClearButton) {
  688. this.clearFilterControl()
  689. }
  690. super.resetSearch()
  691. }
  692. clearFilterControl () {
  693. if (this.options.filterControl) {
  694. const that = this
  695. const cookies = UtilsFilterControl.collectBootstrapCookies()
  696. const header = UtilsFilterControl.getCurrentHeader(that)
  697. const table = header.closest('table')
  698. const controls = header.find(UtilsFilterControl.getCurrentSearchControls(that))
  699. const search = that.$toolbar.find('.search input')
  700. let hasValues = false
  701. let timeoutId = 0
  702. $.each(that.options.valuesFilterControl, (i, item) => {
  703. hasValues = hasValues ? true : item.value !== ''
  704. item.value = ''
  705. })
  706. $.each(that.options.filterControls, (i, item) => {
  707. item.text = ''
  708. })
  709. UtilsFilterControl.setValues(that)
  710. // clear cookies once the filters are clean
  711. clearTimeout(timeoutId)
  712. timeoutId = setTimeout(() => {
  713. if (cookies && cookies.length > 0) {
  714. $.each(cookies, (i, item) => {
  715. if (that.deleteCookie !== undefined) {
  716. that.deleteCookie(item)
  717. }
  718. })
  719. }
  720. }, that.options.searchTimeOut)
  721. // If there is not any value in the controls exit this method
  722. if (!hasValues) {
  723. return
  724. }
  725. // Clear each type of filter if it exists.
  726. // Requires the body to reload each time a type of filter is found because we never know
  727. // which ones are going to be present.
  728. if (controls.length > 0) {
  729. this.filterColumnsPartial = {}
  730. $(controls[0]).trigger(
  731. controls[0].tagName === 'INPUT' ? 'keyup' : 'change', { keyCode: 13 }
  732. )
  733. } else {
  734. return
  735. }
  736. if (search.length > 0) {
  737. that.resetSearch()
  738. }
  739. // use the default sort order if it exists. do nothing if it does not
  740. if (
  741. that.options.sortName !== table.data('sortName') ||
  742. that.options.sortOrder !== table.data('sortOrder')
  743. ) {
  744. const sorter = header.find(
  745. Utils.sprintf(
  746. '[data-field="%s"]',
  747. $(controls[0])
  748. .closest('table')
  749. .data('sortName')
  750. )
  751. )
  752. if (sorter.length > 0) {
  753. that.onSort({ type: 'keypress', currentTarget: sorter })
  754. $(sorter)
  755. .find('.sortable')
  756. .trigger('click')
  757. }
  758. }
  759. }
  760. }
  761. triggerSearch () {
  762. const header = UtilsFilterControl.getCurrentHeader(this)
  763. const searchControls = UtilsFilterControl.getCurrentSearchControls(this)
  764. header.find(searchControls).each(function () {
  765. const el = $(this)
  766. if (el.is('select')) {
  767. el.change()
  768. } else {
  769. el.keyup()
  770. }
  771. })
  772. }
  773. EnableControls (enable) {
  774. if (
  775. this.options.disableControlWhenSearch &&
  776. this.options.sidePagination === 'server'
  777. ) {
  778. const header = UtilsFilterControl.getCurrentHeader(this)
  779. const searchControls = UtilsFilterControl.getCurrentSearchControls(this)
  780. if (!enable) {
  781. header.find(searchControls).prop('disabled', 'disabled')
  782. } else {
  783. header.find(searchControls).removeProp('disabled')
  784. }
  785. }
  786. }
  787. }