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