bootstrap-table-filter-control.js 28 KB

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