/** * @author: Dennis Hernández * @webSite: http://djhvscf.github.io/Blog * @version: v2.1.0 */ (function ($) { 'use strict'; var sprintf = $.fn.bootstrapTable.utils.sprintf, objectKeys = $.fn.bootstrapTable.utils.objectKeys; var addOptionToSelectControl = function (selectControl, value, text) { value = $.trim(value); selectControl = $(selectControl.get(selectControl.length - 1)); if (!existOptionInSelectControl(selectControl, value)) { selectControl.append($("") .attr("value", value) .text($('
').html(text).text())); } }; var sortSelectControl = function (selectControl) { var $opts = selectControl.find('option:gt(0)'); $opts.sort(function (a, b) { a = $(a).text().toLowerCase(); b = $(b).text().toLowerCase(); if ($.isNumeric(a) && $.isNumeric(b)) { // Convert numerical values from string to float. a = parseFloat(a); b = parseFloat(b); } return a > b ? 1 : a < b ? -1 : 0; }); selectControl.find('option:gt(0)').remove(); selectControl.append($opts); }; var existOptionInSelectControl = function (selectControl, value) { var options = selectControl.get(selectControl.length - 1).options; for (var i = 0; i < options.length; i++) { if (options[i].value === value.toString()) { //The value is not valid to add return true; } } //If we get here, the value is valid to add return false; }; var fixHeaderCSS = function (that) { that.$tableHeader.css('height', '77px'); }; var getCurrentHeader = function (that) { var header = that.$header; if (that.options.height) { header = that.$tableHeader; } return header; }; var getCurrentSearchControls = function (that) { var searchControls = 'select, input'; if (that.options.height) { searchControls = 'table select, table input'; } return searchControls; }; var getCursorPosition = function(el) { if ($.fn.bootstrapTable.utils.isIEBrowser()) { if ($(el).is('input')) { var pos = 0; if ('selectionStart' in el) { pos = el.selectionStart; } else if ('selection' in document) { el.focus(); var Sel = document.selection.createRange(); var SelLength = document.selection.createRange().text.length; Sel.moveStart('character', -el.value.length); pos = Sel.text.length - SelLength; } return pos; } else { return -1; } } else { return -1; } }; var setCursorPosition = function (el, index) { if ($.fn.bootstrapTable.utils.isIEBrowser()) { if(el.setSelectionRange !== undefined) { el.setSelectionRange(index, index); } else { $(el).val(el.value); } } }; var copyValues = function (that) { var header = getCurrentHeader(that), searchControls = getCurrentSearchControls(that); that.options.valuesFilterControl = []; header.find(searchControls).each(function () { that.options.valuesFilterControl.push( { field: $(this).closest('[data-field]').data('field'), value: $(this).val(), position: getCursorPosition($(this).get(0)) }); }); }; var setValues = function(that) { var field = null, result = [], header = getCurrentHeader(that), searchControls = getCurrentSearchControls(that); if (that.options.valuesFilterControl.length > 0) { header.find(searchControls).each(function (index, ele) { field = $(this).closest('[data-field]').data('field'); result = $.grep(that.options.valuesFilterControl, function (valueObj) { return valueObj.field === field; }); if (result.length > 0) { $(this).val(result[0].value); setCursorPosition($(this).get(0), result[0].position); } }); } }; var collectBootstrapCookies = function cookiesRegex() { var cookies = [], foundCookies = document.cookie.match(/(?:bs.table.)(\w*)/g); if (foundCookies) { $.each(foundCookies, function (i, cookie) { if (/./.test(cookie)) { cookie = cookie.split(".").pop(); } if ($.inArray(cookie, cookies) === -1) { cookies.push(cookie); } }); return cookies; } }; var initFilterSelectControls = function (that) { var data = that.options.data, itemsPerPage = that.pageTo < that.options.data.length ? that.options.data.length : that.pageTo, isColumnSearchableViaSelect = function (column) { return column.filterControl && column.filterControl.toLowerCase() === 'select' && column.searchable; }, isFilterDataNotGiven = function (column) { return column.filterData === undefined || column.filterData.toLowerCase() === 'column'; }, hasSelectControlElement = function (selectControl) { return selectControl && selectControl.length > 0; }; var z = that.options.pagination ? (that.options.sidePagination === 'server' ? that.pageTo : that.options.totalRows) : that.pageTo; $.each(that.header.fields, function (j, field) { var column = that.columns[$.fn.bootstrapTable.utils.getFieldIndex(that.columns, field)], selectControl = $('.bootstrap-table-filter-control-' + escapeID(column.field)); if (isColumnSearchableViaSelect(column) && isFilterDataNotGiven(column) && hasSelectControlElement(selectControl)) { if (selectControl.get(selectControl.length - 1).options.length === 0) { //Added the default option addOptionToSelectControl(selectControl, '', ''); } var uniqueValues = {}; for (var i = 0; i < z; i++) { //Added a new value var fieldValue = data[i][field], formattedValue = $.fn.bootstrapTable.utils.calculateObjectValue(that.header, that.header.formatters[j], [fieldValue, data[i], i], fieldValue); uniqueValues[formattedValue] = fieldValue; } for (var key in uniqueValues) { addOptionToSelectControl(selectControl, uniqueValues[key], key); } sortSelectControl(selectControl); } }); }; var escapeID = function( id ) { return String(id).replace( /(:|\.|\[|\]|,)/g, "\\$1" ); }; var createControls = function (that, header) { var addedFilterControl = false, isVisible, html, timeoutId = 0; $.each(that.columns, function (i, column) { isVisible = 'hidden'; html = []; if (!column.visible) { return; } if (!column.filterControl) { html.push('
'); } else { html.push('
'); var nameControl = column.filterControl.toLowerCase(); if (column.searchable && that.options.filterTemplate[nameControl]) { addedFilterControl = true; isVisible = 'visible'; html.push(that.options.filterTemplate[nameControl](that, column.field, isVisible)); } } $.each(header.children().children(), function (i, tr) { tr = $(tr); if (tr.data('field') === column.field) { tr.find('.fht-cell').append(html.join('')); return false; } }); if (column.filterData !== undefined && column.filterData.toLowerCase() !== 'column') { var filterDataType = getFilterDataMethod(filterDataMethods, column.filterData.substring(0, column.filterData.indexOf(':'))); var filterDataSource, selectControl; if (filterDataType !== null) { filterDataSource = column.filterData.substring(column.filterData.indexOf(':') + 1, column.filterData.length); selectControl = $('.bootstrap-table-filter-control-' + escapeID(column.field)); addOptionToSelectControl(selectControl, '', ''); filterDataType(filterDataSource, selectControl); } else { throw new SyntaxError('Error. You should use any of these allowed filter data methods: var, json, url.' + ' Use like this: var: {key: "value"}'); } var variableValues, key; switch (filterDataType) { case 'url': $.ajax({ url: filterDataSource, dataType: 'json', success: function (data) { for (var key in data) { addOptionToSelectControl(selectControl, key, data[key]); } sortSelectControl(selectControl); } }); break; case 'var': variableValues = window[filterDataSource]; for (key in variableValues) { addOptionToSelectControl(selectControl, key, variableValues[key]); } sortSelectControl(selectControl); break; case 'jso': variableValues = JSON.parse(filterDataSource); for (key in variableValues) { addOptionToSelectControl(selectControl, key, variableValues[key]); } sortSelectControl(selectControl); break; } } }); if (addedFilterControl) { header.off('keyup', 'input').on('keyup', 'input', function (event) { clearTimeout(timeoutId); timeoutId = setTimeout(function () { that.onColumnSearch(event); }, that.options.searchTimeOut); }); header.off('change', 'select').on('change', 'select', function (event) { clearTimeout(timeoutId); timeoutId = setTimeout(function () { that.onColumnSearch(event); }, that.options.searchTimeOut); }); header.off('mouseup', 'input').on('mouseup', 'input', function (event) { var $input = $(this), oldValue = $input.val(); if (oldValue === "") { return; } setTimeout(function(){ var newValue = $input.val(); if (newValue === "") { clearTimeout(timeoutId); timeoutId = setTimeout(function () { that.onColumnSearch(event); }, that.options.searchTimeOut); } }, 1); }); if (header.find('.date-filter-control').length > 0) { $.each(that.columns, function (i, column) { if (column.filterControl !== undefined && column.filterControl.toLowerCase() === 'datepicker') { header.find('.date-filter-control.bootstrap-table-filter-control-' + column.field).datepicker(column.filterDatepickerOptions) .on('changeDate', function (e) { //Fired the keyup event $(e.currentTarget).keyup(); }); } }); } } else { header.find('.filterControl').hide(); } }; var getDirectionOfSelectOptions = function (alignment) { alignment = alignment === undefined ? 'left' : alignment.toLowerCase(); switch (alignment) { case 'left': return 'ltr'; case 'right': return 'rtl'; case 'auto': return 'auto'; default: return 'ltr'; } }; var filterDataMethods = { 'var': function (filterDataSource, selectControl) { var variableValues = window[filterDataSource]; for (var key in variableValues) { addOptionToSelectControl(selectControl, key, variableValues[key]); } sortSelectControl(selectControl); }, 'url': function (filterDataSource, selectControl) { $.ajax({ url: filterDataSource, dataType: 'json', success: function (data) { for (var key in data) { addOptionToSelectControl(selectControl, key, data[key]); } sortSelectControl(selectControl); } }); }, 'json':function (filterDataSource, selectControl) { var variableValues = JSON.parse(filterDataSource); for (var key in variableValues) { addOptionToSelectControl(selectControl, key, variableValues[key]); } sortSelectControl(selectControl); } }; var getFilterDataMethod = function (objFilterDataMethod, searchTerm) { var keys = Object.keys(objFilterDataMethod); for (var i = 0; i < keys.length; i++) { if (keys[i] === searchTerm) { return objFilterDataMethod[searchTerm]; } } return null; }; $.extend($.fn.bootstrapTable.defaults, { filterControl: false, onColumnSearch: function (field, text) { return false; }, filterShowClear: false, alignmentSelectControlOptions: undefined, filterTemplate: { input: function (that, field, isVisible) { return sprintf('', field, isVisible); }, select: function (that, field, isVisible) { return sprintf('', field, isVisible, getDirectionOfSelectOptions(that.options.alignmentSelectControlOptions)); }, datepicker: function (that, field, isVisible) { return sprintf('', field, isVisible); } }, //internal variables valuesFilterControl: [] }); $.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, { filterControl: undefined, filterData: undefined, filterDatepickerOptions: undefined, filterStrictSearch: false, filterStartsWithSearch: false }); $.extend($.fn.bootstrapTable.Constructor.EVENTS, { 'column-search.bs.table': 'onColumnSearch' }); $.extend($.fn.bootstrapTable.defaults.icons, { clear: 'glyphicon-trash icon-clear' }); $.extend($.fn.bootstrapTable.locales, { formatClearFilters: function () { return 'Clear Filters'; } }); $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales); var BootstrapTable = $.fn.bootstrapTable.Constructor, _init = BootstrapTable.prototype.init, _initToolbar = BootstrapTable.prototype.initToolbar, _initHeader = BootstrapTable.prototype.initHeader, _initBody = BootstrapTable.prototype.initBody, _initSearch = BootstrapTable.prototype.initSearch; BootstrapTable.prototype.init = function () { //Make sure that the filterControl option is set if (this.options.filterControl) { var that = this; // Compatibility: IE < 9 and old browsers if (!Object.keys) { objectKeys(); } //Make sure that the internal variables are set correctly this.options.valuesFilterControl = []; this.$el.on('reset-view.bs.table', function () { //Create controls on $tableHeader if the height is set if (!that.options.height) { return; } //Avoid recreate the controls if (that.$tableHeader.find('select').length > 0 || that.$tableHeader.find('input').length > 0) { return; } createControls(that, that.$tableHeader); }).on('post-header.bs.table', function () { setValues(that); }).on('post-body.bs.table', function () { if (that.options.height) { fixHeaderCSS(that); } }).on('column-switch.bs.table', function() { setValues(that); }); } _init.apply(this, Array.prototype.slice.apply(arguments)); }; BootstrapTable.prototype.initToolbar = function () { this.showToolbar = this.options.filterControl && this.options.filterShowClear; _initToolbar.apply(this, Array.prototype.slice.apply(arguments)); if (this.options.filterControl && this.options.filterShowClear) { var $btnGroup = this.$toolbar.find('>.btn-group'), $btnClear = $btnGroup.find('.filter-show-clear'); if (!$btnClear.length) { $btnClear = $([ '' ].join('')).appendTo($btnGroup); $btnClear.off('click').on('click', $.proxy(this.clearFilterControl, this)); } } }; BootstrapTable.prototype.initHeader = function () { _initHeader.apply(this, Array.prototype.slice.apply(arguments)); if (!this.options.filterControl) { return; } createControls(this, this.$header); }; BootstrapTable.prototype.initBody = function () { _initBody.apply(this, Array.prototype.slice.apply(arguments)); initFilterSelectControls(this); }; BootstrapTable.prototype.initSearch = function () { _initSearch.apply(this, Array.prototype.slice.apply(arguments)); if (this.options.sidePagination === 'server') { return; } var that = this; var fp = $.isEmptyObject(this.filterColumnsPartial) ? null : this.filterColumnsPartial; //Check partial column filter this.data = fp ? $.grep(this.data, function (item, i) { for (var key in fp) { var thisColumn = that.columns[$.fn.bootstrapTable.utils.getFieldIndex(that.columns, key)]; var fval = fp[key].toLowerCase(); var value = item[key]; // Fix #142: search use formated data if (thisColumn && thisColumn.searchFormatter) { value = $.fn.bootstrapTable.utils.calculateObjectValue(that.header, that.header.formatters[$.inArray(key, that.header.fields)], [value, item, i], value); } if (thisColumn.filterStrictSearch) { if (!($.inArray(key, that.header.fields) !== -1 && (typeof value === 'string' || typeof value === 'number') && value.toString().toLowerCase() === fval.toString().toLowerCase())) { return false; } } else if (thisColumn.filterStartsWithSearch) { if (!($.inArray(key, that.header.fields) !== -1 && (typeof value === 'string' || typeof value === 'number') && (value + '').toLowerCase().indexOf(fval) === 0)) { return false; } } else { if (!($.inArray(key, that.header.fields) !== -1 && (typeof value === 'string' || typeof value === 'number') && (value + '').toLowerCase().indexOf(fval) !== -1)) { return false; } } } return true; }) : this.data; }; BootstrapTable.prototype.initColumnSearch = function(filterColumnsDefaults) { copyValues(this); if (filterColumnsDefaults) { this.filterColumnsPartial = filterColumnsDefaults; this.updatePagination(); for (var filter in filterColumnsDefaults) { this.trigger('column-search', filter, filterColumnsDefaults[filter]); } } }; BootstrapTable.prototype.onColumnSearch = function (event) { if ($.inArray(event.keyCode, [37, 38, 39, 40]) > -1) { return; } copyValues(this); var text = $.trim($(event.currentTarget).val()); var $field = $(event.currentTarget).closest('[data-field]').data('field'); if ($.isEmptyObject(this.filterColumnsPartial)) { this.filterColumnsPartial = {}; } if (text) { this.filterColumnsPartial[$field] = text; } else { delete this.filterColumnsPartial[$field]; } // if the searchText is the same as the previously selected column value, // bootstrapTable will not try searching again (even though the selected column // may be different from the previous search). As a work around // we're manually appending some text to bootrap's searchText field // to guarantee that it will perform a search again when we call this.onSearch(event) this.searchText += "randomText"; this.options.pageNumber = 1; this.onSearch(event); this.trigger('column-search', $field, text); }; BootstrapTable.prototype.clearFilterControl = function () { if (this.options.filterControl && this.options.filterShowClear) { var that = this, cookies = collectBootstrapCookies(), header = getCurrentHeader(that), table = header.closest('table'), controls = header.find(getCurrentSearchControls(that)), search = that.$toolbar.find('.search input'), timeoutId = 0; $.each(that.options.valuesFilterControl, function (i, item) { item.value = ''; }); setValues(that); // Clear each type of filter if it exists. // Requires the body to reload each time a type of filter is found because we never know // which ones are going to be present. if (controls.length > 0) { this.filterColumnsPartial = {}; $(controls[0]).trigger(controls[0].tagName === 'INPUT' ? 'keyup' : 'change'); } else { return; } if (search.length > 0) { that.resetSearch(); } // use the default sort order if it exists. do nothing if it does not if (that.options.sortName !== table.data('sortName') || that.options.sortOrder !== table.data('sortOrder')) { var sorter = header.find(sprintf('[data-field="%s"]', $(controls[0]).closest('table').data('sortName'))); if (sorter.length > 0) { that.onSort(table.data('sortName'), table.data('sortName')); $(sorter).find('.sortable').trigger('click'); } } // clear cookies once the filters are clean clearTimeout(timeoutId); timeoutId = setTimeout(function () { if (cookies && cookies.length > 0) { $.each(cookies, function (i, item) { if (that.deleteCookie !== undefined) { that.deleteCookie(item); } }); } }, that.options.searchTimeOut); } }; })(jQuery);