bootstrap-table-filter-control.js 28 KB

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