bootstrap-table-filter-control.js 26 KB

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