bootstrap-table-filter-control.js 27 KB

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