bootstrap-table-filter-control.js 27 KB

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