2 * @author zhixin wen <wenzhixin2010@gmail.com>
4 * https://github.com/wenzhixin/bootstrap-table/
11 // ======================
13 var cachedWidth = null;
15 // it only does '%s', and return '' when arguments are undefined
16 var sprintf = function (str) {
21 str = str.replace(/%s/g, function () {
24 if (typeof arg === 'undefined') {
30 return flag ? str : '';
33 var getPropertyFromOther = function (list, from, to, value) {
35 $.each(list, function (i, item) {
36 if (item[from] === value) {
45 var getFieldIndex = function (columns, field) {
48 $.each(columns, function (i, column) {
49 if (column.field === field) {
58 // http://jsfiddle.net/wenyi/47nz7ez9/3/
59 var setFieldIndex = function (columns) {
64 for (i = 0; i < columns[0].length; i++) {
65 totalCol += columns[0][i].colspan || 1;
68 for (i = 0; i < columns.length; i++) {
70 for (j = 0; j < totalCol; j++) {
75 for (i = 0; i < columns.length; i++) {
76 for (j = 0; j < columns[i].length; j++) {
77 var r = columns[i][j],
78 rowspan = r.rowspan || 1,
79 colspan = r.colspan || 1,
80 index = $.inArray(false, flag[i]);
84 // when field is undefined, use index instead
85 if (typeof r.field === 'undefined') {
90 for (k = 0; k < rowspan; k++) {
91 flag[i + k][index] = true;
93 for (k = 0; k < colspan; k++) {
94 flag[i][index + k] = true;
100 var getScrollBarWidth = function () {
101 if (cachedWidth === null) {
102 var inner = $('<p/>').addClass('fixed-table-scroll-inner'),
103 outer = $('<div/>').addClass('fixed-table-scroll-outer'),
107 $('body').append(outer);
109 w1 = inner[0].offsetWidth;
110 outer.css('overflow', 'scroll');
111 w2 = inner[0].offsetWidth;
114 w2 = outer[0].clientWidth;
118 cachedWidth = w1 - w2;
123 var calculateObjectValue = function (self, name, args, defaultValue) {
126 if (typeof name === 'string') {
127 // support obj.func1.func2
128 var names = name.split('.');
130 if (names.length > 1) {
132 $.each(names, function (i, f) {
139 if (typeof func === 'object') {
142 if (typeof func === 'function') {
143 return func.apply(self, args);
145 if (!func && typeof name === 'string' && sprintf.apply(this, [name].concat(args))) {
146 return sprintf.apply(this, [name].concat(args));
151 var compareObjects = function (objectA, objectB, compareLength) {
152 // Create arrays of property names
153 var objectAProperties = Object.getOwnPropertyNames(objectA),
154 objectBProperties = Object.getOwnPropertyNames(objectB),
158 // If number of properties is different, objects are not equivalent
159 if (objectAProperties.length !== objectBProperties.length) {
164 for (var i = 0; i < objectAProperties.length; i++) {
165 propName = objectAProperties[i];
167 // If the property is not in the object B properties, continue with the next property
168 if ($.inArray(propName, objectBProperties) > -1) {
169 // If values of same property are not equal, objects are not equivalent
170 if (objectA[propName] !== objectB[propName]) {
176 // If we made it this far, objects are considered equivalent
180 var escapeHTML = function (text) {
181 if (typeof text === 'string') {
183 .replace(/&/g, '&')
184 .replace(/</g, '<')
185 .replace(/>/g, '>')
186 .replace(/"/g, '"')
187 .replace(/'/g, ''')
188 .replace(/`/g, '`');
193 var getRealDataAttr = function (dataAttr) {
194 for (var attr in dataAttr) {
195 var auxAttr = attr.split(/(?=[A-Z])/).join('-').toLowerCase();
196 if (auxAttr !== attr) {
197 dataAttr[auxAttr] = dataAttr[attr];
198 delete dataAttr[attr];
205 var getItemField = function (item, field, escape) {
208 if (typeof field !== 'string' || item.hasOwnProperty(field)) {
209 return escape ? escapeHTML(item[field]) : item[field];
211 var props = field.split('.');
212 for (var p in props) {
213 if (props.hasOwnProperty(p)) {
214 value = value && value[props[p]];
217 return escape ? escapeHTML(value) : value;
220 var isIEBrowser = function () {
221 return !!(navigator.userAgent.indexOf("MSIE ") > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./));
224 var objectKeys = function () {
225 // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
227 Object.keys = (function() {
228 var hasOwnProperty = Object.prototype.hasOwnProperty,
229 hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
236 'propertyIsEnumerable',
239 dontEnumsLength = dontEnums.length;
241 return function(obj) {
242 if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
243 throw new TypeError('Object.keys called on non-object');
246 var result = [], prop, i;
249 if (hasOwnProperty.call(obj, prop)) {
254 if (hasDontEnumBug) {
255 for (i = 0; i < dontEnumsLength; i++) {
256 if (hasOwnProperty.call(obj, dontEnums[i])) {
257 result.push(dontEnums[i]);
267 // BOOTSTRAP TABLE CLASS DEFINITION
268 // ======================
270 var BootstrapTable = function (el, options) {
271 this.options = options;
273 this.$el_ = this.$el.clone();
275 this.timeoutFooter_ = 0;
280 BootstrapTable.DEFAULTS = {
281 classes: 'table table-hover',
282 sortClass: undefined,
298 contentType: 'application/json',
301 queryParams: function (params) {
304 queryParamsType: 'limit', // undefined
305 responseHandler: function (res) {
309 onlyInfoPagination: false,
310 paginationLoop: true,
311 sidePagination: 'client', // client or server
312 totalRows: 0, // server side need to set
315 pageList: [10, 25, 50, 100],
316 paginationHAlign: 'right', //right, left
317 paginationVAlign: 'bottom', //bottom, top, both
318 paginationDetailHAlign: 'left', //right, left
319 paginationPreText: '‹',
320 paginationNextText: '›',
322 searchOnEnterKey: false,
324 searchAlign: 'right',
325 selectItemName: 'btSelectItem',
329 showPaginationSwitch: false,
332 buttonsAlign: 'right',
335 minimumCountColumns: 1,
340 detailFormatter: function (index, row) {
344 clickToSelect: false,
347 toolbarAlign: 'left',
348 checkboxHeader: true,
351 maintainSelected: false,
355 buttonsClass: 'default',
356 iconsPrefix: 'glyphicon', // glyphicon of fa (font awesome)
358 paginationSwitchDown: 'glyphicon-collapse-down icon-chevron-down',
359 paginationSwitchUp: 'glyphicon-collapse-up icon-chevron-up',
360 refresh: 'glyphicon-refresh icon-refresh',
361 toggle: 'glyphicon-list-alt icon-list-alt',
362 columns: 'glyphicon-th icon-th',
363 detailOpen: 'glyphicon-plus icon-plus',
364 detailClose: 'glyphicon-minus icon-minus'
367 customSearch: $.noop,
371 rowStyle: function (row, index) {
375 rowAttributes: function (row, index) {
379 footerStyle: function (row, index) {
383 onAll: function (name, args) {
386 onClickCell: function (field, value, row, $element) {
389 onDblClickCell: function (field, value, row, $element) {
392 onClickRow: function (item, $element) {
395 onDblClickRow: function (item, $element) {
398 onSort: function (name, order) {
401 onCheck: function (row) {
404 onUncheck: function (row) {
407 onCheckAll: function (rows) {
410 onUncheckAll: function (rows) {
413 onCheckSome: function (rows) {
416 onUncheckSome: function (rows) {
419 onLoadSuccess: function (data) {
422 onLoadError: function (status) {
425 onColumnSwitch: function (field, checked) {
428 onPageChange: function (number, size) {
431 onSearch: function (text) {
434 onToggle: function (cardView) {
437 onPreBody: function (data) {
440 onPostBody: function () {
443 onPostHeader: function () {
446 onExpandRow: function (index, row, $detail) {
449 onCollapseRow: function (index, row) {
452 onRefreshOptions: function (options) {
455 onRefresh: function (params) {
458 onResetView: function () {
463 BootstrapTable.LOCALES = {};
465 BootstrapTable.LOCALES['en-US'] = BootstrapTable.LOCALES.en = {
466 formatLoadingMessage: function () {
467 return 'Loading, please wait...';
469 formatRecordsPerPage: function (pageNumber) {
470 return sprintf('%s rows per page', pageNumber);
472 formatShowingRows: function (pageFrom, pageTo, totalRows) {
473 return sprintf('Showing %s to %s of %s rows', pageFrom, pageTo, totalRows);
475 formatDetailPagination: function (totalRows) {
476 return sprintf('Showing %s rows', totalRows);
478 formatSearch: function () {
481 formatNoMatches: function () {
482 return 'No matching records found';
484 formatPaginationSwitch: function () {
485 return 'Hide/Show pagination';
487 formatRefresh: function () {
490 formatToggle: function () {
493 formatColumns: function () {
496 formatAllRows: function () {
501 $.extend(BootstrapTable.DEFAULTS, BootstrapTable.LOCALES['en-US']);
503 BootstrapTable.COLUMN_DEFAULTS = {
506 checkboxEnabled: true,
509 titleTooltip: undefined,
511 align: undefined, // left, right, center
512 halign: undefined, // left, right, center
513 falign: undefined, // left, right, center
514 valign: undefined, // top, middle, bottom
517 order: 'asc', // asc, desc
521 formatter: undefined,
522 footerFormatter: undefined,
526 cellStyle: undefined,
528 searchFormatter: true,
532 BootstrapTable.EVENTS = {
533 'all.bs.table': 'onAll',
534 'click-cell.bs.table': 'onClickCell',
535 'dbl-click-cell.bs.table': 'onDblClickCell',
536 'click-row.bs.table': 'onClickRow',
537 'dbl-click-row.bs.table': 'onDblClickRow',
538 'sort.bs.table': 'onSort',
539 'check.bs.table': 'onCheck',
540 'uncheck.bs.table': 'onUncheck',
541 'check-all.bs.table': 'onCheckAll',
542 'uncheck-all.bs.table': 'onUncheckAll',
543 'check-some.bs.table': 'onCheckSome',
544 'uncheck-some.bs.table': 'onUncheckSome',
545 'load-success.bs.table': 'onLoadSuccess',
546 'load-error.bs.table': 'onLoadError',
547 'column-switch.bs.table': 'onColumnSwitch',
548 'page-change.bs.table': 'onPageChange',
549 'search.bs.table': 'onSearch',
550 'toggle.bs.table': 'onToggle',
551 'pre-body.bs.table': 'onPreBody',
552 'post-body.bs.table': 'onPostBody',
553 'post-header.bs.table': 'onPostHeader',
554 'expand-row.bs.table': 'onExpandRow',
555 'collapse-row.bs.table': 'onCollapseRow',
556 'refresh-options.bs.table': 'onRefreshOptions',
557 'reset-view.bs.table': 'onResetView',
558 'refresh.bs.table': 'onRefresh'
561 BootstrapTable.prototype.init = function () {
563 this.initContainer();
567 this.initHiddenRows();
570 this.initPagination();
572 this.initSearchText();
576 BootstrapTable.prototype.initLocale = function () {
577 if (this.options.locale) {
578 var parts = this.options.locale.split(/-|_/);
579 parts[0].toLowerCase();
580 if (parts[1]) parts[1].toUpperCase();
581 if ($.fn.bootstrapTable.locales[this.options.locale]) {
582 // locale as requested
583 $.extend(this.options, $.fn.bootstrapTable.locales[this.options.locale]);
584 } else if ($.fn.bootstrapTable.locales[parts.join('-')]) {
585 // locale with sep set to - (in case original was specified with _)
586 $.extend(this.options, $.fn.bootstrapTable.locales[parts.join('-')]);
587 } else if ($.fn.bootstrapTable.locales[parts[0]]) {
588 // short locale language code (i.e. 'en')
589 $.extend(this.options, $.fn.bootstrapTable.locales[parts[0]]);
594 BootstrapTable.prototype.initContainer = function () {
595 this.$container = $([
596 '<div class="bootstrap-table">',
597 '<div class="fixed-table-toolbar"></div>',
598 this.options.paginationVAlign === 'top' || this.options.paginationVAlign === 'both' ?
599 '<div class="fixed-table-pagination" style="clear: both;"></div>' :
601 '<div class="fixed-table-container">',
602 '<div class="fixed-table-header"><table></table></div>',
603 '<div class="fixed-table-body">',
604 '<div class="fixed-table-loading">',
605 this.options.formatLoadingMessage(),
608 '<div class="fixed-table-footer"><table><tr></tr></table></div>',
609 this.options.paginationVAlign === 'bottom' || this.options.paginationVAlign === 'both' ?
610 '<div class="fixed-table-pagination"></div>' :
616 this.$container.insertAfter(this.$el);
617 this.$tableContainer = this.$container.find('.fixed-table-container');
618 this.$tableHeader = this.$container.find('.fixed-table-header');
619 this.$tableBody = this.$container.find('.fixed-table-body');
620 this.$tableLoading = this.$container.find('.fixed-table-loading');
621 this.$tableFooter = this.$container.find('.fixed-table-footer');
622 this.$toolbar = this.$container.find('.fixed-table-toolbar');
623 this.$pagination = this.$container.find('.fixed-table-pagination');
625 this.$tableBody.append(this.$el);
626 this.$container.after('<div class="clearfix"></div>');
628 this.$el.addClass(this.options.classes);
629 if (this.options.striped) {
630 this.$el.addClass('table-striped');
632 if ($.inArray('table-no-bordered', this.options.classes.split(' ')) !== -1) {
633 this.$tableContainer.addClass('table-no-bordered');
637 BootstrapTable.prototype.initTable = function () {
642 this.$header = this.$el.find('>thead');
643 if (!this.$header.length) {
644 this.$header = $('<thead></thead>').appendTo(this.$el);
646 this.$header.find('tr').each(function () {
649 $(this).find('th').each(function () {
650 // Fix #2014 - getFieldIndex and elsewhere assume this is string, causes issues if not
651 if (typeof $(this).data('field') !== 'undefined') {
652 $(this).data('field', $(this).data('field') + '');
654 column.push($.extend({}, {
655 title: $(this).html(),
656 'class': $(this).attr('class'),
657 titleTooltip: $(this).attr('title'),
658 rowspan: $(this).attr('rowspan') ? +$(this).attr('rowspan') : undefined,
659 colspan: $(this).attr('colspan') ? +$(this).attr('colspan') : undefined
662 columns.push(column);
664 if (!$.isArray(this.options.columns[0])) {
665 this.options.columns = [this.options.columns];
667 this.options.columns = $.extend(true, [], columns, this.options.columns);
670 setFieldIndex(this.options.columns);
671 $.each(this.options.columns, function (i, columns) {
672 $.each(columns, function (j, column) {
673 column = $.extend({}, BootstrapTable.COLUMN_DEFAULTS, column);
675 if (typeof column.fieldIndex !== 'undefined') {
676 that.columns[column.fieldIndex] = column;
679 that.options.columns[i][j] = column;
683 // if options.data is setting, do not process tbody data
684 if (this.options.data.length) {
689 this.$el.find('>tbody>tr').each(function (y) {
692 // save tr's id, class and data-* attributes
693 row._id = $(this).attr('id');
694 row._class = $(this).attr('class');
695 row._data = getRealDataAttr($(this).data());
697 $(this).find('>td').each(function (x) {
699 cspan = +$this.attr('colspan') || 1,
700 rspan = +$this.attr('rowspan') || 1,
703 for (; m[y] && m[y][x]; x++); //skip already occupied cells in current row
705 for (tx = x; tx < x + cspan; tx++) { //mark matrix elements occupied by current cell with true
706 for (ty = y; ty < y + rspan; ty++) {
707 if (!m[ty]) { //fill missing rows
714 var field = that.columns[x].field;
716 row[field] = $(this).html();
717 // save td's id, class and data-* attributes
718 row['_' + field + '_id'] = $(this).attr('id');
719 row['_' + field + '_class'] = $(this).attr('class');
720 row['_' + field + '_rowspan'] = $(this).attr('rowspan');
721 row['_' + field + '_colspan'] = $(this).attr('colspan');
722 row['_' + field + '_title'] = $(this).attr('title');
723 row['_' + field + '_data'] = getRealDataAttr($(this).data());
727 this.options.data = data;
728 if (data.length) this.fromHtml = true;
731 BootstrapTable.prototype.initHeader = function () {
748 $.each(this.options.columns, function (i, columns) {
751 if (i === 0 && !that.options.cardView && that.options.detailView) {
752 html.push(sprintf('<th class="detail" rowspan="%s"><div class="fht-cell"></div></th>',
753 that.options.columns.length));
756 $.each(columns, function (j, column) {
758 halign = '', // header align style
759 align = '', // body align style
761 class_ = sprintf(' class="%s"', column['class']),
762 order = that.options.sortOrder || column.order,
764 width = column.width;
766 if (column.width !== undefined && (!that.options.cardView)) {
767 if (typeof column.width === 'string') {
768 if (column.width.indexOf('%') !== -1) {
773 if (column.width && typeof column.width === 'string') {
774 width = column.width.replace('%', '').replace('px', '');
777 halign = sprintf('text-align: %s; ', column.halign ? column.halign : column.align);
778 align = sprintf('text-align: %s; ', column.align);
779 style = sprintf('vertical-align: %s; ', column.valign);
780 style += sprintf('width: %s; ', (column.checkbox || column.radio) && !width ?
781 '36px' : (width ? width + unitWidth : undefined));
783 if (typeof column.fieldIndex !== 'undefined') {
784 that.header.fields[column.fieldIndex] = column.field;
785 that.header.styles[column.fieldIndex] = align + style;
786 that.header.classes[column.fieldIndex] = class_;
787 that.header.formatters[column.fieldIndex] = column.formatter;
788 that.header.events[column.fieldIndex] = column.events;
789 that.header.sorters[column.fieldIndex] = column.sorter;
790 that.header.sortNames[column.fieldIndex] = column.sortName;
791 that.header.cellStyles[column.fieldIndex] = column.cellStyle;
792 that.header.searchables[column.fieldIndex] = column.searchable;
794 if (!column.visible) {
798 if (that.options.cardView && (!column.cardVisible)) {
802 visibleColumns[column.field] = column;
805 html.push('<th' + sprintf(' title="%s"', column.titleTooltip),
806 column.checkbox || column.radio ?
807 sprintf(' class="bs-checkbox %s"', column['class'] || '') :
809 sprintf(' style="%s"', halign + style),
810 sprintf(' rowspan="%s"', column.rowspan),
811 sprintf(' colspan="%s"', column.colspan),
812 sprintf(' data-field="%s"', column.field),
816 html.push(sprintf('<div class="th-inner %s">', that.options.sortable && column.sortable ?
817 'sortable both' : ''));
821 if (column.checkbox) {
822 if (!that.options.singleSelect && that.options.checkboxHeader) {
823 text = '<input name="btSelectAll" type="checkbox" />';
825 that.header.stateField = column.field;
829 that.header.stateField = column.field;
830 that.options.singleSelect = true;
835 html.push('<div class="fht-cell"></div>');
842 this.$header.html(html.join(''));
843 this.$header.find('th[data-field]').each(function (i) {
844 $(this).data(visibleColumns[$(this).data('field')]);
846 this.$container.off('click', '.th-inner').on('click', '.th-inner', function (event) {
847 var target = $(this);
849 if (that.options.detailView) {
850 if (target.closest('.bootstrap-table')[0] !== that.$container[0])
854 if (that.options.sortable && target.parent().data().sortable) {
859 this.$header.children().children().off('keypress').on('keypress', function (event) {
860 if (that.options.sortable && $(this).data().sortable) {
861 var code = event.keyCode || event.which;
862 if (code == 13) { //Enter keycode
868 $(window).off('resize.bootstrap-table');
869 if (!this.options.showHeader || this.options.cardView) {
871 this.$tableHeader.hide();
872 this.$tableLoading.css('top', 0);
875 this.$tableHeader.show();
876 this.$tableLoading.css('top', this.$header.outerHeight() + 1);
877 // Assign the correct sortable arrow
879 $(window).on('resize.bootstrap-table', $.proxy(this.resetWidth, this));
882 this.$selectAll = this.$header.find('[name="btSelectAll"]');
883 this.$selectAll.off('click').on('click', function () {
884 var checked = $(this).prop('checked');
885 that[checked ? 'checkAll' : 'uncheckAll']();
886 that.updateSelected();
890 BootstrapTable.prototype.initFooter = function () {
891 if (!this.options.showFooter || this.options.cardView) {
892 this.$tableFooter.hide();
894 this.$tableFooter.show();
900 * @param type: append / prepend
902 BootstrapTable.prototype.initData = function (data, type) {
903 if (type === 'append') {
904 this.data = this.data.concat(data);
905 } else if (type === 'prepend') {
906 this.data = [].concat(data).concat(this.data);
908 this.data = data || this.options.data;
911 // Fix #839 Records deleted when adding new row on filtered table
912 if (type === 'append') {
913 this.options.data = this.options.data.concat(data);
914 } else if (type === 'prepend') {
915 this.options.data = [].concat(data).concat(this.options.data);
917 this.options.data = this.data;
920 if (this.options.sidePagination === 'server') {
926 BootstrapTable.prototype.initSort = function () {
928 name = this.options.sortName,
929 order = this.options.sortOrder === 'desc' ? -1 : 1,
930 index = $.inArray(this.options.sortName, this.header.fields),
933 if (this.options.customSort !== $.noop) {
934 this.options.customSort.apply(this, [this.options.sortName, this.options.sortOrder]);
939 if (this.options.sortStable) {
940 $.each(this.data, function (i, row) {
941 if (!row.hasOwnProperty('_position')) row._position = i;
945 this.data.sort(function (a, b) {
946 if (that.header.sortNames[index]) {
947 name = that.header.sortNames[index];
949 var aa = getItemField(a, name, that.options.escape),
950 bb = getItemField(b, name, that.options.escape),
951 value = calculateObjectValue(that.header, that.header.sorters[index], [aa, bb]);
953 if (value !== undefined) {
954 return order * value;
957 // Fix #161: undefined or null string sort bug.
958 if (aa === undefined || aa === null) {
961 if (bb === undefined || bb === null) {
965 if (that.options.sortStable && aa === bb) {
970 // IF both values are numeric, do a numeric comparison
971 if ($.isNumeric(aa) && $.isNumeric(bb)) {
972 // Convert numerical values form string to float.
985 // If value is not a string, convert to string
986 if (typeof aa !== 'string') {
990 if (aa.localeCompare(bb) === -1) {
997 if (this.options.sortClass !== undefined) {
998 clearTimeout(timeoutId);
999 timeoutId = setTimeout(function () {
1000 that.$el.removeClass(that.options.sortClass);
1001 var index = that.$header.find(sprintf('[data-field="%s"]',
1002 that.options.sortName).index() + 1);
1003 that.$el.find(sprintf('tr td:nth-child(%s)', index))
1004 .addClass(that.options.sortClass);
1010 BootstrapTable.prototype.onSort = function (event) {
1011 var $this = event.type === "keypress" ? $(event.currentTarget) : $(event.currentTarget).parent(),
1012 $this_ = this.$header.find('th').eq($this.index());
1014 this.$header.add(this.$header_).find('span.order').remove();
1016 if (this.options.sortName === $this.data('field')) {
1017 this.options.sortOrder = this.options.sortOrder === 'asc' ? 'desc' : 'asc';
1019 this.options.sortName = $this.data('field');
1020 this.options.sortOrder = $this.data('order') === 'asc' ? 'desc' : 'asc';
1022 this.trigger('sort', this.options.sortName, this.options.sortOrder);
1024 $this.add($this_).data('order', this.options.sortOrder);
1026 // Assign the correct sortable arrow
1029 if (this.options.sidePagination === 'server') {
1030 this.initServer(this.options.silentSort);
1038 BootstrapTable.prototype.initToolbar = function () {
1044 switchableCount = 0;
1046 if (this.$toolbar.find('.bs-bars').children().length) {
1047 $('body').append($(this.options.toolbar));
1049 this.$toolbar.html('');
1051 if (typeof this.options.toolbar === 'string' || typeof this.options.toolbar === 'object') {
1052 $(sprintf('<div class="bs-bars pull-%s"></div>', this.options.toolbarAlign))
1053 .appendTo(this.$toolbar)
1054 .append($(this.options.toolbar));
1057 // showColumns, showToggle, showRefresh
1058 html = [sprintf('<div class="columns columns-%s btn-group pull-%s">',
1059 this.options.buttonsAlign, this.options.buttonsAlign)];
1061 if (typeof this.options.icons === 'string') {
1062 this.options.icons = calculateObjectValue(null, this.options.icons);
1065 if (this.options.showPaginationSwitch) {
1066 html.push(sprintf('<button class="btn' +
1067 sprintf(' btn-%s', this.options.buttonsClass) +
1068 sprintf(' btn-%s', this.options.iconSize) +
1069 '" type="button" name="paginationSwitch" title="%s">',
1070 this.options.formatPaginationSwitch()),
1071 sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.paginationSwitchDown),
1075 if (this.options.showRefresh) {
1076 html.push(sprintf('<button class="btn' +
1077 sprintf(' btn-%s', this.options.buttonsClass) +
1078 sprintf(' btn-%s', this.options.iconSize) +
1079 '" type="button" name="refresh" title="%s">',
1080 this.options.formatRefresh()),
1081 sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.refresh),
1085 if (this.options.showToggle) {
1086 html.push(sprintf('<button class="btn' +
1087 sprintf(' btn-%s', this.options.buttonsClass) +
1088 sprintf(' btn-%s', this.options.iconSize) +
1089 '" type="button" name="toggle" title="%s">',
1090 this.options.formatToggle()),
1091 sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.toggle),
1095 if (this.options.showColumns) {
1096 html.push(sprintf('<div class="keep-open btn-group" title="%s">',
1097 this.options.formatColumns()),
1098 '<button type="button" class="btn' +
1099 sprintf(' btn-%s', this.options.buttonsClass) +
1100 sprintf(' btn-%s', this.options.iconSize) +
1101 ' dropdown-toggle" data-toggle="dropdown">',
1102 sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.columns),
1103 ' <span class="caret"></span>',
1105 '<ul class="dropdown-menu" role="menu">');
1107 $.each(this.columns, function (i, column) {
1108 if (column.radio || column.checkbox) {
1112 if (that.options.cardView && !column.cardVisible) {
1116 var checked = column.visible ? ' checked="checked"' : '';
1118 if (column.switchable) {
1119 html.push(sprintf('<li>' +
1120 '<label><input type="checkbox" data-field="%s" value="%s"%s> %s</label>' +
1121 '</li>', column.field, i, checked, column.title));
1129 html.push('</div>');
1131 // Fix #188: this.showToolbar is for extensions
1132 if (this.showToolbar || html.length > 2) {
1133 this.$toolbar.append(html.join(''));
1136 if (this.options.showPaginationSwitch) {
1137 this.$toolbar.find('button[name="paginationSwitch"]')
1138 .off('click').on('click', $.proxy(this.togglePagination, this));
1141 if (this.options.showRefresh) {
1142 this.$toolbar.find('button[name="refresh"]')
1143 .off('click').on('click', $.proxy(this.refresh, this));
1146 if (this.options.showToggle) {
1147 this.$toolbar.find('button[name="toggle"]')
1148 .off('click').on('click', function () {
1153 if (this.options.showColumns) {
1154 $keepOpen = this.$toolbar.find('.keep-open');
1156 if (switchableCount <= this.options.minimumCountColumns) {
1157 $keepOpen.find('input').prop('disabled', true);
1160 $keepOpen.find('li').off('click').on('click', function (event) {
1161 event.stopImmediatePropagation();
1163 $keepOpen.find('input').off('click').on('click', function () {
1164 var $this = $(this);
1166 that.toggleColumn($(this).val(), $this.prop('checked'), false);
1167 that.trigger('column-switch', $(this).data('field'), $this.prop('checked'));
1171 if (this.options.search) {
1174 '<div class="pull-' + this.options.searchAlign + ' search">',
1175 sprintf('<input class="form-control' +
1176 sprintf(' input-%s', this.options.iconSize) +
1177 '" type="text" placeholder="%s">',
1178 this.options.formatSearch()),
1181 this.$toolbar.append(html.join(''));
1182 $search = this.$toolbar.find('.search input');
1183 $search.off('keyup drop blur').on('keyup drop blur', function (event) {
1184 if (that.options.searchOnEnterKey && event.keyCode !== 13) {
1188 if ($.inArray(event.keyCode, [37, 38, 39, 40]) > -1) {
1192 clearTimeout(timeoutId); // doesn't matter if it's 0
1193 timeoutId = setTimeout(function () {
1194 that.onSearch(event);
1195 }, that.options.searchTimeOut);
1198 if (isIEBrowser()) {
1199 $search.off('mouseup').on('mouseup', function (event) {
1200 clearTimeout(timeoutId); // doesn't matter if it's 0
1201 timeoutId = setTimeout(function () {
1202 that.onSearch(event);
1203 }, that.options.searchTimeOut);
1209 BootstrapTable.prototype.onSearch = function (event) {
1210 var text = $.trim($(event.currentTarget).val());
1212 // trim search input
1213 if (this.options.trimOnSearch && $(event.currentTarget).val() !== text) {
1214 $(event.currentTarget).val(text);
1217 if (text === this.searchText) {
1220 this.searchText = text;
1221 this.options.searchText = text;
1223 this.options.pageNumber = 1;
1225 this.updatePagination();
1226 this.trigger('search', text);
1229 BootstrapTable.prototype.initSearch = function () {
1232 if (this.options.sidePagination !== 'server') {
1233 if (this.options.customSearch !== $.noop) {
1234 this.options.customSearch.apply(this, [this.searchText]);
1238 var s = this.searchText && (this.options.escape ?
1239 escapeHTML(this.searchText) : this.searchText).toLowerCase();
1240 var f = $.isEmptyObject(this.filterColumns) ? null : this.filterColumns;
1243 this.data = f ? $.grep(this.options.data, function (item, i) {
1244 for (var key in f) {
1245 if ($.isArray(f[key]) && $.inArray(item[key], f[key]) === -1 ||
1246 !$.isArray(f[key]) && item[key] !== f[key]) {
1251 }) : this.options.data;
1253 this.data = s ? $.grep(this.data, function (item, i) {
1254 for (var j = 0; j < that.header.fields.length; j++) {
1256 if (!that.header.searchables[j]) {
1260 var key = $.isNumeric(that.header.fields[j]) ? parseInt(that.header.fields[j], 10) : that.header.fields[j];
1261 var column = that.columns[getFieldIndex(that.columns, key)];
1264 if (typeof key === 'string') {
1266 var props = key.split('.');
1267 for (var prop_index = 0; prop_index < props.length; prop_index++) {
1268 value = value[props[prop_index]];
1271 // Fix #142: respect searchForamtter boolean
1272 if (column && column.searchFormatter) {
1273 value = calculateObjectValue(column,
1274 that.header.formatters[j], [value, item, i], value);
1280 if (typeof value === 'string' || typeof value === 'number') {
1281 if (that.options.strictSearch) {
1282 if ((value + '').toLowerCase() === s) {
1286 if ((value + '').toLowerCase().indexOf(s) !== -1) {
1297 BootstrapTable.prototype.initPagination = function () {
1298 if (!this.options.pagination) {
1299 this.$pagination.hide();
1302 this.$pagination.show();
1307 $allSelected = false,
1313 data = this.getData(),
1314 pageList = this.options.pageList;
1316 if (this.options.sidePagination !== 'server') {
1317 this.options.totalRows = data.length;
1320 this.totalPages = 0;
1321 if (this.options.totalRows) {
1322 if (this.options.pageSize === this.options.formatAllRows()) {
1323 this.options.pageSize = this.options.totalRows;
1324 $allSelected = true;
1325 } else if (this.options.pageSize === this.options.totalRows) {
1326 // Fix #667 Table with pagination,
1327 // multiple pages and a search that matches to one page throws exception
1328 var pageLst = typeof this.options.pageList === 'string' ?
1329 this.options.pageList.replace('[', '').replace(']', '')
1330 .replace(/ /g, '').toLowerCase().split(',') : this.options.pageList;
1331 if ($.inArray(this.options.formatAllRows().toLowerCase(), pageLst) > -1) {
1332 $allSelected = true;
1336 this.totalPages = ~~((this.options.totalRows - 1) / this.options.pageSize) + 1;
1338 this.options.totalPages = this.totalPages;
1340 if (this.totalPages > 0 && this.options.pageNumber > this.totalPages) {
1341 this.options.pageNumber = this.totalPages;
1344 this.pageFrom = (this.options.pageNumber - 1) * this.options.pageSize + 1;
1345 this.pageTo = this.options.pageNumber * this.options.pageSize;
1346 if (this.pageTo > this.options.totalRows) {
1347 this.pageTo = this.options.totalRows;
1351 '<div class="pull-' + this.options.paginationDetailHAlign + ' pagination-detail">',
1352 '<span class="pagination-info">',
1353 this.options.onlyInfoPagination ? this.options.formatDetailPagination(this.options.totalRows) :
1354 this.options.formatShowingRows(this.pageFrom, this.pageTo, this.options.totalRows),
1357 if (!this.options.onlyInfoPagination) {
1358 html.push('<span class="page-list">');
1361 sprintf('<span class="btn-group %s">',
1362 this.options.paginationVAlign === 'top' || this.options.paginationVAlign === 'both' ?
1363 'dropdown' : 'dropup'),
1364 '<button type="button" class="btn' +
1365 sprintf(' btn-%s', this.options.buttonsClass) +
1366 sprintf(' btn-%s', this.options.iconSize) +
1367 ' dropdown-toggle" data-toggle="dropdown">',
1368 '<span class="page-size">',
1369 $allSelected ? this.options.formatAllRows() : this.options.pageSize,
1371 ' <span class="caret"></span>',
1373 '<ul class="dropdown-menu" role="menu">'
1376 if (typeof this.options.pageList === 'string') {
1377 var list = this.options.pageList.replace('[', '').replace(']', '')
1378 .replace(/ /g, '').split(',');
1381 $.each(list, function (i, value) {
1382 pageList.push(value.toUpperCase() === that.options.formatAllRows().toUpperCase() ?
1383 that.options.formatAllRows() : +value);
1387 $.each(pageList, function (i, page) {
1388 if (!that.options.smartDisplay || i === 0 || pageList[i - 1] < that.options.totalRows) {
1391 active = page === that.options.formatAllRows() ? ' class="active"' : '';
1393 active = page === that.options.pageSize ? ' class="active"' : '';
1395 pageNumber.push(sprintf('<li%s><a href="javascript:void(0)">%s</a></li>', active, page));
1398 pageNumber.push('</ul></span>');
1400 html.push(this.options.formatRecordsPerPage(pageNumber.join('')));
1401 html.push('</span>');
1404 '<div class="pull-' + this.options.paginationHAlign + ' pagination">',
1405 '<ul class="pagination' + sprintf(' pagination-%s', this.options.iconSize) + '">',
1406 '<li class="page-pre"><a href="javascript:void(0)">' + this.options.paginationPreText + '</a></li>');
1408 if (this.totalPages < 5) {
1410 to = this.totalPages;
1412 from = this.options.pageNumber - 2;
1418 if (to > this.totalPages) {
1419 to = this.totalPages;
1424 if (this.totalPages >= 6) {
1425 if (this.options.pageNumber >= 3) {
1426 html.push('<li class="page-first' + (1 === this.options.pageNumber ? ' active' : '') + '">',
1427 '<a href="javascript:void(0)">', 1, '</a>',
1433 if (this.options.pageNumber >= 4) {
1434 if (this.options.pageNumber == 4 || this.totalPages == 6 || this.totalPages == 7) {
1437 html.push('<li class="page-first-separator disabled">',
1438 '<a href="javascript:void(0)">...</a>',
1446 if (this.totalPages >= 7) {
1447 if (this.options.pageNumber >= (this.totalPages - 2)) {
1452 if (this.totalPages == 6) {
1453 if (this.options.pageNumber >= (this.totalPages - 2)) {
1456 } else if (this.totalPages >= 7) {
1457 if (this.totalPages == 7 || this.options.pageNumber >= (this.totalPages - 3)) {
1462 for (i = from; i <= to; i++) {
1463 html.push('<li class="page-number' + (i === this.options.pageNumber ? ' active' : '') + '">',
1464 '<a href="javascript:void(0)">', i, '</a>',
1468 if (this.totalPages >= 8) {
1469 if (this.options.pageNumber <= (this.totalPages - 4)) {
1470 html.push('<li class="page-last-separator disabled">',
1471 '<a href="javascript:void(0)">...</a>',
1476 if (this.totalPages >= 6) {
1477 if (this.options.pageNumber <= (this.totalPages - 3)) {
1478 html.push('<li class="page-last' + (this.totalPages === this.options.pageNumber ? ' active' : '') + '">',
1479 '<a href="javascript:void(0)">', this.totalPages, '</a>',
1485 '<li class="page-next"><a href="javascript:void(0)">' + this.options.paginationNextText + '</a></li>',
1489 this.$pagination.html(html.join(''));
1491 if (!this.options.onlyInfoPagination) {
1492 $pageList = this.$pagination.find('.page-list a');
1493 $first = this.$pagination.find('.page-first');
1494 $pre = this.$pagination.find('.page-pre');
1495 $next = this.$pagination.find('.page-next');
1496 $last = this.$pagination.find('.page-last');
1497 $number = this.$pagination.find('.page-number');
1499 if (this.options.smartDisplay) {
1500 if (this.totalPages <= 1) {
1501 this.$pagination.find('div.pagination').hide();
1503 if (pageList.length < 2 || this.options.totalRows <= pageList[0]) {
1504 this.$pagination.find('span.page-list').hide();
1507 // when data is empty, hide the pagination
1508 this.$pagination[this.getData().length ? 'show' : 'hide']();
1511 if (!this.options.paginationLoop) {
1512 if (this.options.pageNumber === 1) {
1513 $pre.addClass('disabled');
1515 if (this.options.pageNumber === this.totalPages) {
1516 $next.addClass('disabled');
1521 this.options.pageSize = this.options.formatAllRows();
1523 $pageList.off('click').on('click', $.proxy(this.onPageListChange, this));
1524 $first.off('click').on('click', $.proxy(this.onPageFirst, this));
1525 $pre.off('click').on('click', $.proxy(this.onPagePre, this));
1526 $next.off('click').on('click', $.proxy(this.onPageNext, this));
1527 $last.off('click').on('click', $.proxy(this.onPageLast, this));
1528 $number.off('click').on('click', $.proxy(this.onPageNumber, this));
1532 BootstrapTable.prototype.updatePagination = function (event) {
1533 // Fix #171: IE disabled button can be clicked bug.
1534 if (event && $(event.currentTarget).hasClass('disabled')) {
1538 if (!this.options.maintainSelected) {
1542 this.initPagination();
1543 if (this.options.sidePagination === 'server') {
1549 this.trigger('page-change', this.options.pageNumber, this.options.pageSize);
1552 BootstrapTable.prototype.onPageListChange = function (event) {
1553 var $this = $(event.currentTarget);
1555 $this.parent().addClass('active').siblings().removeClass('active');
1556 this.options.pageSize = $this.text().toUpperCase() === this.options.formatAllRows().toUpperCase() ?
1557 this.options.formatAllRows() : +$this.text();
1558 this.$toolbar.find('.page-size').text(this.options.pageSize);
1560 this.updatePagination(event);
1563 BootstrapTable.prototype.onPageFirst = function (event) {
1564 this.options.pageNumber = 1;
1565 this.updatePagination(event);
1568 BootstrapTable.prototype.onPagePre = function (event) {
1569 if ((this.options.pageNumber - 1) === 0) {
1570 this.options.pageNumber = this.options.totalPages;
1572 this.options.pageNumber--;
1574 this.updatePagination(event);
1577 BootstrapTable.prototype.onPageNext = function (event) {
1578 if ((this.options.pageNumber + 1) > this.options.totalPages) {
1579 this.options.pageNumber = 1;
1581 this.options.pageNumber++;
1583 this.updatePagination(event);
1586 BootstrapTable.prototype.onPageLast = function (event) {
1587 this.options.pageNumber = this.totalPages;
1588 this.updatePagination(event);
1591 BootstrapTable.prototype.onPageNumber = function (event) {
1592 if (this.options.pageNumber === +$(event.currentTarget).text()) {
1595 this.options.pageNumber = +$(event.currentTarget).text();
1596 this.updatePagination(event);
1599 BootstrapTable.prototype.initBody = function (fixedScroll) {
1602 data = this.getData();
1604 this.trigger('pre-body', data);
1606 this.$body = this.$el.find('>tbody');
1607 if (!this.$body.length) {
1608 this.$body = $('<tbody></tbody>').appendTo(this.$el);
1611 //Fix #389 Bootstrap-table-flatJSON is not working
1613 if (!this.options.pagination || this.options.sidePagination === 'server') {
1615 this.pageTo = data.length;
1618 for (var i = this.pageFrom - 1; i < this.pageTo; i++) {
1625 htmlAttributes = [];
1627 if ($.inArray(item, this.hiddenRows) > -1) {
1631 style = calculateObjectValue(this.options, this.options.rowStyle, [item, i], style);
1633 if (style && style.css) {
1634 for (key in style.css) {
1635 csses.push(key + ': ' + style.css[key]);
1639 attributes = calculateObjectValue(this.options,
1640 this.options.rowAttributes, [item, i], attributes);
1643 for (key in attributes) {
1644 htmlAttributes.push(sprintf('%s="%s"', key, escapeHTML(attributes[key])));
1648 if (item._data && !$.isEmptyObject(item._data)) {
1649 $.each(item._data, function (k, v) {
1650 // ignore data-index
1651 if (k === 'index') {
1654 data_ += sprintf(' data-%s="%s"', k, v);
1659 sprintf(' %s', htmlAttributes.join(' ')),
1660 sprintf(' id="%s"', $.isArray(item) ? undefined : item._id),
1661 sprintf(' class="%s"', style.classes || ($.isArray(item) ? undefined : item._class)),
1662 sprintf(' data-index="%s"', i),
1663 sprintf(' data-uniqueid="%s"', item[this.options.uniqueId]),
1664 sprintf('%s', data_),
1668 if (this.options.cardView) {
1669 html.push(sprintf('<td colspan="%s"><div class="card-views">', this.header.fields.length));
1672 if (!this.options.cardView && this.options.detailView) {
1674 '<a class="detail-icon" href="javascript:">',
1675 sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.detailOpen),
1680 $.each(this.header.fields, function (j, field) {
1682 value_ = getItemField(item, field, that.options.escape),
1687 class_ = that.header.classes[j],
1692 column = that.columns[j];
1694 if (that.fromHtml && typeof value_ === 'undefined') {
1698 if (!column.visible) {
1702 if (that.options.cardView && !column.cardVisible) {
1706 style = sprintf('style="%s"', csses.concat(that.header.styles[j]).join('; '));
1708 // handle td's id and class
1709 if (item['_' + field + '_id']) {
1710 id_ = sprintf(' id="%s"', item['_' + field + '_id']);
1712 if (item['_' + field + '_class']) {
1713 class_ = sprintf(' class="%s"', item['_' + field + '_class']);
1715 if (item['_' + field + '_rowspan']) {
1716 rowspan_ = sprintf(' rowspan="%s"', item['_' + field + '_rowspan']);
1718 if (item['_' + field + '_colspan']) {
1719 colspan_ = sprintf(' colspan="%s"', item['_' + field + '_colspan']);
1721 if (item['_' + field + '_title']) {
1722 title_ = sprintf(' title="%s"', item['_' + field + '_title']);
1724 cellStyle = calculateObjectValue(that.header,
1725 that.header.cellStyles[j], [value_, item, i, field], cellStyle);
1726 if (cellStyle.classes) {
1727 class_ = sprintf(' class="%s"', cellStyle.classes);
1729 if (cellStyle.css) {
1731 for (var key in cellStyle.css) {
1732 csses_.push(key + ': ' + cellStyle.css[key]);
1734 style = sprintf('style="%s"', csses_.concat(that.header.styles[j]).join('; '));
1737 value = calculateObjectValue(column,
1738 that.header.formatters[j], [value_, item, i], value_);
1740 if (item['_' + field + '_data'] && !$.isEmptyObject(item['_' + field + '_data'])) {
1741 $.each(item['_' + field + '_data'], function (k, v) {
1742 // ignore data-index
1743 if (k === 'index') {
1746 data_ += sprintf(' data-%s="%s"', k, v);
1750 if (column.checkbox || column.radio) {
1751 type = column.checkbox ? 'checkbox' : type;
1752 type = column.radio ? 'radio' : type;
1754 text = [sprintf(that.options.cardView ?
1755 '<div class="card-view %s">' : '<td class="bs-checkbox %s">', column['class'] || ''),
1757 sprintf(' data-index="%s"', i) +
1758 sprintf(' name="%s"', that.options.selectItemName) +
1759 sprintf(' type="%s"', type) +
1760 sprintf(' value="%s"', item[that.options.idField]) +
1761 sprintf(' checked="%s"', value === true ||
1762 (value_ || value && value.checked) ? 'checked' : undefined) +
1763 sprintf(' disabled="%s"', !column.checkboxEnabled ||
1764 (value && value.disabled) ? 'disabled' : undefined) +
1766 that.header.formatters[j] && typeof value === 'string' ? value : '',
1767 that.options.cardView ? '</div>' : '</td>'
1770 item[that.header.stateField] = value === true || (value && value.checked);
1772 value = typeof value === 'undefined' || value === null ?
1773 that.options.undefinedText : value;
1775 text = that.options.cardView ? ['<div class="card-view">',
1776 that.options.showHeader ? sprintf('<span class="title" %s>%s</span>', style,
1777 getPropertyFromOther(that.columns, 'field', 'title', field)) : '',
1778 sprintf('<span class="value">%s</span>', value),
1780 ].join('') : [sprintf('<td%s %s %s %s %s %s %s>',
1781 id_, class_, style, data_, rowspan_, colspan_, title_),
1786 // Hide empty data on Card view when smartDisplay is set to true.
1787 if (that.options.cardView && that.options.smartDisplay && value === '') {
1788 // Should set a placeholder for event binding correct fieldIndex
1789 text = '<div class="card-view"></div>';
1796 if (this.options.cardView) {
1797 html.push('</div></td>');
1805 html.push('<tr class="no-records-found">',
1806 sprintf('<td colspan="%s">%s</td>',
1807 this.$header.find('th').length, this.options.formatNoMatches()),
1811 this.$body.html(html.join(''));
1817 // click to select by column
1818 this.$body.find('> tr[data-index] > td').off('click dblclick').on('click dblclick', function (e) {
1821 item = that.data[$tr.data('index')],
1822 index = $td[0].cellIndex,
1823 fields = that.getVisibleFields(),
1824 field = fields[that.options.detailView && !that.options.cardView ? index - 1 : index],
1825 column = that.columns[getFieldIndex(that.columns, field)],
1826 value = getItemField(item, field, that.options.escape);
1828 if ($td.find('.detail-icon').length) {
1832 that.trigger(e.type === 'click' ? 'click-cell' : 'dbl-click-cell', field, value, item, $td);
1833 that.trigger(e.type === 'click' ? 'click-row' : 'dbl-click-row', item, $tr, field);
1835 // if click to select - then trigger the checkbox/radio click
1836 if (e.type === 'click' && that.options.clickToSelect && column.clickToSelect) {
1837 var $selectItem = $tr.find(sprintf('[name="%s"]', that.options.selectItemName));
1838 if ($selectItem.length) {
1839 $selectItem[0].click(); // #144: .trigger('click') bug
1844 this.$body.find('> tr[data-index] > td > .detail-icon').off('click').on('click', function () {
1845 var $this = $(this),
1846 $tr = $this.parent().parent(),
1847 index = $tr.data('index'),
1848 row = data[index]; // Fix #980 Detail view, when searching, returns wrong row
1850 // remove and update
1851 if ($tr.next().is('tr.detail-view')) {
1852 $this.find('i').attr('class', sprintf('%s %s', that.options.iconsPrefix, that.options.icons.detailOpen));
1853 $tr.next().remove();
1854 that.trigger('collapse-row', index, row);
1856 $this.find('i').attr('class', sprintf('%s %s', that.options.iconsPrefix, that.options.icons.detailClose));
1857 $tr.after(sprintf('<tr class="detail-view"><td colspan="%s"></td></tr>', $tr.find('td').length));
1858 var $element = $tr.next().find('td');
1859 var content = calculateObjectValue(that.options, that.options.detailFormatter, [index, row, $element], '');
1860 if($element.length === 1) {
1861 $element.append(content);
1863 that.trigger('expand-row', index, row, $element);
1868 this.$selectItem = this.$body.find(sprintf('[name="%s"]', this.options.selectItemName));
1869 this.$selectItem.off('click').on('click', function (event) {
1870 event.stopImmediatePropagation();
1872 var $this = $(this),
1873 checked = $this.prop('checked'),
1874 row = that.data[$this.data('index')];
1876 if (that.options.maintainSelected && $(this).is(':radio')) {
1877 $.each(that.options.data, function (i, row) {
1878 row[that.header.stateField] = false;
1882 row[that.header.stateField] = checked;
1884 if (that.options.singleSelect) {
1885 that.$selectItem.not(this).each(function () {
1886 that.data[$(this).data('index')][that.header.stateField] = false;
1888 that.$selectItem.filter(':checked').not(this).prop('checked', false);
1891 that.updateSelected();
1892 that.trigger(checked ? 'check' : 'uncheck', row, $this);
1895 $.each(this.header.events, function (i, events) {
1899 // fix bug, if events is defined with namespace
1900 if (typeof events === 'string') {
1901 events = calculateObjectValue(null, events);
1904 var field = that.header.fields[i],
1905 fieldIndex = $.inArray(field, that.getVisibleFields());
1907 if (that.options.detailView && !that.options.cardView) {
1911 for (var key in events) {
1912 that.$body.find('>tr:not(.no-records-found)').each(function () {
1914 $td = $tr.find(that.options.cardView ? '.card-view' : 'td').eq(fieldIndex),
1915 index = key.indexOf(' '),
1916 name = key.substring(0, index),
1917 el = key.substring(index + 1),
1920 $td.find(el).off(name).on(name, function (e) {
1921 var index = $tr.data('index'),
1922 row = that.data[index],
1925 func.apply(this, [e, value, row, index]);
1931 this.updateSelected();
1934 this.trigger('post-body', data);
1937 BootstrapTable.prototype.initServer = function (silent, query, url) {
1941 searchText: this.searchText,
1942 sortName: this.options.sortName,
1943 sortOrder: this.options.sortOrder
1947 if (this.options.pagination) {
1948 params.pageSize = this.options.pageSize === this.options.formatAllRows() ?
1949 this.options.totalRows : this.options.pageSize;
1950 params.pageNumber = this.options.pageNumber;
1953 if (!(url || this.options.url) && !this.options.ajax) {
1957 if (this.options.queryParamsType === 'limit') {
1959 search: params.searchText,
1960 sort: params.sortName,
1961 order: params.sortOrder
1964 if (this.options.pagination) {
1965 params.offset = this.options.pageSize === this.options.formatAllRows() ?
1966 0 : this.options.pageSize * (this.options.pageNumber - 1);
1967 params.limit = this.options.pageSize === this.options.formatAllRows() ?
1968 this.options.totalRows : this.options.pageSize;
1972 if (!($.isEmptyObject(this.filterColumnsPartial))) {
1973 params.filter = JSON.stringify(this.filterColumnsPartial, null);
1976 data = calculateObjectValue(this.options, this.options.queryParams, [params], data);
1978 $.extend(data, query || {});
1980 // false to stop request
1981 if (data === false) {
1986 this.$tableLoading.show();
1988 request = $.extend({}, calculateObjectValue(null, this.options.ajaxOptions), {
1989 type: this.options.method,
1990 url: url || this.options.url,
1991 data: this.options.contentType === 'application/json' && this.options.method === 'post' ?
1992 JSON.stringify(data) : data,
1993 cache: this.options.cache,
1994 contentType: this.options.contentType,
1995 dataType: this.options.dataType,
1996 success: function (res) {
1997 res = calculateObjectValue(that.options, that.options.responseHandler, [res], res);
2000 that.trigger('load-success', res);
2001 if (!silent) that.$tableLoading.hide();
2003 error: function (res) {
2004 that.trigger('load-error', res.status, res);
2005 if (!silent) that.$tableLoading.hide();
2009 if (this.options.ajax) {
2010 calculateObjectValue(this, this.options.ajax, [request], null);
2012 if (this._xhr && this._xhr.readyState !== 4) {
2015 this._xhr = $.ajax(request);
2019 BootstrapTable.prototype.initSearchText = function () {
2020 if (this.options.search) {
2021 if (this.options.searchText !== '') {
2022 var $search = this.$toolbar.find('.search input');
2023 $search.val(this.options.searchText);
2024 this.onSearch({currentTarget: $search});
2029 BootstrapTable.prototype.getCaret = function () {
2032 $.each(this.$header.find('th'), function (i, th) {
2033 $(th).find('.sortable').removeClass('desc asc').addClass($(th).data('field') === that.options.sortName ? that.options.sortOrder : 'both');
2037 BootstrapTable.prototype.updateSelected = function () {
2038 var checkAll = this.$selectItem.filter(':enabled').length &&
2039 this.$selectItem.filter(':enabled').length ===
2040 this.$selectItem.filter(':enabled').filter(':checked').length;
2042 this.$selectAll.add(this.$selectAll_).prop('checked', checkAll);
2044 this.$selectItem.each(function () {
2045 $(this).closest('tr')[$(this).prop('checked') ? 'addClass' : 'removeClass']('selected');
2049 BootstrapTable.prototype.updateRows = function () {
2052 this.$selectItem.each(function () {
2053 that.data[$(this).data('index')][that.header.stateField] = $(this).prop('checked');
2057 BootstrapTable.prototype.resetRows = function () {
2060 $.each(this.data, function (i, row) {
2061 that.$selectAll.prop('checked', false);
2062 that.$selectItem.prop('checked', false);
2063 if (that.header.stateField) {
2064 row[that.header.stateField] = false;
2067 this.initHiddenRows();
2070 BootstrapTable.prototype.trigger = function (name) {
2071 var args = Array.prototype.slice.call(arguments, 1);
2073 name += '.bs.table';
2074 this.options[BootstrapTable.EVENTS[name]].apply(this.options, args);
2075 this.$el.trigger($.Event(name), args);
2077 this.options.onAll(name, args);
2078 this.$el.trigger($.Event('all.bs.table'), [name, args]);
2081 BootstrapTable.prototype.resetHeader = function () {
2082 // fix #61: the hidden table reset header bug.
2083 // fix bug: get $el.css('width') error sometime (height = 500)
2084 clearTimeout(this.timeoutId_);
2085 this.timeoutId_ = setTimeout($.proxy(this.fitHeader, this), this.$el.is(':hidden') ? 100 : 0);
2088 BootstrapTable.prototype.fitHeader = function () {
2095 if (that.$el.is(':hidden')) {
2096 that.timeoutId_ = setTimeout($.proxy(that.fitHeader, that), 100);
2099 fixedBody = this.$tableBody.get(0);
2101 scrollWidth = fixedBody.scrollWidth > fixedBody.clientWidth &&
2102 fixedBody.scrollHeight > fixedBody.clientHeight + this.$header.outerHeight() ?
2103 getScrollBarWidth() : 0;
2105 this.$el.css('margin-top', -this.$header.outerHeight());
2107 focused = $(':focus');
2108 if (focused.length > 0) {
2109 var $th = focused.parents('th');
2110 if ($th.length > 0) {
2111 var dataField = $th.attr('data-field');
2112 if (dataField !== undefined) {
2113 var $headerTh = this.$header.find("[data-field='" + dataField + "']");
2114 if ($headerTh.length > 0) {
2115 $headerTh.find(":input").addClass("focus-temp");
2121 this.$header_ = this.$header.clone(true, true);
2122 this.$selectAll_ = this.$header_.find('[name="btSelectAll"]');
2123 this.$tableHeader.css({
2124 'margin-right': scrollWidth
2125 }).find('table').css('width', this.$el.outerWidth())
2126 .html('').attr('class', this.$el.attr('class'))
2127 .append(this.$header_);
2130 focusedTemp = $('.focus-temp:visible:eq(0)');
2131 if (focusedTemp.length > 0) {
2132 focusedTemp.focus();
2133 this.$header.find('.focus-temp').removeClass('focus-temp');
2136 // fix bug: $.data() is not working as expected after $.append()
2137 this.$header.find('th[data-field]').each(function (i) {
2138 that.$header_.find(sprintf('th[data-field="%s"]', $(this).data('field'))).data($(this).data());
2141 var visibleFields = this.getVisibleFields(),
2142 $ths = this.$header_.find('th');
2144 this.$body.find('>tr:first-child:not(.no-records-found) > *').each(function (i) {
2145 var $this = $(this),
2148 if (that.options.detailView && !that.options.cardView) {
2150 that.$header_.find('th.detail').find('.fht-cell').width($this.innerWidth());
2155 var $th = that.$header_.find(sprintf('th[data-field="%s"]', visibleFields[index]));
2156 if ($th.length > 1) {
2157 $th = $($ths[$this[0].cellIndex]);
2160 $th.find('.fht-cell').width($this.innerWidth());
2162 // horizontal scroll event
2163 // TODO: it's probably better improving the layout than binding to scroll event
2164 this.$tableBody.off('scroll').on('scroll', function () {
2165 that.$tableHeader.scrollLeft($(this).scrollLeft());
2167 if (that.options.showFooter && !that.options.cardView) {
2168 that.$tableFooter.scrollLeft($(this).scrollLeft());
2171 that.trigger('post-header');
2174 BootstrapTable.prototype.resetFooter = function () {
2176 data = that.getData(),
2179 if (!this.options.showFooter || this.options.cardView) { //do nothing
2183 if (!this.options.cardView && this.options.detailView) {
2184 html.push('<td><div class="th-inner"> </div><div class="fht-cell"></div></td>');
2187 $.each(this.columns, function (i, column) {
2189 falign = '', // footer align style
2193 class_ = sprintf(' class="%s"', column['class']);
2195 if (!column.visible) {
2199 if (that.options.cardView && (!column.cardVisible)) {
2203 falign = sprintf('text-align: %s; ', column.falign ? column.falign : column.align);
2204 valign = sprintf('vertical-align: %s; ', column.valign);
2206 style = calculateObjectValue(null, that.options.footerStyle);
2208 if (style && style.css) {
2209 for (key in style.css) {
2210 csses.push(key + ': ' + style.css[key]);
2214 html.push('<td', class_, sprintf(' style="%s"', falign + valign + csses.concat().join('; ')), '>');
2215 html.push('<div class="th-inner">');
2217 html.push(calculateObjectValue(column, column.footerFormatter, [data], ' ') || ' ');
2219 html.push('</div>');
2220 html.push('<div class="fht-cell"></div>');
2221 html.push('</div>');
2225 this.$tableFooter.find('tr').html(html.join(''));
2226 this.$tableFooter.show();
2227 clearTimeout(this.timeoutFooter_);
2228 this.timeoutFooter_ = setTimeout($.proxy(this.fitFooter, this),
2229 this.$el.is(':hidden') ? 100 : 0);
2232 BootstrapTable.prototype.fitFooter = function () {
2238 clearTimeout(this.timeoutFooter_);
2239 if (this.$el.is(':hidden')) {
2240 this.timeoutFooter_ = setTimeout($.proxy(this.fitFooter, this), 100);
2244 elWidth = this.$el.css('width');
2245 scrollWidth = elWidth > this.$tableBody.width() ? getScrollBarWidth() : 0;
2247 this.$tableFooter.css({
2248 'margin-right': scrollWidth
2249 }).find('table').css('width', elWidth)
2250 .attr('class', this.$el.attr('class'));
2252 $footerTd = this.$tableFooter.find('td');
2254 this.$body.find('>tr:first-child:not(.no-records-found) > *').each(function (i) {
2255 var $this = $(this);
2257 $footerTd.eq(i).find('.fht-cell').width($this.innerWidth());
2261 BootstrapTable.prototype.toggleColumn = function (index, checked, needUpdate) {
2265 this.columns[index].visible = checked;
2268 this.initPagination();
2271 if (this.options.showColumns) {
2272 var $items = this.$toolbar.find('.keep-open input').prop('disabled', false);
2275 $items.filter(sprintf('[value="%s"]', index)).prop('checked', checked);
2278 if ($items.filter(':checked').length <= this.options.minimumCountColumns) {
2279 $items.filter(':checked').prop('disabled', true);
2284 BootstrapTable.prototype.getVisibleFields = function () {
2288 $.each(this.header.fields, function (j, field) {
2289 var column = that.columns[getFieldIndex(that.columns, field)];
2291 if (!column.visible) {
2294 visibleFields.push(field);
2296 return visibleFields;
2299 // PUBLIC FUNCTION DEFINITION
2300 // =======================
2302 BootstrapTable.prototype.resetView = function (params) {
2305 if (params && params.height) {
2306 this.options.height = params.height;
2309 this.$selectAll.prop('checked', this.$selectItem.length > 0 &&
2310 this.$selectItem.length === this.$selectItem.filter(':checked').length);
2312 if (this.options.height) {
2313 var toolbarHeight = this.$toolbar.outerHeight(true),
2314 paginationHeight = this.$pagination.outerHeight(true),
2315 height = this.options.height - toolbarHeight - paginationHeight;
2317 this.$tableContainer.css('height', height + 'px');
2320 if (this.options.cardView) {
2321 // remove the element css
2322 this.$el.css('margin-top', '0');
2323 this.$tableContainer.css('padding-bottom', '0');
2324 this.$tableFooter.hide();
2328 if (this.options.showHeader && this.options.height) {
2329 this.$tableHeader.show();
2331 padding += this.$header.outerHeight();
2333 this.$tableHeader.hide();
2334 this.trigger('post-header');
2337 if (this.options.showFooter) {
2339 if (this.options.height) {
2340 padding += this.$tableFooter.outerHeight() + 1;
2344 // Assign the correct sortable arrow
2346 this.$tableContainer.css('padding-bottom', padding + 'px');
2347 this.trigger('reset-view');
2350 BootstrapTable.prototype.getData = function (useCurrentPage) {
2351 return (this.searchText || !$.isEmptyObject(this.filterColumns) || !$.isEmptyObject(this.filterColumnsPartial)) ?
2352 (useCurrentPage ? this.data.slice(this.pageFrom - 1, this.pageTo) : this.data) :
2353 (useCurrentPage ? this.options.data.slice(this.pageFrom - 1, this.pageTo) : this.options.data);
2356 BootstrapTable.prototype.load = function (data) {
2357 var fixedScroll = false;
2359 // #431: support pagination
2360 if (this.options.sidePagination === 'server') {
2361 this.options.totalRows = data[this.options.totalField];
2362 fixedScroll = data.fixedScroll;
2363 data = data[this.options.dataField];
2364 } else if (!$.isArray(data)) { // support fixedScroll
2365 fixedScroll = data.fixedScroll;
2369 this.initData(data);
2371 this.initPagination();
2372 this.initBody(fixedScroll);
2375 BootstrapTable.prototype.append = function (data) {
2376 this.initData(data, 'append');
2378 this.initPagination();
2380 this.initBody(true);
2383 BootstrapTable.prototype.prepend = function (data) {
2384 this.initData(data, 'prepend');
2386 this.initPagination();
2388 this.initBody(true);
2391 BootstrapTable.prototype.remove = function (params) {
2392 var len = this.options.data.length,
2395 if (!params.hasOwnProperty('field') || !params.hasOwnProperty('values')) {
2399 for (i = len - 1; i >= 0; i--) {
2400 row = this.options.data[i];
2402 if (!row.hasOwnProperty(params.field)) {
2405 if ($.inArray(row[params.field], params.values) !== -1) {
2406 this.options.data.splice(i, 1);
2407 if (this.options.sidePagination === 'server') {
2408 this.options.totalRows -= 1;
2413 if (len === this.options.data.length) {
2418 this.initPagination();
2420 this.initBody(true);
2423 BootstrapTable.prototype.removeAll = function () {
2424 if (this.options.data.length > 0) {
2425 this.options.data.splice(0, this.options.data.length);
2427 this.initPagination();
2428 this.initBody(true);
2432 BootstrapTable.prototype.getRowByUniqueId = function (id) {
2433 var uniqueId = this.options.uniqueId,
2434 len = this.options.data.length,
2436 i, row, rowUniqueId;
2438 for (i = len - 1; i >= 0; i--) {
2439 row = this.options.data[i];
2441 if (row.hasOwnProperty(uniqueId)) { // uniqueId is a column
2442 rowUniqueId = row[uniqueId];
2443 } else if(row._data.hasOwnProperty(uniqueId)) { // uniqueId is a row data property
2444 rowUniqueId = row._data[uniqueId];
2449 if (typeof rowUniqueId === 'string') {
2451 } else if (typeof rowUniqueId === 'number') {
2452 if ((Number(rowUniqueId) === rowUniqueId) && (rowUniqueId % 1 === 0)) {
2454 } else if ((rowUniqueId === Number(rowUniqueId)) && (rowUniqueId !== 0)) {
2455 id = parseFloat(id);
2459 if (rowUniqueId === id) {
2468 BootstrapTable.prototype.removeByUniqueId = function (id) {
2469 var len = this.options.data.length,
2470 row = this.getRowByUniqueId(id);
2473 this.options.data.splice(this.options.data.indexOf(row), 1);
2476 if (len === this.options.data.length) {
2481 this.initPagination();
2482 this.initBody(true);
2485 BootstrapTable.prototype.updateByUniqueId = function (params) {
2487 var allParams = $.isArray(params) ? params : [ params ];
2489 $.each(allParams, function(i, params) {
2492 if (!params.hasOwnProperty('id') || !params.hasOwnProperty('row')) {
2496 rowId = $.inArray(that.getRowByUniqueId(params.id), that.options.data);
2501 $.extend(that.options.data[rowId], params.row);
2505 this.initPagination();
2507 this.initBody(true);
2510 BootstrapTable.prototype.insertRow = function (params) {
2511 if (!params.hasOwnProperty('index') || !params.hasOwnProperty('row')) {
2514 this.data.splice(params.index, 0, params.row);
2516 this.initPagination();
2518 this.initBody(true);
2521 BootstrapTable.prototype.updateRow = function (params) {
2523 var allParams = $.isArray(params) ? params : [ params ];
2525 $.each(allParams, function(i, params) {
2526 if (!params.hasOwnProperty('index') || !params.hasOwnProperty('row')) {
2529 $.extend(that.options.data[params.index], params.row);
2533 this.initPagination();
2535 this.initBody(true);
2538 BootstrapTable.prototype.initHiddenRows = function () {
2539 this.hiddenRows = [];
2542 BootstrapTable.prototype.showRow = function (params) {
2543 this.toggleRow(params, true);
2546 BootstrapTable.prototype.hideRow = function (params) {
2547 this.toggleRow(params, false);
2550 BootstrapTable.prototype.toggleRow = function (params, visible) {
2553 if (params.hasOwnProperty('index')) {
2554 row = this.getData()[params.index];
2555 } else if (params.hasOwnProperty('uniqueId')) {
2556 row = this.getRowByUniqueId(params.uniqueId);
2563 index = $.inArray(row, this.hiddenRows);
2565 if (!visible && index === -1) {
2566 this.hiddenRows.push(row);
2567 } else if (visible && index > -1) {
2568 this.hiddenRows.splice(index, 1);
2570 this.initBody(true);
2573 BootstrapTable.prototype.getHiddenRows = function (show) {
2575 data = this.getData(),
2578 $.each(data, function (i, row) {
2579 if ($.inArray(row, that.hiddenRows) > -1) {
2583 this.hiddenRows = rows;
2587 BootstrapTable.prototype.mergeCells = function (options) {
2588 var row = options.index,
2589 col = $.inArray(options.field, this.getVisibleFields()),
2590 rowspan = options.rowspan || 1,
2591 colspan = options.colspan || 1,
2593 $tr = this.$body.find('>tr'),
2596 if (this.options.detailView && !this.options.cardView) {
2600 $td = $tr.eq(row).find('>td').eq(col);
2602 if (row < 0 || col < 0 || row >= this.data.length) {
2606 for (i = row; i < row + rowspan; i++) {
2607 for (j = col; j < col + colspan; j++) {
2608 $tr.eq(i).find('>td').eq(j).hide();
2612 $td.attr('rowspan', rowspan).attr('colspan', colspan).show();
2615 BootstrapTable.prototype.updateCell = function (params) {
2616 if (!params.hasOwnProperty('index') ||
2617 !params.hasOwnProperty('field') ||
2618 !params.hasOwnProperty('value')) {
2621 this.data[params.index][params.field] = params.value;
2623 if (params.reinit === false) {
2627 this.initBody(true);
2630 BootstrapTable.prototype.getOptions = function () {
2631 return this.options;
2634 BootstrapTable.prototype.getSelections = function () {
2637 return $.grep(this.options.data, function (row) {
2638 // fix #2424: from html with checkbox
2639 return row[that.header.stateField] === true;
2643 BootstrapTable.prototype.getAllSelections = function () {
2646 return $.grep(this.options.data, function (row) {
2647 return row[that.header.stateField];
2651 BootstrapTable.prototype.checkAll = function () {
2652 this.checkAll_(true);
2655 BootstrapTable.prototype.uncheckAll = function () {
2656 this.checkAll_(false);
2659 BootstrapTable.prototype.checkInvert = function () {
2661 var rows = that.$selectItem.filter(':enabled');
2662 var checked = rows.filter(':checked');
2663 rows.each(function() {
2664 $(this).prop('checked', !$(this).prop('checked'));
2667 that.updateSelected();
2668 that.trigger('uncheck-some', checked);
2669 checked = that.getSelections();
2670 that.trigger('check-some', checked);
2673 BootstrapTable.prototype.checkAll_ = function (checked) {
2676 rows = this.getSelections();
2678 this.$selectAll.add(this.$selectAll_).prop('checked', checked);
2679 this.$selectItem.filter(':enabled').prop('checked', checked);
2682 rows = this.getSelections();
2684 this.trigger(checked ? 'check-all' : 'uncheck-all', rows);
2687 BootstrapTable.prototype.check = function (index) {
2688 this.check_(true, index);
2691 BootstrapTable.prototype.uncheck = function (index) {
2692 this.check_(false, index);
2695 BootstrapTable.prototype.check_ = function (checked, index) {
2696 var $el = this.$selectItem.filter(sprintf('[data-index="%s"]', index)).prop('checked', checked);
2697 this.data[index][this.header.stateField] = checked;
2698 this.updateSelected();
2699 this.trigger(checked ? 'check' : 'uncheck', this.data[index], $el);
2702 BootstrapTable.prototype.checkBy = function (obj) {
2703 this.checkBy_(true, obj);
2706 BootstrapTable.prototype.uncheckBy = function (obj) {
2707 this.checkBy_(false, obj);
2710 BootstrapTable.prototype.checkBy_ = function (checked, obj) {
2711 if (!obj.hasOwnProperty('field') || !obj.hasOwnProperty('values')) {
2717 $.each(this.options.data, function (index, row) {
2718 if (!row.hasOwnProperty(obj.field)) {
2721 if ($.inArray(row[obj.field], obj.values) !== -1) {
2722 var $el = that.$selectItem.filter(':enabled')
2723 .filter(sprintf('[data-index="%s"]', index)).prop('checked', checked);
2724 row[that.header.stateField] = checked;
2726 that.trigger(checked ? 'check' : 'uncheck', row, $el);
2729 this.updateSelected();
2730 this.trigger(checked ? 'check-some' : 'uncheck-some', rows);
2733 BootstrapTable.prototype.destroy = function () {
2734 this.$el.insertBefore(this.$container);
2735 $(this.options.toolbar).insertBefore(this.$el);
2736 this.$container.next().remove();
2737 this.$container.remove();
2738 this.$el.html(this.$el_.html())
2739 .css('margin-top', '0')
2740 .attr('class', this.$el_.attr('class') || ''); // reset the class
2743 BootstrapTable.prototype.showLoading = function () {
2744 this.$tableLoading.show();
2747 BootstrapTable.prototype.hideLoading = function () {
2748 this.$tableLoading.hide();
2751 BootstrapTable.prototype.togglePagination = function () {
2752 this.options.pagination = !this.options.pagination;
2753 var button = this.$toolbar.find('button[name="paginationSwitch"] i');
2754 if (this.options.pagination) {
2755 button.attr("class", this.options.iconsPrefix + " " + this.options.icons.paginationSwitchDown);
2757 button.attr("class", this.options.iconsPrefix + " " + this.options.icons.paginationSwitchUp);
2759 this.updatePagination();
2762 BootstrapTable.prototype.refresh = function (params) {
2763 if (params && params.url) {
2764 this.options.pageNumber = 1;
2766 this.initServer(params && params.silent,
2767 params && params.query, params && params.url);
2768 this.trigger('refresh', params);
2771 BootstrapTable.prototype.resetWidth = function () {
2772 if (this.options.showHeader && this.options.height) {
2775 if (this.options.showFooter) {
2780 BootstrapTable.prototype.showColumn = function (field) {
2781 this.toggleColumn(getFieldIndex(this.columns, field), true, true);
2784 BootstrapTable.prototype.hideColumn = function (field) {
2785 this.toggleColumn(getFieldIndex(this.columns, field), false, true);
2788 BootstrapTable.prototype.getHiddenColumns = function () {
2789 return $.grep(this.columns, function (column) {
2790 return !column.visible;
2794 BootstrapTable.prototype.getVisibleColumns = function () {
2795 return $.grep(this.columns, function (column) {
2796 return column.visible;
2800 BootstrapTable.prototype.toggleAllColumns = function (visible) {
2801 $.each(this.columns, function (i, column) {
2802 this.columns[i].visible = visible;
2807 this.initPagination();
2809 if (this.options.showColumns) {
2810 var $items = this.$toolbar.find('.keep-open input').prop('disabled', false);
2812 if ($items.filter(':checked').length <= this.options.minimumCountColumns) {
2813 $items.filter(':checked').prop('disabled', true);
2818 BootstrapTable.prototype.showAllColumns = function () {
2819 this.toggleAllColumns(true);
2822 BootstrapTable.prototype.hideAllColumns = function () {
2823 this.toggleAllColumns(false);
2826 BootstrapTable.prototype.filterBy = function (columns) {
2827 this.filterColumns = $.isEmptyObject(columns) ? {} : columns;
2828 this.options.pageNumber = 1;
2830 this.updatePagination();
2833 BootstrapTable.prototype.scrollTo = function (value) {
2834 if (typeof value === 'string') {
2835 value = value === 'bottom' ? this.$tableBody[0].scrollHeight : 0;
2837 if (typeof value === 'number') {
2838 this.$tableBody.scrollTop(value);
2840 if (typeof value === 'undefined') {
2841 return this.$tableBody.scrollTop();
2845 BootstrapTable.prototype.getScrollPosition = function () {
2846 return this.scrollTo();
2849 BootstrapTable.prototype.selectPage = function (page) {
2850 if (page > 0 && page <= this.options.totalPages) {
2851 this.options.pageNumber = page;
2852 this.updatePagination();
2856 BootstrapTable.prototype.prevPage = function () {
2857 if (this.options.pageNumber > 1) {
2858 this.options.pageNumber--;
2859 this.updatePagination();
2863 BootstrapTable.prototype.nextPage = function () {
2864 if (this.options.pageNumber < this.options.totalPages) {
2865 this.options.pageNumber++;
2866 this.updatePagination();
2870 BootstrapTable.prototype.toggleView = function () {
2871 this.options.cardView = !this.options.cardView;
2873 // Fixed remove toolbar when click cardView button.
2874 //that.initToolbar();
2876 this.trigger('toggle', this.options.cardView);
2879 BootstrapTable.prototype.refreshOptions = function (options) {
2880 //If the objects are equivalent then avoid the call of destroy / init methods
2881 if (compareObjects(this.options, options, true)) {
2884 this.options = $.extend(this.options, options);
2885 this.trigger('refresh-options', this.options);
2890 BootstrapTable.prototype.resetSearch = function (text) {
2891 var $search = this.$toolbar.find('.search input');
2892 $search.val(text || '');
2893 this.onSearch({currentTarget: $search});
2896 BootstrapTable.prototype.expandRow_ = function (expand, index) {
2897 var $tr = this.$body.find(sprintf('> tr[data-index="%s"]', index));
2898 if ($tr.next().is('tr.detail-view') === (expand ? false : true)) {
2899 $tr.find('> td > .detail-icon').click();
2903 BootstrapTable.prototype.expandRow = function (index) {
2904 this.expandRow_(true, index);
2907 BootstrapTable.prototype.collapseRow = function (index) {
2908 this.expandRow_(false, index);
2911 BootstrapTable.prototype.expandAllRows = function (isSubTable) {
2913 var $tr = this.$body.find(sprintf('> tr[data-index="%s"]', 0)),
2916 executeInterval = false,
2919 if (!$tr.next().is('tr.detail-view')) {
2920 $tr.find('> td > .detail-icon').click();
2921 executeInterval = true;
2922 } else if (!$tr.next().next().is('tr.detail-view')) {
2923 $tr.next().find(".detail-icon").click();
2924 executeInterval = true;
2927 if (executeInterval) {
2929 idInterval = setInterval(function () {
2930 detailIcon = that.$body.find("tr.detail-view").last().find(".detail-icon");
2931 if (detailIcon.length > 0) {
2934 clearInterval(idInterval);
2938 clearInterval(idInterval);
2942 var trs = this.$body.children();
2943 for (var i = 0; i < trs.length; i++) {
2944 this.expandRow_(true, $(trs[i]).data("index"));
2949 BootstrapTable.prototype.collapseAllRows = function (isSubTable) {
2951 this.expandRow_(false, 0);
2953 var trs = this.$body.children();
2954 for (var i = 0; i < trs.length; i++) {
2955 this.expandRow_(false, $(trs[i]).data("index"));
2960 BootstrapTable.prototype.updateFormatText = function (name, text) {
2961 if (this.options[sprintf('format%s', name)]) {
2962 if (typeof text === 'string') {
2963 this.options[sprintf('format%s', name)] = function () {
2966 } else if (typeof text === 'function') {
2967 this.options[sprintf('format%s', name)] = text;
2971 this.initPagination();
2975 // BOOTSTRAP TABLE PLUGIN DEFINITION
2976 // =======================
2978 var allowedMethods = [
2980 'getSelections', 'getAllSelections', 'getData',
2981 'load', 'append', 'prepend', 'remove', 'removeAll',
2982 'insertRow', 'updateRow', 'updateCell', 'updateByUniqueId', 'removeByUniqueId',
2983 'getRowByUniqueId', 'showRow', 'hideRow', 'getHiddenRows',
2985 'checkAll', 'uncheckAll', 'checkInvert',
2987 'checkBy', 'uncheckBy',
2992 'showLoading', 'hideLoading',
2993 'showColumn', 'hideColumn', 'getHiddenColumns', 'getVisibleColumns',
2994 'showAllColumns', 'hideAllColumns',
2997 'getScrollPosition',
2998 'selectPage', 'prevPage', 'nextPage',
3003 'expandRow', 'collapseRow', 'expandAllRows', 'collapseAllRows',
3007 $.fn.bootstrapTable = function (option) {
3009 args = Array.prototype.slice.call(arguments, 1);
3011 this.each(function () {
3012 var $this = $(this),
3013 data = $this.data('bootstrap.table'),
3014 options = $.extend({}, BootstrapTable.DEFAULTS, $this.data(),
3015 typeof option === 'object' && option);
3017 if (typeof option === 'string') {
3018 if ($.inArray(option, allowedMethods) < 0) {
3019 throw new Error("Unknown method: " + option);
3026 value = data[option].apply(data, args);
3028 if (option === 'destroy') {
3029 $this.removeData('bootstrap.table');
3034 $this.data('bootstrap.table', (data = new BootstrapTable(this, options)));
3038 return typeof value === 'undefined' ? this : value;
3041 $.fn.bootstrapTable.Constructor = BootstrapTable;
3042 $.fn.bootstrapTable.defaults = BootstrapTable.DEFAULTS;
3043 $.fn.bootstrapTable.columnDefaults = BootstrapTable.COLUMN_DEFAULTS;
3044 $.fn.bootstrapTable.locales = BootstrapTable.LOCALES;
3045 $.fn.bootstrapTable.methods = allowedMethods;
3046 $.fn.bootstrapTable.utils = {
3048 getFieldIndex: getFieldIndex,
3049 compareObjects: compareObjects,
3050 calculateObjectValue: calculateObjectValue,
3051 getItemField: getItemField,
3052 objectKeys: objectKeys,
3053 isIEBrowser: isIEBrowser
3056 // BOOTSTRAP TABLE INIT
3057 // =======================
3060 $('[data-toggle="table"]').bootstrapTable();