PageSourceSearch

https://www.ncausa.org/NOAH/plugins/bootstrap-table-master/src/bootstrap-table.js

js ncausa.org collected 2026-09-24 08:34:31 UTC 120,769 bytes, 3,295 lines download raw bytes

1/**
2 * @author zhixin wen <[email protected]>
3 * version: 1.12.1
4 * https://github.com/wenzhixin/bootstrap-table/
5 */
6
7(function ($) {
8    'use strict';
9
10    // TOOLS DEFINITION
11    // ======================
12
13    var bootstrapVersion = 3;
14    try {
15        bootstrapVersion = parseInt($.fn.dropdown.Constructor.VERSION, 10);
16    } catch (e) {}
17    var bs = {
18        3: {
19            buttonsClass: 'default',
20            iconsPrefix: 'glyphicon',
21            icons: {
22                paginationSwitchDown: 'glyphicon-collapse-down icon-chevron-down',
23                paginationSwitchUp: 'glyphicon-collapse-up icon-chevron-up',
24                refresh: 'glyphicon-refresh icon-refresh',
25                toggleOff: 'glyphicon-list-alt icon-list-alt',
26                toggleOn: 'glyphicon-list-alt icon-list-alt',
27                columns: 'glyphicon-th icon-th',
28                detailOpen: 'glyphicon-plus icon-plus',
29                detailClose: 'glyphicon-minus icon-minus',
30                fullscreen: 'glyphicon-fullscreen'
31            },
32            pullClass: 'pull',
33            toobarDropdowHtml: ['<ul class="dropdown-menu" role="menu">', '</ul>'],
34            toobarDropdowItemHtml: '<li role="menuitem"><label>%s</label></li>',
35            pageDropdownHtml: ['<ul class="dropdown-menu" role="menu">', '</ul>'],
36            pageDropdownItemHtml: '<li role="menuitem" class="%s"><a href="#">%s</a></li>'
37        },
38        4: {
39            buttonsClass: 'secondary',
40            iconsPrefix: 'fa',
41            icons: {
42                paginationSwitchDown: 'fa-toggle-down',
43                paginationSwitchUp: 'fa-toggle-up',
44                refresh: 'fa-refresh',
45                toggleOff: 'fa-toggle-off',
46                toggleOn: 'fa-toggle-on',
47                columns: 'fa-th-list',
48                detailOpen: 'fa-plus',
49                detailClose: 'fa-minus',
50                fullscreen: 'fa-arrows-alt'
51            },
52            pullClass: 'float',
53            toobarDropdowHtml: ['<div class="dropdown-menu dropdown-menu-right">', '</div>'],
54            toobarDropdowItemHtml: '<label class="dropdown-item">%s</label>',
55            pageDropdownHtml: ['<div class="dropdown-menu">', '</div>'],
56            pageDropdownItemHtml: '<a class="dropdown-item %s" href="#">%s</a>'
57        }
58    }[bootstrapVersion];
59
60    var cachedWidth = null;
61
62    // it only does '%s', and return '' when arguments are undefined
63    var sprintf = function (str) {
64        var args = arguments,
65            flag = true,
66            i = 1;
67
68        str = str.replace(/%s/g, function () {
69            var arg = args[i++];
70
71            if (typeof arg === 'undefined') {
72                flag = false;
73                return '';
74            }
75            return arg;
76        });
77        return flag ? str : '';
78    };
79
80    var getPropertyFromOther = function (list, from, to, value) {
81        var result = '';
82        $.each(list, function (i, item) {
83            if (item[from] === value) {
84                result = item[to];
85                return false;
86            }
87            return true;
88        });
89        return result;
90    };
91
92    // http://jsfiddle.net/wenyi/47nz7ez9/3/
93    var setFieldIndex = function (columns) {
94        var i, j, k,
95            totalCol = 0,
96            flag = [];
97
98        for (i = 0; i < columns[0].length; i++) {
99            totalCol += columns[0][i].colspan || 1;
100        }
101
102        for (i = 0; i < columns.length; i++) {
103            flag[i] = [];
104            for (j = 0; j < totalCol; j++) {
105                flag[i][j] = false;
106            }
107        }
108
109        for (i = 0; i < columns.length; i++) {
110            for (j = 0; j < columns[i].length; j++) {
111                var r = columns[i][j],
112                    rowspan = r.rowspan || 1,
113                    colspan = r.colspan || 1,
114                    index = $.inArray(false, flag[i]);
115
116                if (colspan === 1) {
117                    r.fieldIndex = index;
118                    // when field is undefined, use index instead
119                    if (typeof r.field === 'undefined') {
120                        r.field = index;
121                    }
122                }
123
124                for (k = 0; k < rowspan; k++) {
125                    flag[i + k][index] = true;
126                }
127                for (k = 0; k < colspan; k++) {
128                    flag[i][index + k] = true;
129                }
130            }
131        }
132    };
133
134    var getScrollBarWidth = function () {
135        if (cachedWidth === null) {
136            var inner = $('<p/>').addClass('fixed-table-scroll-inner'),
137                outer = $('<div/>').addClass('fixed-table-scroll-outer'),
138                w1, w2;
139
140            outer.append(inner);
141            $('body').append(outer);
142
143            w1 = inner[0].offsetWidth;
144            outer.css('overflow', 'scroll');
145            w2 = inner[0].offsetWidth;
146
147            if (w1 === w2) {
148                w2 = outer[0].clientWidth;
149            }
150
151            outer.remove();
152            cachedWidth = w1 - w2;
153        }
154        return cachedWidth;
155    };
156
157    var calculateObjectValue = function (self, name, args, defaultValue) {
158        var func = name;
159
160        if (typeof name === 'string') {
161            // support obj.func1.func2
162            var names = name.split('.');
163
164            if (names.length > 1) {
165                func = window;
166                $.each(names, function (i, f) {
167                    func = func[f];
168                });
169            } else {
170                func = window[name];
171            }
172        }
173        if (typeof func === 'object') {
174            return func;
175        }
176        if (typeof func === 'function') {
177            return func.apply(self, args || []);
178        }
179        if (!func && typeof name === 'string' && sprintf.apply(this, [name].concat(args))) {
180            return sprintf.apply(this, [name].concat(args));
181        }
182        return defaultValue;
183    };
184
185    var compareObjects = function (objectA, objectB, compareLength) {
186        // Create arrays of property names
187        var getOwnPropertyNames = Object.getOwnPropertyNames || function (obj) {
188            var arr = [];
189            for (var k in obj) {
190                if (obj.hasOwnProperty(k)) {
191                    arr.push(k);
192                }
193            }
194            return arr;
195        };
196        var objectAProperties = getOwnPropertyNames(objectA),
197            objectBProperties = getOwnPropertyNames(objectB),
198            propName = '';
199
200        if (compareLength) {
201            // If number of properties is different, objects are not equivalent
202            if (objectAProperties.length !== objectBProperties.length) {
203                return false;
204            }
205        }
206
207        for (var i = 0; i < objectAProperties.length; i++) {
208            propName = objectAProperties[i];
209
210            // If the property is not in the object B properties, continue with the next property
211            if ($.inArray(propName, objectBProperties) > -1) {
212                // If values of same property are not equal, objects are not equivalent
213                if (objectA[propName] !== objectB[propName]) {
214                    return false;
215                }
216            }
217        }
218
219        // If we made it this far, objects are considered equivalent
220        return true;
221    };
222
223    var escapeHTML = function (text) {
224        if (typeof text === 'string') {
225            return text
226                .replace(/&/g, '&amp;')
227                .replace(/</g, '&lt;')
228                .replace(/>/g, '&gt;')
229                .replace(/"/g, '&quot;')
230                .replace(/'/g, '&#039;')
231                .replace(/`/g, '&#x60;');
232        }
233        return text;
234    };
235
236    var getRealDataAttr = function (dataAttr) {
237        for (var attr in dataAttr) {
238            var auxAttr = attr.split(/(?=[A-Z])/).join('-').toLowerCase();
239            if (auxAttr !== attr) {
240                dataAttr[auxAttr] = dataAttr[attr];
241                delete dataAttr[attr];
242            }
243        }
244
245        return dataAttr;
246    };
247
248    var getItemField = function (item, field, escape) {
249        var value = item;
250
251        if (typeof field !== 'string' || item.hasOwnProperty(field)) {
252            return escape ? escapeHTML(item[field]) : item[field];
253        }
254        var props = field.split('.');
255        for (var p in props) {
256            if (props.hasOwnProperty(p)) {
257                value = value && value[props[p]];
258            }
259        }
260        return escape ? escapeHTML(value) : value;
261    };
262
263    var isIEBrowser = function () {
264        return !!(navigator.userAgent.indexOf("MSIE ") > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./));
265    };
266
267    var objectKeys = function () {
268        // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
269        if (!Object.keys) {
270            Object.keys = (function() {
271                var hasOwnProperty = Object.prototype.hasOwnProperty,
272                    hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
273                    dontEnums = [
274                        'toString',
275                        'toLocaleString',
276                        'valueOf',
277                        'hasOwnProperty',
278                        'isPrototypeOf',
279                        'propertyIsEnumerable',
280                        'constructor'
281                    ],
282                    dontEnumsLength = dontEnums.length;
283
284                return function(obj) {
285                    if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
286                        throw new TypeError('Object.keys called on non-object');
287                    }
288
289                    var result = [], prop, i;
290
291                    for (prop in obj) {
292                        if (hasOwnProperty.call(obj, prop)) {
293                            result.push(prop);
294                        }
295                    }
296
297                    if (hasDontEnumBug) {
298                        for (i = 0; i < dontEnumsLength; i++) {
299                            if (hasOwnProperty.call(obj, dontEnums[i])) {
300                                result.push(dontEnums[i]);
301                            }
302                        }
303                    }
304                    return result;
305                };
306            }());
307        }
308    };
309
310    // BOOTSTRAP TABLE CLASS DEFINITION
311    // ======================
312
313    var BootstrapTable = function (el, options) {
314        this.options = options;
315        this.$el = $(el);
316        this.$el_ = this.$el.clone();
317        this.timeoutId_ = 0;
318        this.timeoutFooter_ = 0;
319
320        this.init();
321    };
322
323    BootstrapTable.DEFAULTS = {
324        classes: 'table table-hover',
325        sortClass: undefined,
326        locale: undefined,
327        height: undefined,
328        undefinedText: '-',
329        sortName: undefined,
330        sortOrder: 'asc',
331        sortStable: false,
332        rememberOrder: false,
333        striped: false,
334        columns: [[]],
335        data: [],
336        totalField: 'total',
337        dataField: 'rows',
338        method: 'get',
339        url: undefined,
340        ajax: undefined,
341        cache: true,
342        contentType: 'application/json',
343        dataType: 'json',
344        ajaxOptions: {},
345        queryParams: function (params) {
346            return params;
347        },
348        queryParamsType: 'limit', // undefined
349        responseHandler: function (res) {
350            return res;
351        },
352        pagination: false,
353        onlyInfoPagination: false,
354        paginationLoop: true,
355        sidePagination: 'client', // client or server
356        totalRows: 0, // server side need to set
357        pageNumber: 1,
358        pageSize: 10,
359        pageList: [10, 25, 50, 100],
360        paginationHAlign: 'right', //right, left
361        paginationVAlign: 'bottom', //bottom, top, both
362        paginationDetailHAlign: 'left', //right, left
363        paginationPreText: '&lsaquo;',
364        paginationNextText: '&rsaquo;',
365        search: false,
366        searchOnEnterKey: false,
367        strictSearch: false,
368        searchAlign: 'right',
369        selectItemName: 'btSelectItem',
370        showHeader: true,
371        showFooter: false,
372        showColumns: false,
373        showPaginationSwitch: false,
374        showRefresh: false,
375        showToggle: false,
376        showFullscreen: false,
377        smartDisplay: true,
378        escape: false,
379        minimumCountColumns: 1,
380        idField: undefined,
381        uniqueId: undefined,
382        cardView: false,
383        detailView: false,
384        detailFormatter: function (index, row) {
385            return '';
386        },
387        detailFilter: function (index, row) {
388            return true;
389        },
390        trimOnSearch: true,
391        clickToSelect: false,
392        singleSelect: false,
393        toolbar: undefined,
394        toolbarAlign: 'left',
395        buttonsToolbar: undefined,
396        buttonsAlign: 'right',
397        checkboxHeader: true,
398        sortable: true,
399        silentSort: true,
400        maintainSelected: false,
401        searchTimeOut: 500,
402        searchText: '',
403        iconSize: undefined,
404        buttonsClass: bs.buttonsClass,
405        iconsPrefix: bs.iconsPrefix, // glyphicon or fa (font awesome)
406        icons: bs.icons,
407
408        customSearch: $.noop,
409
410        customSort: $.noop,
411
412        ignoreClickToSelectOn: function (element) {
413            return $.inArray(element.tagName, ['A', 'BUTTON']);
414        },
415
416        rowStyle: function (row, index) {
417            return {};
418        },
419
420        rowAttributes: function (row, index) {
421            return {};
422        },
423
424        footerStyle: function (row, index) {
425            return {};
426        },
427
428        onAll: function (name, args) {
429            return false;
430        },
431        onClickCell: function (field, value, row, $element) {
432            return false;
433        },
434        onDblClickCell: function (field, value, row, $element) {
435            return false;
436        },
437        onClickRow: function (item, $element) {
438            return false;
439        },
440        onDblClickRow: function (item, $element) {
441            return false;
442        },
443        onSort: function (name, order) {
444            return false;
445        },
446        onCheck: function (row) {
447            return false;
448        },
449        onUncheck: function (row) {
450            return false;
451        },
452        onCheckAll: function (rows) {
453            return false;
454        },
455        onUncheckAll: function (rows) {
456            return false;
457        },
458        onCheckSome: function (rows) {
459            return false;
460        },
461        onUncheckSome: function (rows) {
462            return false;
463        },
464        onLoadSuccess: function (data) {
465            return false;
466        },
467        onLoadError: function (status) {
468            return false;
469        },
470        onColumnSwitch: function (field, checked) {
471            return false;
472        },
473        onPageChange: function (number, size) {
474            return false;
475        },
476        onSearch: function (text) {
477            return false;
478        },
479        onToggle: function (cardView) {
480            return false;
481        },
482        onPreBody: function (data) {
483            return false;
484        },
485        onPostBody: function () {
486            return false;
487        },
488        onPostHeader: function () {
489            return false;
490        },
491        onExpandRow: function (index, row, $detail) {
492            return false;
493        },
494        onCollapseRow: function (index, row) {
495            return false;
496        },
497        onRefreshOptions: function (options) {
498            return false;
499        },
500        onRefresh: function (params) {
501          return false;
502        },
503        onResetView: function () {
504            return false;
505        },
506        onScrollBody: function () {
507            return false;
508        }
509    };
510
511    BootstrapTable.LOCALES = {};
512
513    BootstrapTable.LOCALES['en-US'] = BootstrapTable.LOCALES.en = {
514        formatLoadingMessage: function () {
515            return 'Loading, please wait...';
516        },
517        formatRecordsPerPage: function (pageNumber) {
518            return sprintf('%s rows per page', pageNumber);
519        },
520        formatShowingRows: function (pageFrom, pageTo, totalRows) {
521            return sprintf('Showing %s to %s of %s rows', pageFrom, pageTo, totalRows);
522        },
523        formatDetailPagination: function (totalRows) {
524            return sprintf('Showing %s rows', totalRows);
525        },
526        formatSearch: function () {
527            return 'Search';
528        },
529        formatNoMatches: function () {
530            return 'No matching records found';
531        },
532        formatPaginationSwitch: function () {
533            return 'Hide/Show pagination';
534        },
535        formatRefresh: function () {
536            return 'Refresh';
537        },
538        formatToggle: function () {
539            return 'Toggle';
540        },
541        formatFullscreen: function () {
542            return 'Fullscreen';
543        },
544        formatColumns: function () {
545            return 'Columns';
546        },
547        formatAllRows: function () {
548            return 'All';
549        }
550    };
551
552    $.extend(BootstrapTable.DEFAULTS, BootstrapTable.LOCALES['en-US']);
553
554    BootstrapTable.COLUMN_DEFAULTS = {
555        radio: false,
556        checkbox: false,
557        checkboxEnabled: true,
558        field: undefined,
559        title: undefined,
560        titleTooltip: undefined,
561        'class': undefined,
562        align: undefined, // left, right, center
563        halign: undefined, // left, right, center
564        falign: undefined, // left, right, center
565        valign: undefined, // top, middle, bottom
566        width: undefined,
567        sortable: false,
568        order: 'asc', // asc, desc
569        visible: true,
570        switchable: true,
571        clickToSelect: true,
572        formatter: undefined,
573        footerFormatter: undefined,
574        events: undefined,
575        sorter: undefined,
576        sortName: undefined,
577        cellStyle: undefined,
578        searchable: true,
579        searchFormatter: true,
580        cardVisible: true,
581        escape: false,
582        showSelectTitle: false
583    };
584
585    BootstrapTable.EVENTS = {
586        'all.bs.table': 'onAll',
587        'click-cell.bs.table': 'onClickCell',
588        'dbl-click-cell.bs.table': 'onDblClickCell',
589        'click-row.bs.table': 'onClickRow',
590        'dbl-click-row.bs.table': 'onDblClickRow',
591        'sort.bs.table': 'onSort',
592        'check.bs.table': 'onCheck',
593        'uncheck.bs.table': 'onUncheck',
594        'check-all.bs.table': 'onCheckAll',
595        'uncheck-all.bs.table': 'onUncheckAll',
596        'check-some.bs.table': 'onCheckSome',
597        'uncheck-some.bs.table': 'onUncheckSome',
598        'load-success.bs.table': 'onLoadSuccess',
599        'load-error.bs.table': 'onLoadError',
600        'column-switch.bs.table': 'onColumnSwitch',
601        'page-change.bs.table': 'onPageChange',
602        'search.bs.table': 'onSearch',
603        'toggle.bs.table': 'onToggle',
604        'pre-body.bs.table': 'onPreBody',
605        'post-body.bs.table': 'onPostBody',
606        'post-header.bs.table': 'onPostHeader',
607        'expand-row.bs.table': 'onExpandRow',
608        'collapse-row.bs.table': 'onCollapseRow',
609        'refresh-options.bs.table': 'onRefreshOptions',
610        'reset-view.bs.table': 'onResetView',
611        'refresh.bs.table': 'onRefresh',
612        'scroll-body.bs.table': 'onScrollBody'
613    };
614
615    BootstrapTable.prototype.init = function () {
616        this.initLocale();
617        this.initContainer();
618        this.initTable();
619        this.initHeader();
620        this.initData();
621        this.initHiddenRows();
622        this.initFooter();
623        this.initToolbar();
624        this.initPagination();
625        this.initBody();
626        this.initSearchText();
627        this.initServer();
628    };
629
630    BootstrapTable.prototype.initLocale = function () {
631        if (this.options.locale) {
632            var parts = this.options.locale.split(/-|_/);
633            parts[0].toLowerCase();
634            if (parts[1]) {
635                parts[1].toUpperCase();
636            }
637            if ($.fn.bootstrapTable.locales[this.options.locale]) {
638                // locale as requested
639                $.extend(this.options, $.fn.bootstrapTable.locales[this.options.locale]);
640            } else if ($.fn.bootstrapTable.locales[parts.join('-')]) {
641                // locale with sep set to - (in case original was specified with _)
642                $.extend(this.options, $.fn.bootstrapTable.locales[parts.join('-')]);
643            } else if ($.fn.bootstrapTable.locales[parts[0]]) {
644                // short locale language code (i.e. 'en')
645                $.extend(this.options, $.fn.bootstrapTable.locales[parts[0]]);
646            }
647        }
648    };
649
650    BootstrapTable.prototype.initContainer = function () {
651        this.$container = $([
652            '<div class="bootstrap-table">',
653            '<div class="fixed-table-toolbar"></div>',
654            this.options.paginationVAlign === 'top' || this.options.paginationVAlign === 'both' ?
655                '<div class="fixed-table-pagination" style="clear: both;"></div>' :
656                '',
657            '<div class="fixed-table-container">',
658            '<div class="fixed-table-header"><table></table></div>',
659            '<div class="fixed-table-body">',
660            '<div class="fixed-table-loading">',
661            this.options.formatLoadingMessage(),
662            '</div>',
663            '</div>',
664            '<div class="fixed-table-footer"><table><tr></tr></table></div>',
665            '</div>',
666            this.options.paginationVAlign === 'bottom' || this.options.paginationVAlign === 'both' ?
667                '<div class="fixed-table-pagination"></div>' :
668                '',
669            '</div>'
670        ].join(''));
671
672        this.$container.insertAfter(this.$el);
673        this.$tableContainer = this.$container.find('.fixed-table-container');
674        this.$tableHeader = this.$container.find('.fixed-table-header');
675        this.$tableBody = this.$container.find('.fixed-table-body');
676        this.$tableLoading = this.$container.find('.fixed-table-loading');
677        this.$tableFooter = this.$container.find('.fixed-table-footer');
678        // checking if custom table-toolbar exists or not
679        if (this.options.buttonsToolbar) {
680            this.$toolbar = $('body').find(this.options.buttonsToolbar);
681        } else {
682            this.$toolbar = this.$container.find('.fixed-table-toolbar');
683        }
684        this.$pagination = this.$container.find('.fixed-table-pagination');
685
686        this.$tableBody.append(this.$el);
687        this.$container.after('<div class="clearfix"></div>');
688
689        this.$el.addClass(this.options.classes);
690        if (this.options.striped) {
691            this.$el.addClass('table-striped');
692        }
693        if ($.inArray('table-no-bordered', this.options.classes.split(' ')) !== -1) {
694            this.$tableContainer.addClass('table-no-bordered');
695        }
696    };
697
698    BootstrapTable.prototype.initTable = function () {
699        var that = this,
700            columns = [],
701            data = [];
702
703        this.$header = this.$el.find('>thead');
704        if (!this.$header.length) {
705            this.$header = $('<thead></thead>').appendTo(this.$el);
706        }
707        this.$header.find('tr').each(function () {
708            var column = [];
709
710            $(this).find('th').each(function () {
711                // Fix #2014 - getFieldIndex and elsewhere assume this is string, causes issues if not
712                if (typeof $(this).data('field') !== 'undefined') {
713                    $(this).data('field', $(this).data('field') + '');
714                }
715                column.push($.extend({}, {
716                    title: $(this).html(),
717                    'class': $(this).attr('class'),
718                    titleTooltip: $(this).attr('title'),
719                    rowspan: $(this).attr('rowspan') ? +$(this).attr('rowspan') : undefined,
720                    colspan: $(this).attr('colspan') ? +$(this).attr('colspan') : undefined
721                }, $(this).data()));
722            });
723            columns.push(column);
724        });
725        if (!$.isArray(this.options.columns[0])) {
726            this.options.columns = [this.options.columns];
727        }
728        this.options.columns = $.extend(true, [], columns, this.options.columns);
729        this.columns = [];
730        this.fieldsColumnsIndex = [];
731
732        setFieldIndex(this.options.columns);
733        $.each(this.options.columns, function (i, columns) {
734            $.each(columns, function (j, column) {
735                column = $.extend({}, BootstrapTable.COLUMN_DEFAULTS, column);
736
737                if (typeof column.fieldIndex !== 'undefined') {
738                    that.columns[column.fieldIndex] = column;
739                    that.fieldsColumnsIndex[column.field] = column.fieldIndex;
740                }
741
742                that.options.columns[i][j] = column;
743            });
744        });
745
746        // if options.data is setting, do not process tbody data
747        if (this.options.data.length) {
748            return;
749        }
750
751        var m = [];
752        this.$el.find('>tbody>tr').each(function (y) {
753            var row = {};
754
755            // save tr's id, class and data-* attributes
756            row._id = $(this).attr('id');
757            row._class = $(this).attr('class');
758            row._data = getRealDataAttr($(this).data());
759
760            $(this).find('>td').each(function (x) {
761                var $this = $(this),
762                    cspan = +$this.attr('colspan') || 1,
763                    rspan = +$this.attr('rowspan') || 1,
764                    tx,
765                    ty;
766
767                // skip already occupied cells in current row
768                for (; m[y] && m[y][x]; x++);
769
770                for (tx = x; tx < x + cspan; tx++) { //mark matrix elements occupied by current cell with true
771                    for (ty = y; ty < y + rspan; ty++) {
772                        if (!m[ty]) { //fill missing rows
773                            m[ty] = [];
774                        }
775                        m[ty][tx] = true;
776                    }
777                }
778
779                var field = that.columns[x].field;
780
781                row[field] = $(this).html();
782                // save td's id, class and data-* attributes
783                row['_' + field + '_id'] = $(this).attr('id');
784                row['_' + field + '_class'] = $(this).attr('class');
785                row['_' + field + '_rowspan'] = $(this).attr('rowspan');
786                row['_' + field + '_colspan'] = $(this).attr('colspan');
787                row['_' + field + '_title'] = $(this).attr('title');
788                row['_' + field + '_data'] = getRealDataAttr($(this).data());
789            });
790            data.push(row);
791        });
792        this.options.data = data;
793        if (data.length) this.fromHtml = true;
794    };
795
796    BootstrapTable.prototype.initHeader = function () {
797        var that = this,
798            visibleColumns = {},
799            html = [];
800
801        this.header = {
802            fields: [],
803            styles: [],
804            classes: [],
805            formatters: [],
806            events: [],
807            sorters: [],
808            sortNames: [],
809            cellStyles: [],
810            searchables: []
811        };
812
813        $.each(this.options.columns, function (i, columns) {
814            html.push('<tr>');
815
816            if (i === 0 && !that.options.cardView && that.options.detailVie
816w) {
817                html.push(sprintf('<th class="detail" rowspan="%s"><div class="fht-cell"></div></th>',
818                    that.options.columns.length));
819            }
820
821            $.each(columns, function (j, column) {
822                var text = '',
823                    halign = '', // header align style
824                    align = '', // body align style
825                    style = '',
826                    class_ = sprintf(' class="%s"', column['class']),
827                    order = that.options.sortOrder || column.order,
828                    unitWidth = 'px',
829                    width = column.width;
830
831                if (column.width !== undefined && (!that.options.cardView)) {
832                    if (typeof column.width === 'string') {
833                        if (column.width.indexOf('%') !== -1) {
834                            unitWidth = '%';
835                        }
836                    }
837                }
838                if (column.width && typeof column.width === 'string') {
839                    width = column.width.replace('%', '').replace('px', '');
840                }
841
842                halign = sprintf('text-align: %s; ', column.halign ? column.halign : column.align);
843                align = sprintf('text-align: %s; ', column.align);
844                style = sprintf('vertical-align: %s; ', column.valign);
845                style += sprintf('width: %s; ', (column.checkbox || column.radio) && !width ?
846                    (!column.showSelectTitle ? '36px' : undefined) :
847                    (width ? width + unitWidth : undefined));
848
849                if (typeof column.fieldIndex !== 'undefined') {
850                    that.header.fields[column.fieldIndex] = column.field;
851                    that.header.styles[column.fieldIndex] = align + style;
852                    that.header.classes[column.fieldIndex] = class_;
853                    that.header.formatters[column.fieldIndex] = column.formatter;
854                    that.header.events[column.fieldIndex] = column.events;
855                    that.header.sorters[column.fieldIndex] = column.sorter;
856                    that.header.sortNames[column.fieldIndex] = column.sortName;
857                    that.header.cellStyles[column.fieldIndex] = column.cellStyle;
858                    that.header.searchables[column.fieldIndex] = column.searchable;
859
860                    if (!column.visible) {
861                        return;
862                    }
863
864                    if (that.options.cardView && (!column.cardVisible)) {
865                        return;
866                    }
867
868                    visibleColumns[column.field] = column;
869                }
870
871                html.push('<th' + sprintf(' title="%s"', column.titleTooltip),
872                    column.checkbox || column.radio ?
873                        sprintf(' class="bs-checkbox %s"', column['class'] || '') :
874                        class_,
875                    sprintf(' style="%s"', halign + style),
876                    sprintf(' rowspan="%s"', column.rowspan),
877                    sprintf(' colspan="%s"', column.colspan),
878                    sprintf(' data-field="%s"', column.field),
879                    j === 0 && column.fieldIndex ? ' data-not-first-th' : '',
880                    '>');
881
882                html.push(sprintf('<div class="th-inner %s">', that.options.sortable && column.sortable ?
883                    'sortable both' : ''));
884
885                text = that.options.escape ? escapeHTML(column.title) : column.title;
886
887                var title = text;
888                if (column.checkbox) {
889                    text = '';
890                    if (!that.options.singleSelect && that.options.checkboxHeader) {
891                        text = '<input name="btSelectAll" type="checkbox" />';
892                    }
893                    that.header.stateField = column.field;
894                }
895                if (column.radio) {
896                    text = '';
897                    that.header.stateField = column.field;
898                    that.options.singleSelect = true;
899                }
900                if (!text && column.showSelectTitle) {
901                    text += title;
902                }
903
904                html.push(text);
905                html.push('</div>');
906                html.push('<div class="fht-cell"></div>');
907                html.push('</div>');
908                html.push('</th>');
909            });
910            html.push('</tr>');
911        });
912
913        this.$header.html(html.join(''));
914        this.$header.find('th[data-field]').each(function (i) {
915            $(this).data(visibleColumns[$(this).data('field')]);
916        });
917        this.$container.off('click', '.th-inner').on('click', '.th-inner', function (event) {
918            var $this = $(this);
919
920            if (that.options.detailView && !$this.parent().hasClass('bs-checkbox')) {
921                if ($this.closest('.bootstrap-table')[0] !== that.$container[0]) {
922                    return false;
923                }
924            }
925
926            if (that.options.sortable && $this.parent().data().sortable) {
927                that.onSort(event);
928            }
929        });
930
931        this.$header.children().children().off('keypress').on('keypress', function (event) {
932            if (that.options.sortable && $(this).data().sortable) {
933                var code = event.keyCode || event.which;
934                if (code == 13) { //Enter keycode
935                    that.onSort(event);
936                }
937            }
938        });
939
940        $(window).off('resize.bootstrap-table');
941        if (!this.options.showHeader || this.options.cardView) {
942            this.$header.hide();
943            this.$tableHeader.hide();
944            this.$tableLoading.css('top', 0);
945        } else {
946            this.$header.show();
947            this.$tableHeader.show();
948            this.$tableLoading.css('top', this.$header.outerHeight() + 1);
949            // Assign the correct sortable arrow
950            this.getCaret();
951            $(window).on('resize.bootstrap-table', $.proxy(this.resetWidth, this));
952        }
953
954        this.$selectAll = this.$header.find('[name="btSelectAll"]');
955        this.$selectAll.off('click').on('click', function () {
956                var checked = $(this).prop('checked');
957                that[checked ? 'checkAll' : 'uncheckAll']();
958                that.updateSelected();
959            });
960    };
961
962    BootstrapTable.prototype.initFooter = function () {
963        if (!this.options.showFooter || this.options.cardView) {
964            this.$tableFooter.hide();
965        } else {
966            this.$tableFooter.show();
967        }
968    };
969
970    /**
971     * @param data
972     * @param type: append / prepend
973     */
974    BootstrapTable.prototype.initData = function (data, type) {
975        if (type === 'append') {
976            this.options.data = this.options.data.concat(data);
977        } else if (type === 'prepend') {
978            this.options.data = [].concat(data).concat(this.options.data);
979        } else {
980            this.options.data = data || this.options.data;
981        }
982
983        this.data = this.options.data;
984
985        if (this.options.sidePagination === 'server') {
986            return;
987        }
988        this.initSort();
989    };
990
991    BootstrapTable.prototype.initSort = function () {
992        var that = this,
993            name = this.options.sortName,
994            order = this.options.sortOrder === 'desc' ? -1 : 1,
995            index = $.inArray(this.options.sortName, this.header.fields),
996            timeoutId = 0;
997
998        if (this.options.customSort !== $.noop) {
999            this.options.customSort.apply(this, [this.options.sortName, this.options.sortOrder]);
1000            return;
1001        }
1002
1003        if (index !== -1) {
1004            if (this.options.sortStable) {
1005                $.each(this.data, function (i, row) {
1006                    row._position = i;
1007                });
1008            }
1009
1010            this.data.sort(function (a, b) {
1011                if (that.header.sortNames[index]) {
1012                    name = that.header.sortNames[index];
1013                }
1014                var aa = getItemField(a, name, that.options.escape),
1015                    bb = getItemField(b, name, that.options.escape),
1016                    value = calculateObjectValue(that.header, that.header.sorters[index], [aa, bb, a, b]);
1017
1018                if (value !== undefined) {
1019                    if (that.options.sortStable && value === 0) {
1020                        return a._position - b._position;
1021                    }
1022                    return order * value;
1023                }
1024
1025                // Fix #161: undefined or null string sort bug.
1026                if (aa === undefined || aa === null) {
1027                    aa = '';
1028                }
1029                if (bb === undefined || bb === null) {
1030                    bb = '';
1031                }
1032
1033                if (that.options.sortStable && aa === bb) {
1034                    aa = a._position;
1035                    bb = b._position;
1036                    return a._position - b._position;
1037                }
1038
1039                // IF both values are numeric, do a numeric comparison
1040                if ($.isNumeric(aa) && $.isNumeric(bb)) {
1041                    // Convert numerical values form string to float.
1042                    aa = parseFloat(aa);
1043                    bb = parseFloat(bb);
1044                    if (aa < bb) {
1045                        return order * -1;
1046                    }
1047                    return order;
1048                }
1049
1050                if (aa === bb) {
1051                    return 0;
1052                }
1053
1054                // If value is not a string, convert to string
1055                if (typeof aa !== 'string') {
1056                    aa = aa.toString();
1057                }
1058
1059                if (aa.localeCompare(bb) === -1) {
1060                    return order * -1;
1061                }
1062
1063                return order;
1064            });
1065
1066            if (this.options.sortClass !== undefined) {
1067                clearTimeout(timeoutId);
1068                timeoutId = setTimeout(function () {
1069                    that.$el.removeClass(that.options.sortClass);
1070                    var index = that.$header.find(sprintf('[data-field="%s"]',
1071                        that.options.sortName).index() + 1);
1072                    that.$el.find(sprintf('tr td:nth-child(%s)', index))
1073                        .addClass(that.options.sortClass);
1074                }, 250);
1075            }
1076        }
1077    };
1078
1079    BootstrapTable.prototype.onSort = function (event) {
1080        var $this = event.type === "keypress" ? $(event.currentTarget) : $(event.currentTarget).parent(),
1081            $this_ = this.$header.find('th').eq($this.index());
1082
1083        this.$header.add(this.$header_).find('span.order').remove();
1084
1085        if (this.options.sortName === $this.data('field')) {
1086            this.options.sortOrder = this.options.sortOrder === 'asc' ? 'desc' : 'asc';
1087        } else {
1088            this.options.sortName = $this.data('field');
1089            if (this.options.rememberOrder) {
1090                this.options.sortOrder = $this.data('order') === 'asc' ? 'desc' : 'asc';
1091            } else {
1092                this.options.sortOrder = this.columns[this.fieldsColumnsIndex[$this.data('field')]].order;
1093            }
1094        }
1095        this.trigger('sort', this.options.sortName, this.options.sortOrder);
1096
1097        $this.add($this_).data('order', this.options.sortOrder);
1098
1099        // Assign the correct sortable arrow
1100        this.getCaret();
1101
1102        if (this.options.sidePagination === 'server') {
1103            this.initServer(this.options.silentSort);
1104            return;
1105        }
1106
1107        this.initSort();
1108        this.initBody();
1109    };
1110
1111    BootstrapTable.prototype.initToolbar = function () {
1112        var that = this,
1113            html = [],
1114            timeoutId = 0,
1115            $keepOpen,
1116            $search,
1117            switchableCount = 0;
1118
1119        if (this.$toolbar.find('.bs-bars').children().length) {
1120            $('body').append($(this.options.toolbar));
1121        }
1122        this.$toolbar.html('');
1123
1124        if (typeof this.options.toolbar === 'string' || typeof this.options.toolbar === 'object') {
1125            $(sprintf('<div class="bs-bars %s-%s"></div>', bs.pullClass, this.options.toolbarAlign))
1126                .appendTo(this.$toolbar)
1127                .append($(this.options.toolbar));
1128        }
1129
1130        // showColumns, showToggle, showRefresh
1131        html = [sprintf('<div class="columns columns-%s btn-group %s-%s">',
1132            this.options.buttonsAlign, bs.pullClass, this.options.buttonsAlign)];
1133
1134        if (typeof this.options.icons === 'string') {
1135            this.options.icons = calculateObjectValue(null, this.options.icons);
1136        }
1137
1138        if (this.options.showPaginationSwitch) {
1139            html.push(sprintf('<button class="btn' +
1140                    sprintf(' btn-%s', this.options.buttonsClass) +
1141                    sprintf(' btn-%s', this.options.iconSize) +
1142                    '" type="button" name="paginationSwitch" aria-label="pagination Switch" title="%s">',
1143                    this.options.formatPaginationSwitch()),
1144                sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.paginationSwitchDown),
1145                '</button>');
1146        }
1147
1148        if (this.options.showFullscreen) {
1149            this.$toolbar.find('button[name="fullscreen"]')
1150                .off('click').on('click', $.proxy(this.toggleFullscreen, this));
1151        }
1152
1153        if (this.options.showRefresh) {
1154            html.push(sprintf('<button class="btn' +
1155                    sprintf(' btn-%s', this.options.buttonsClass) +
1156                    sprintf(' btn-%s', this.options.iconSize) +
1157                    '" type="button" name="refresh" aria-label="refresh" title="%s">',
1158                    this.options.formatRefresh()),
1159                sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.refresh),
1160                '</button>');
1161        }
1162
1163        if (this.options.showToggle) {
1164            html.push(sprintf('<button class="btn' +
1165                    sprintf(' btn-%s', this.options.buttonsClass) +
1166                    sprintf(' btn-%s', this.options.iconSize) +
1167                    '" type="button" name="toggle" aria-label="toggle" title="%s">',
1168                    this.options.formatToggle()),
1169                sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.toggleOff),
1170                '</button>');
1171        }
1172
1173        if (this.options.showFullscreen) {
1174            html.push(sprintf('<button class="btn' +
1175                    sprintf(' btn-%s', this.options.buttonsClass) +
1176                    sprintf(' btn-%s', this.options.iconSize) +
1177                    '" type="button" name="fullscreen" aria-label="fullscreen" title="%s">',
1178                    this.options.formatFullscreen()),
1179                    sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.fullscreen),
1180                    '</button>');
1181        }
1182
1183        if (this.options.showColumns) {
1184            html.push(sprintf('<div class="keep-open btn-group" title="%s">',
1185                    this.options.formatColumns()),
1186                '<button type="button" aria-label="columns" class="btn' +
1187                sprintf(' btn-%s', this.options.buttonsClass) +
1188                sprintf(' btn-%s', this.options.iconSize) +
1189                ' dropdown-toggle" data-toggle="dropdown">',
1190                sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.columns),
1191                ' <span class="caret"></span>',
1192                '</button>',
1193                bs.toobarDropdowHtml[0]);
1194
1195            $.each(this.columns, function (i, column) {
1196                if (column.radio || column.checkbox) {
1197                    return;
1198                }
1199
1200                if (that.options.cardView && !column.cardVisible) {
1201                    return;
1202                }
1203
1204                var checked = column.visible ? ' checked="checked"' : '';
1205
1206                if (column.switchable) {
1207                    html.push(sprintf(bs.toobarDropdowItemHtml,
1208                        sprintf('<input type="checkbox" data-field="%s" value="%s"%s> %s',
1209                        column.field, i, checked, column.title)));
1210                    switchableCount++;
1211                }
1212            });
1213            html.push(bs.toobarDropdowHtml[1], '</div>');
1214        }
1215
1216        html.push('</div>');
1217
1218        // Fix #188: this.showToolbar is for extensions
1219        if (this.showToolbar || html.length > 2) {
1220            this.$toolbar.append(html.join(''));
1221        }
1222
1223        if (this.options.showPaginationSwitch) {
1224            this.$toolbar.find('button[name="paginationSwitch"]')
1225                .off('click').on('click', $.proxy(this.togglePagination, this));
1226        }
1227
1228        if (this.options.showRefresh) {
1229            this.$toolbar.find('button[name="refresh"]')
1230                .off('click').on('click', $.proxy(this.refresh, this));
1231        }
1232
1233        if (this.options.showToggle) {
1234            this.$toolbar.find('button[name="toggle"]')
1235                .off('click').on('click', function () {
1236                    that.toggleView();
1237                });
1238        }
1239
1240        if (this.options.showColumns) {
1241            $keepOpen = this.$toolbar.find('.keep-open');
1242
1243            if (switchableCount <= this.options.minimumCountColumns) {
1244                $keepOpen.find('input').prop('disabled', true);
1245            }
1246
1247            $keepOpen.find('li').off('click').on('click', function (event) {
1248                event.stopImmediatePropagation();
1249            });
1250            $keepOpen.find('input').off('click').on('click', function () {
1251                var $this = $(this);
1252
1253                that.toggleColumn($(this).val(), $this.prop('checked'), false);
1254                that.trigger('column-switch', $(this).data('field'), $this.prop('checked'));
1255            });
1256        }
1257
1258        if (this.options.search) {
1259            html = [];
1260            html.push(
1261                sprintf('<div class="%s-%s search">', bs.pullClass, this.options.searchAlign),
1262                sprintf('<input class="form-control' +
1263                    sprintf(' input-%s', this.options.iconSize) +
1264                    '" type="text" placeholder="%s">',
1265                    this.options.formatSearch()),
1266                '</div>');
1267
1268            this.$toolbar.append(html.join(''));
1269            $search = this.$toolbar.find('.search input');
1270            $search.off('keyup drop blur').on('keyup drop blur', function (event) {
1271                if (that.options.searchOnEnterKey && event.keyCode !== 13) {
1272                    return;
1273                }
1274
1275                if ($.inArray(event.keyCode, [37, 38, 39, 40]) > -1) {
1276                    return;
1277                }
1278
1279                clearTimeout(timeoutId); // doesn't matter if it's 0
1280                timeoutId = setTimeout(function () {
1281                    that.onSearch(event);
1282                }, that.options.searchTimeOut);
1283            });
1284
1285            if (isIEBrowser()) {
1286                $search.off('mouseup').on('mouseup', function (event) {
1287                    clearTimeout(timeoutId); // doesn't matter if it's 0
1288                    timeoutId = setTimeout(function () {
1289                        that.onSearch(event);
1290                    }, that.options.searchTimeOut);
1291                });
1292            }
1293        }
1294    };
1295
1296    BootstrapTable.prototype.onSearch = function (event) {
1297        var text = $.trim($(event.currentTarget).val());
1298
1299        // trim search input
1300        if (this.options.trimOnSearch && $(event.currentTarget).val() !== text) {
1301            $(event.currentTarget).val(text);
1302        }
1303
1304        if (text === this.searchText) {
1305            return;
1306        }
1307        this.searchText = text;
1308        this.options.searchText = text;
1309
1310        this.options.pageNumber = 1;
1311        this.initSearch();
1312        if (event.firedByInitSearchText) {
1313            if (this.options.sidePagination === 'client') {
1314                this.updatePagination();
1315            }
1316        } else {
1317            this.updatePagination();
1318        }
1319        this.trigger('search', text);
1320    };
1321
1322    BootstrapTable.prototype.initSearch = function () {
1323        var that = this;
1324
1325        if (this.options.sidePagination !== 'server') {
1326            if (this.options.customSearch !== $.noop) {
1327                window[this.options.customSearch].apply(this, [this.searchText]);
1328                return;
1329            }
1330
1331            var s = this.searchText && (this.options.escape ?
1332                escapeHTML(this.searchText) : this.searchText).toLowerCase();
1333            var f = $.isEmptyObject(this.filterColumns) ? null : this.filterColumns;
1334
1335            // Check filter
1336            this.data = f ? $.grep(this.options.data, function (item, i) {
1337                for (var key in f) {
1338                    if ($.isArray(f[key]) && $.inArray(item[key], f[key]) === -1 ||
1339                            !$.isArray(f[key]) && item[key] !== f[key]) {
1340                        return false;
1341                    }
1342                }
1343                return true;
1344            }) : this.options.data;
1345
1346            this.data = s ? $.grep(this.data, function (item, i) {
1347                for (var j = 0; j < that.header.fields.length; j++) {
1348
1349                    if (!that.header.searchables[j]) {
1350                        continue;
1351                    }
1352
1353                    var key = $.isNumeric(that.header.fields[j]) ? parseInt(that.header.fields[j], 10) : that.header.fields[j];
1354                    var column = that.columns[that.fieldsColumnsIndex[key]];
1355                    var value;
1356
1357                    if (typeof key === 'string') {
1358                        value = item;
1359                        var props = key.split('.');
1360                        for (var prop_index = 0; prop_index < props.length; prop_index++) {
1361                            if (value[props[prop_index]] != null) {
1362                                value = value[props[prop_index]];
1363                            }
1364                        }
1365
1366                        // Fix #142: respect searchForamtter boolean
1367                        if (column && column.searchFormatter) {
1368                            value = calculateObjectValue(column,
1369                                that.header.formatters[j], [value, item, i], value);
1370                        }
1371                    } else {
1372                        value = item[key];
1373                    }
1374
1375                    if (typeof value === 'string' || typeof value === 'number') {
1376                        if (that.options.strictSearch) {
1377                            if ((value + '').toLowerCase() === s) {
1378                                return true;
1379                            }
1380                        } else {
1381                            if ((value + '').toLowerCase().indexOf(s) !== -1) {
1382                                return true;
1383                            }
1384                        }
1385                    }
1386                }
1387                return false;
1388            }) : this.data;
1389        }
1390    };
1391
1392    BootstrapTable.prototype.initPagination = function () {
1393        if (!this.options.pagination) {
1394            this.$pagination.hide();
1395            return;
1396        } else {
1397            this.$pagination.show();
1398        }
1399
1400        var that = this,
1401            html = [],
1402            $allSelected = false,
1403            i, from, to,
1404            $pageList,
1405            $pre,
1406            $next,
1407            $number,
1408            data = this.getData(),
1409            pageList = this.options.pageList;
1410
1411        if (this.options.sidePagination !== 'server') {
1412            this.options.totalRows = data.length;
1413        }
1414
1415        this.totalPages = 0;
1416        if (this.options.totalRows) {
1417            if (this.options.pageSize === this.options.formatAllRows()) {
1418                this.options.pageSize = this.options.totalRows;
1419                $allSelected = true;
1420            } else if (this.options.pageSize === this.options.totalRows) {
1421                // Fix #667 Table with pagination,
1422                // multiple pages and a search that matches to one page throws exception
1423                var pageLst = typeof this.options.pageList === 'string' ?
1424                    this.options.pageList.replace('[', '').replace(']', '')
1425                        .replace(/ /g, '').toLowerCase().split(',') : this.options.pageList;
1426                if ($.inArray(this.options.formatAllRows().toLowerCase(), pageLst)  > -1) {
1427                    $allSelected = true;
1428                }
1429            }
1430
1431            this.totalPages = ~~((this.options.totalRows - 1) / this.options.pageSize) + 1;
1432
1433            this.options.totalPages = this.totalPages;
1434        }
1435        if (this.totalPages > 0 && this.options.pageNumber > this.totalPages) {
1436            this.options.pageNumber = this.totalPages;
1437        }
1438
1439        this.pageFrom = (this.options.pageNumber - 1) * this.options.pageSize + 1;
1440        this.pageTo = this.options.pageNumber * this.options.pageSize;
1441        if (this.pageTo > this.options.totalRows) {
1442            this.pageTo = this.options.totalRows;
1443        }
1444
1445        html.push(
1446            sprintf('<div class="%s-%s pagination-detail">', bs.pullClass, this.options.paginationDetailHAlign),
1447            '<span class="pagination-info">',
1448            this.options.onlyInfoPagination ? this.options.formatDetailPagination(this.options.totalRows) :
1449            this.options.formatShowingRows(this.pageFrom, this.pageTo, this.options.totalRows),
1450            '</span>');
1451
1452        if (!this.options.onlyInfoPagination) {
1453            html.push('<span class="page-list">');
1454
1455            var pageNumber = [
1456                    sprintf('<span class="btn-group %s">',
1457                        this.options.paginationVAlign === 'top' || this.options.paginationVAlign === 'both' ?
1458                            'dropdown' : 'dropup'),
1459                    '<button type="button" class="btn' +
1460                    sprintf(' btn-%s', this.options.buttonsClass) +
1461                    sprintf(' btn-%s', this.options.iconSize) +
1462                    ' dropdown-toggle" data-toggle="dropdown">',
1463                    '<span class="page-size">',
1464                    $allSelected ? this.options.formatAllRows() : this.options.pageSize,
1465                    '</span>',
1466                    ' <span class="caret"></span>',
1467                    '</button>',
1468                    bs.pageDropdownHtml[0]
1469                ];
1470
1471            if (typeof this.options.pageList === 'string') {
1472                var list = this.options.pageList.replace('[', '').replace(']', '')
1473                    .replace(/ /g, '').split(',');
1474
1475                pageList = [];
1476                $.each(list, function (i, value) {
1477                    pageList.push((value.toUpperCase() === that.options.formatAllRows().toUpperCase() || value.toUpperCase() === "UNLIMITED") ?
1478                        that.options.formatAllRows() : +value);
1479                });
1480            }
1481
1482            $.each(pageList, function (i, page) {
1483                if (!that.options.smartDisplay || i === 0 || pageList[i - 1] < that.options.totalRows) {
1484                    var active;
1485                    if ($allSelected) {
1486                        active = page === that.options.formatAllRows() ? 'active' : '';
1487                    } else {
1488                        active = page === that.options.pageSize ? 'active' : '';
1489                    }
1490                    pageNumber.push(sprintf(bs.pageDropdownItemHtml, active, page));
1491                }
1492            });
1493            pageNumber.push(bs.pageDropdownHtml[1] + '</span>');
1494
1495            html.push(this.options.formatRecordsPerPage(pageNumber.join('')));
1496            html.push('</span>');
1497
1498            html.push('</div>',
1499                sprintf('<div class="%s-%s pagination">', bs.pullClass, this.options.paginationHAlign),
1500                '<ul class="pagination' + sprintf(' pagination-%s', this.options.iconSize) + '">',
1501                sprintf('<li class="page-item page-pre"><a class="page-link" href="#">%s</a></li>',
1502                this.options.paginationPreText));
1503
1504            if (this.totalPages < 5) {
1505                from = 1;
1506                to = this.totalPages;
1507            } else {
1508                from = this.options.pageNumber - 2;
1509                to = from + 4;
1510                if (from < 1) {
1511                    from = 1;
1512                    to = 5;
1513                }
1514                if (to > this.totalPages) {
1515                    to = this.totalPages;
1516                    from = to - 4;
1517                }
1518            }
1519
1520            if (this.totalPages >= 6) {
1521                if (this.options.pageNumber >= 3) {
1522                    html.push(
1523                        sprintf('<li class="page-item page-first%s">',
1524                        1 === this.options.pageNumber ? ' active' : ''),
1525                        '<a class="page-link" href="#">', 1, '</a>',
1526                        '</li>');
1527
1528                    from++;
1529                }
1530
1531                if (this.options.pageNumber >= 4) {
1532                    if (this.options.pageNumber == 4 || this.totalPages == 6 || this.totalPages == 7) {
1533                        from--;
1534                    } else {
1535                        html.push('<li class="page-item page-first-separator disabled">',
1536                            '<a class="page-link" href="#">...</a>',
1537                            '</li>');
1538                    }
1539
1540                    to--;
1541                }
1542            }
1543
1544            if (this.totalPages >= 7) {
1545                if (this.options.pageNumber >= (this.totalPages - 2)) {
1546                    from--;
1547                }
1548            }
1549
1550            if (this.totalPages == 6) {
1551                if (this.options.pageNumber >= (this.totalPages - 2)) {
1552                    to++;
1553                }
1554            } else if (this.totalPages >= 7) {
1555                if (this.totalPages == 7 || this.options.pageNumber >= (this.totalPages - 3)) {
1556                    to++;
1557                }
1558            }
1559
1560            for (i = from; i <= to; i++) {
1561                html.push(sprintf('<li class="page-item%s">',
1562                    i === this.options.pageNumber ? ' active' : ''),
1563                    '<a class="page-link" href="#">', i, '</a>',
1564                    '</li>');
1565            }
1566
1567            if (this.totalPages >= 8) {
1568                if (this.options.pageNumber <= (this.totalPages - 4)) {
1569                    html.push('<li class="page-item page-last-separator disabled">',
1570                        '<a class="page-link" href="#">...</a>',
1571                        '</li>');
1572                }
1573            }
1574
1575            if (this.totalPages >= 6) {
1576                if (this.options.pageNumber <= (this.totalPages - 3)) {
1577                    html.push(sprintf('<li class="page-item page-last%s">',
1578                        this.totalPages === this.options.pageNumber ? ' active' : ''),
1579                        '<a class="page-link" href="#">
1579', this.totalPages, '</a>',
1580                        '</li>');
1581                }
1582            }
1583
1584            html.push(
1585                sprintf('<li class="page-item page-next"><a class="page-link" href="#">%s</a></li>',
1586                this.options.paginationNextText),
1587                '</ul>',
1588                '</div>');
1589        }
1590        this.$pagination.html(html.join(''));
1591
1592        if (!this.options.onlyInfoPagination) {
1593            $pageList = this.$pagination.find('.page-list a');
1594            $pre = this.$pagination.find('.page-pre');
1595            $next = this.$pagination.find('.page-next');
1596            $number = this.$pagination.find('.page-item').not('.page-next, .page-pre');
1597
1598            if (this.options.smartDisplay) {
1599                if (this.totalPages <= 1) {
1600                    this.$pagination.find('div.pagination').hide();
1601                }
1602                if (pageList.length < 2 || this.options.totalRows <= pageList[0]) {
1603                    this.$pagination.find('span.page-list').hide();
1604                }
1605
1606                // when data is empty, hide the pagination
1607                this.$pagination[this.getData().length ? 'show' : 'hide']();
1608            }
1609
1610            if (!this.options.paginationLoop) {
1611                if (this.options.pageNumber === 1) {
1612                    $pre.addClass('disabled');
1613                }
1614                if (this.options.pageNumber === this.totalPages) {
1615                    $next.addClass('disabled');
1616                }
1617            }
1618
1619            if ($allSelected) {
1620                this.options.pageSize = this.options.formatAllRows();
1621            }
1622            // removed the events for last and first, onPageNumber executeds the same logic
1623            $pageList.off('click').on('click', $.proxy(this.onPageListChange, this));
1624            $pre.off('click').on('click', $.proxy(this.onPagePre, this));
1625            $next.off('click').on('click', $.proxy(this.onPageNext, this));
1626            $number.off('click').on('click', $.proxy(this.onPageNumber, this));
1627        }
1628    };
1629
1630    BootstrapTable.prototype.updatePagination = function (event) {
1631        // Fix #171: IE disabled button can be clicked bug.
1632        if (event && $(event.currentTarget).hasClass('disabled')) {
1633            return;
1634        }
1635
1636        if (!this.options.maintainSelected) {
1637            this.resetRows();
1638        }
1639
1640        this.initPagination();
1641        if (this.options.sidePagination === 'server') {
1642            this.initServer();
1643        } else {
1644            this.initBody();
1645        }
1646
1647        this.trigger('page-change', this.options.pageNumber, this.options.pageSize);
1648    };
1649
1650    BootstrapTable.prototype.onPageListChange = function (event) {
1651        event.preventDefault();
1652        var $this = $(event.currentTarget);
1653
1654        $this.parent().addClass('active').siblings().removeClass('active');
1655        this.options.pageSize = $this.text().toUpperCase() === this.options.formatAllRows().toUpperCase() ?
1656            this.options.formatAllRows() : +$this.text();
1657        this.$toolbar.find('.page-size').text(this.options.pageSize);
1658
1659        this.updatePagination(event);
1660        return false;
1661    };
1662
1663    BootstrapTable.prototype.onPagePre = function (event) {
1664        event.preventDefault();
1665        if ((this.options.pageNumber - 1) === 0) {
1666            this.options.pageNumber = this.options.totalPages;
1667        } else {
1668            this.options.pageNumber--;
1669        }
1670        this.updatePagination(event);
1671        return false;
1672    };
1673
1674    BootstrapTable.prototype.onPageNext = function (event) {
1675        event.preventDefault();
1676        if ((this.options.pageNumber + 1) > this.options.totalPages) {
1677            this.options.pageNumber = 1;
1678        } else {
1679            this.options.pageNumber++;
1680        }
1681        this.updatePagination(event);
1682        return false;
1683    };
1684
1685    BootstrapTable.prototype.onPageNumber = function (event) {
1686        event.preventDefault();
1687        if (this.options.pageNumber === +$(event.currentTarget).text()) {
1688            return;
1689        }
1690        this.options.pageNumber = +$(event.currentTarget).text();
1691        this.updatePagination(event);
1692        return false;
1693    };
1694
1695    BootstrapTable.prototype.initRow = function(item, i, data, parentDom) {
1696        var that=this,
1697            key,
1698            html = [],
1699            style = {},
1700            csses = [],
1701            data_ = '',
1702            attributes = {},
1703            htmlAttributes = [];
1704
1705        if ($.inArray(item, this.hiddenRows) > -1) {
1706            return;
1707        }
1708
1709        style = calculateObjectValue(this.options, this.options.rowStyle, [item, i], style);
1710
1711        if (style && style.css) {
1712            for (key in style.css) {
1713                csses.push(key + ': ' + style.css[key]);
1714            }
1715        }
1716
1717        attributes = calculateObjectValue(this.options,
1718            this.options.rowAttributes, [item, i], attributes);
1719
1720        if (attributes) {
1721            for (key in attributes) {
1722                htmlAttributes.push(sprintf('%s="%s"', key, escapeHTML(attributes[key])));
1723            }
1724        }
1725
1726        if (item._data && !$.isEmptyObject(item._data)) {
1727            $.each(item._data, function(k, v) {
1728                // ignore data-index
1729                if (k === 'index') {
1730                    return;
1731                }
1732                data_ += sprintf(' data-%s="%s"', k, v);
1733            });
1734        }
1735
1736        html.push('<tr',
1737            sprintf(' %s', htmlAttributes.join(' ')),
1738            sprintf(' id="%s"', $.isArray(item) ? undefined : item._id),
1739            sprintf(' class="%s"', style.classes || ($.isArray(item) ? undefined : item._class)),
1740            sprintf(' data-index="%s"', i),
1741            sprintf(' data-uniqueid="%s"', item[this.options.uniqueId]),
1742            sprintf('%s', data_),
1743            '>'
1744        );
1745
1746        if (this.options.cardView) {
1747            html.push(sprintf('<td colspan="%s"><div class="card-views">', this.header.fields.length));
1748        }
1749
1750        if (!this.options.cardView && this.options.detailView) {
1751            html.push('<td>');
1752
1753            if (calculateObjectValue(null, this.options.detailFilter, [i, item])) {
1754                html.push('<a class="detail-icon" href="#">',
1755                sprintf('<i class="%s %s"></i>', this.options.iconsPrefix, this.options.icons.detailOpen),
1756                '</a>');
1757            }
1758
1759            html.push('</td>');
1760        }
1761
1762        $.each(this.header.fields, function(j, field) {
1763            var text = '',
1764                value_ = getItemField(item, field, that.options.escape),
1765                value = '',
1766                type = '',
1767                cellStyle = {},
1768                id_ = '',
1769                class_ = that.header.classes[j],
1770                data_ = '',
1771                rowspan_ = '',
1772                colspan_ = '',
1773                title_ = '',
1774                column = that.columns[j];
1775
1776            if (that.fromHtml && typeof value_ === 'undefined') {
1777                if((!column.checkbox) && (!column.radio)) {
1778                    return;
1779                }
1780            }
1781
1782            if (!column.visible) {
1783                return;
1784            }
1785
1786            if (that.options.cardView && (!column.cardVisible)) {
1787                return;
1788            }
1789
1790            if (column.escape) {
1791                value_ = escapeHTML(value_);
1792            }
1793
1794            style = sprintf('style="%s"', csses.concat(that.header.styles[j]).join('; '));
1795
1796            // handle td's id and class
1797            if (item['_' + field + '_id']) {
1798                id_ = sprintf(' id="%s"', item['_' + field + '_id']);
1799            }
1800            if (item['_' + field + '_class']) {
1801                class_ = sprintf(' class="%s"', item['_' + field + '_class']);
1802            }
1803            if (item['_' + field + '_rowspan']) {
1804                rowspan_ = sprintf(' rowspan="%s"', item['_' + field + '_rowspan']);
1805            }
1806            if (item['_' + field + '_colspan']) {
1807                colspan_ = sprintf(' colspan="%s"', item['_' + field + '_colspan']);
1808            }
1809            if (item['_' + field + '_title']) {
1810                title_ = sprintf(' title="%s"', item['_' + field + '_title']);
1811            }
1812            cellStyle = calculateObjectValue(that.header,
1813                that.header.cellStyles[j], [value_, item, i, field], cellStyle);
1814            if (cellStyle.classes) {
1815                class_ = sprintf(' class="%s"', cellStyle.classes);
1816            }
1817            if (cellStyle.css) {
1818                var csses_ = [];
1819                for (var key in cellStyle.css) {
1820                    csses_.push(key + ': ' + cellStyle.css[key]);
1821                }
1822                style = sprintf('style="%s"', csses_.concat(that.header.styles[j]).join('; '));
1823            }
1824
1825            value = calculateObjectValue(column,
1826                that.header.formatters[j], [value_, item, i, field], value_);
1827
1828            if (item['_' + field + '_data'] && !$.isEmptyObject(item['_' + field + '_data'])) {
1829                $.each(item['_' + field + '_data'], function(k, v) {
1830                    // ignore data-index
1831                    if (k === 'index') {
1832                        return;
1833                    }
1834                    data_ += sprintf(' data-%s="%s"', k, v);
1835                });
1836            }
1837
1838            if (column.checkbox || column.radio) {
1839                type = column.checkbox ? 'checkbox' : type;
1840                type = column.radio ? 'radio' : type;
1841
1842                text = [sprintf(that.options.cardView ?
1843                        '<div class="card-view %s">' : '<td class="bs-checkbox %s">', column['class'] || ''),
1844                    '<input' +
1845                    sprintf(' data-index="%s"', i) +
1846                    sprintf(' name="%s"', that.options.selectItemName) +
1847                    sprintf(' type="%s"', type) +
1848                    sprintf(' value="%s"', item[that.options.idField]) +
1849                    sprintf(' checked="%s"', value === true ||
1850                        (value_ || value && value.checked) ? 'checked' : undefined) +
1851                    sprintf(' disabled="%s"', !column.checkboxEnabled ||
1852                        (value && value.disabled) ? 'disabled' : undefined) +
1853                    ' />',
1854                    that.header.formatters[j] && typeof value === 'string' ? value : '',
1855                    that.options.cardView ? '</div>' : '</td>'
1856                ].join('');
1857
1858                item[that.header.stateField] = value === true || (!!value_ || value && value.checked);
1859            } else {
1860                value = typeof value === 'undefined' || value === null ?
1861                    that.options.undefinedText : value;
1862
1863                text = that.options.cardView ? ['<div class="card-view">',
1864                    that.options.showHeader ? sprintf('<span class="title" %s>%s</span>', style,
1865                        getPropertyFromOther(that.columns, 'field', 'title', field)) : '',
1866                    sprintf('<span class="value">%s</span>', value),
1867                    '</div>'
1868                ].join('') : [sprintf('<td%s %s %s %s %s %s %s>',
1869                        id_, class_, style, data_, rowspan_, colspan_, title_),
1870                    value,
1871                    '</td>'
1872                ].join('');
1873
1874                // Hide empty data on Card view when smartDisplay is set to true.
1875                if (that.options.cardView && that.options.smartDisplay && value === '') {
1876                    // Should set a placeholder for event binding correct fieldIndex
1877                    text = '<div class="card-view"></div>';
1878                }
1879            }
1880
1881            html.push(text);
1882        });
1883
1884        if (this.options.cardView) {
1885            html.push('</div></td>');
1886        }
1887        html.push('</tr>');
1888
1889        return html.join(' ');
1890    };
1891
1892    BootstrapTable.prototype.initBody = function (fixedScroll) {
1893        var that = this,
1894            html = [],
1895            data = this.getData();
1896
1897        this.trigger('pre-body', data);
1898
1899        this.$body = this.$el.find('>tbody');
1900        if (!this.$body.length) {
1901            this.$body = $('<tbody></tbody>').appendTo(this.$el);
1902        }
1903
1904        //Fix #389 Bootstrap-table-flatJSON is not working
1905
1906        if (!this.options.pagination || this.options.sidePagination === 'server') {
1907            this.pageFrom = 1;
1908            this.pageTo = data.length;
1909        }
1910
1911        var trFragments = $(document.createDocumentFragment());
1912        var hasTr;
1913
1914        for (var i = this.pageFrom - 1; i < this.pageTo; i++) {
1915            var item = data[i];
1916            var tr = this.initRow(item, i, data, trFragments);
1917            hasTr = hasTr || !!tr;
1918            if (tr&&tr!==true) {
1919                trFragments.append(tr);
1920            }
1921        }
1922
1923        // show no records
1924        if (!hasTr) {
1925            trFragments.append('<tr class="no-records-found">' +
1926                sprintf('<td colspan="%s">%s</td>',
1927                this.$header.find('th').length,
1928                this.options.formatNoMatches()) +
1929                '</tr>');
1930        }
1931
1932        this.$body.html(trFragments);
1933
1934        if (!fixedScroll) {
1935            this.scrollTo(0);
1936        }
1937
1938        // click to select by column
1939        this.$body.find('> tr[data-index] > td').off('click dblclick').on('click dblclick', function (e) {
1940            var $td = $(this),
1941                $tr = $td.parent(),
1942                item = that.data[$tr.data('index')],
1943                index = $td[0].cellIndex,
1944                fields = that.getVisibleFields(),
1945                field = fields[that.options.detailView && !that.options.cardView ? index - 1 : index],
1946                column = that.columns[that.fieldsColumnsIndex[field]],
1947                value = getItemField(item, field, that.options.escape);
1948
1949            if ($td.find('.detail-icon').length) {
1950                return;
1951            }
1952
1953            that.trigger(e.type === 'click' ? 'click-cell' : 'dbl-click-cell', field, value, item, $td);
1954            that.trigger(e.type === 'click' ? 'click-row' : 'dbl-click-row', item, $tr, field);
1955
1956            // if click to select - then trigger the checkbox/radio click
1957            if (e.type === 'click' && that.options.clickToSelect && column.clickToSelect && that.options.ignoreClickToSelectOn(e.target)) {
1958                var $selectItem = $tr.find(sprintf('[name="%s"]', that.options.selectItemName));
1959                if ($selectItem.length) {
1960                    $selectItem[0].click(); // #144: .trigger('click') bug
1961                }
1962            }
1963        });
1964
1965        this.$body.find('> tr[data-index] > td > .detail-icon').off('click').on('click', function (e) {
1966            e.preventDefault();
1967
1968            var $this = $(this),
1969                $tr = $this.parent().parent(),
1970                index = $tr.data('index'),
1971                row = data[index]; // Fix #980 Detail view, when searching, returns wrong row
1972
1973            // remove and update
1974            if ($tr.next().is('tr.detail-view')) {
1975                $this.find('i').attr('class', sprintf('%s %s', that.options.iconsPrefix, that.options.icons.detailOpen));
1976                that.trigger('collapse-row', index, row, $tr.next());
1977                $tr.next().remove();
1978            } else {
1979                $this.find('i').attr('class', sprintf('%s %s', that.options.iconsPrefix, that.options.icons.detailClose));
1980                $tr.after(sprintf('<tr class="detail-view"><td colspan="%s"></td></tr>', $tr.find('td').length));
1981                var $element = $tr.next().find('td');
1982                var content = calculateObjectValue(that.options, that.options.detailFormatter, [index, row, $element], '');
1983                if ($element.length === 1) {
1984                    $element.append(content);
1985                }
1986                that.trigger('expand-row', index, row, $element);
1987            }
1988            that.resetView();
1989            return false;
1990        });
1991
1992        this.$selectItem = this.$body.find(sprintf('[name="%s"]', this.options.selectItemName));
1993        this.$selectItem.off('click').on('click', function (event) {
1994            event.stopImmediatePropagation();
1995
1996            var $this = $(this),
1997                checked = $this.prop('checked'),
1998                row = that.data[$this.data('index')];
1999
2000            if ($(this).is(':radio') || that.options.singleSelect) {
2001                $.each(that.options.data, function (i, row) {
2002                    row[that.header.stateField] = false;
2003                });
2004            }
2005
2006            row[that.header.stateField] = checked;
2007
2008            if (that.options.singleSelect) {
2009                that.$selectItem.not(this).each(function () {
2010                    that.data[$(this).data('index')][that.header.stateField] = false;
2011                });
2012                that.$selectItem.filter(':checked').not(this).prop('checked', false);
2013            }
2014
2015            that.updateSelected();
2016            that.trigger(checked ? 'check' : 'uncheck', row, $this);
2017        });
2018
2019        $.each(this.header.events, function (i, events) {
2020            if (!events) {
2021                return;
2022            }
2023            // fix bug, if events is defined with namespace
2024            if (typeof events === 'string') {
2025                events = calculateObjectValue(null, events);
2026            }
2027
2028            var field = that.header.fields[i],
2029                fieldIndex = $.inArray(field, that.getVisibleFields());
2030
2031            if (fieldIndex === -1) {
2032                return;
2033            }
2034
2035            if (that.options.detailView && !that.options.cardView) {
2036                fieldIndex += 1;
2037            }
2038
2039            for (var key in events) {
2040                that.$body.find('>tr:not(.no-records-found)').each(function () {
2041                    var $tr = $(this),
2042                        $td = $tr.find(that.options.cardView ? '.card-view' : 'td').eq(fieldIndex),
2043                        index = key.indexOf(' '),
2044                        name = key.substring(0, index),
2045                        el = key.substring(index + 1),
2046                        func = events[key];
2047
2048                    $td.find(el).off(name).on(name, function (e) {
2049                        var index = $tr.data('index'),
2050                            row = that.data[index],
2051                            value = row[field];
2052
2053                        func.apply(this, [e, value, row, index]);
2054                    });
2055                });
2056            }
2057        });
2058
2059        this.updateSelected();
2060        this.resetView();
2061
2062        this.trigger('post-body', data);
2063    };
2064
2065    BootstrapTable.prototype.initServer = function (silent, query, url) {
2066        var that = this,
2067            data = {},
2068            index = $.inArray(this.options.sortName, this.header.fields),
2069            params = {
2070                searchText: this.searchText,
2071                sortName: this.options.sortName,
2072                sortOrder: this.options.sortOrder
2073            },
2074            request;
2075
2076        if (this.header.sortNames[index]) {
2077            params.sortName = this.header.sortNames[index];
2078        }
2079
2080        if (this.options.pagination && this.options.sidePagination === 'server') {
2081            params.pageSize = this.options.pageSize === this.options.formatAllRows() ?
2082                this.options.totalRows : this.options.pageSize;
2083            params.pageNumber = this.options.pageNumber;
2084        }
2085
2086        if (!(url || this.options.url) && !this.options.ajax) {
2087            return;
2088        }
2089
2090        if (this.options.queryParamsType === 'limit') {
2091            params = {
2092                search: params.searchText,
2093                sort: params.sortName,
2094                order: params.sortOrder
2095            };
2096
2097            if (this.options.pagination && this.options.sidePagination === 'server') {
2098                params.offset = this.options.pageSize === this.options.formatAllRows() ?
2099                    0 : this.options.pageSize * (this.options.pageNumber - 1);
2100                params.limit = this.options.pageSize === this.options.formatAllRows() ?
2101                    this.options.totalRows : this.options.pageSize;
2102                if (params.limit === 0) {
2103                    delete params.limit;
2104                }
2105            }
2106        }
2107
2108        if (!($.isEmptyObject(this.filterColumnsPartial))) {
2109            params.filter = JSON.stringify(this.filterColumnsPartial, null);
2110        }
2111
2112        data = calculateObjectValue(this.options, this.options.queryParams, [params], data);
2113
2114        $.extend(data, query || {});
2115
2116        // false to stop request
2117        if (data === false) {
2118            return;
2119        }
2120
2121        if (!silent) {
2122            this.$tableLoading.show();
2123        }
2124        request = $.extend({}, calculateObjectValue(null, this.options.ajaxOptions), {
2125            type: this.options.method,
2126            url:  url || this.options.url,
2127            data: this.options.contentType === 'application/json' && this.options.method === 'post' ?
2128                JSON.stringify(data) : data,
2129            cache: this.options.cache,
2130            contentType: this.options.contentType,
2131            dataType: this.options.dataType,
2132            success: function (res) {
2133                res = calculateObjectValue(that.options, that.options.responseHandler, [res], res);
2134
2135                that.load(res);
2136                that.trigger('load-success', res);
2137                if (!silent) that.$tableLoading.hide();
2138            },
2139            error: function (res) {
2140                var data = [];
2141                if (that.options.sidePagination === 'server') {
2142                    data = {};
2143                    data[that.options.totalField] = 0;
2144                    data[that.options.dataField] = [];
2145                }
2146                that.load(data);
2147                that.trigger('load-error', res.status, res);
2148                if (!silent) that.$tableLoading.hide();
2149            }
2150        });
2151
2152        if (this.options.ajax) {
2153            calculateObjectValue(this, this.options.ajax, [request], null);
2154        } else {
2155            if (this._xhr && this._xhr.readyState !== 4) {
2156                this._xhr.abort();
2157            }
2158            this._xhr = $.ajax(request);
2159        }
2160    };
2161
2162    BootstrapTable.prototype.initSearchText = function () {
2163        if (this.options.search) {
2164            this.searchText = '';
2165            if (this.options.searchText !== '') {
2166                var $search = this.$toolbar.find('.search input');
2167                $search.val(this.options.searchText);
2168                this.onSearch({currentTarget: $search, firedByInitSearchText: true});
2169            }
2170        }
2171    };
2172
2173    BootstrapTable.prototype.getCaret = function () {
2174        var that = this;
2175
2176        $.each(this.$header.find('th'), function (i, th) {
2177            $(th).find('.sortable').removeClass('desc asc').addClass($(th).data('field') === that.options.sortName ? that.options.sortOrder : 'both');
2178        });
2179    };
2180
2181    BootstrapTable.prototype.updateSelected = function () {
2182        var checkAll = this.$selectItem.filter(':enabled').length &&
2183            this.$selectItem.filter(':enabled').length ===
2184            this.$selectItem.filter(':enabled').filter(':checked').length;
2185
2186        this.$selectAll.add(this.$selectAll_).prop('checked', checkAll);
2187
2188        this.$selectItem.each(function () {
2189            $(this).closest('tr')[$(this).prop('checked') ? 'addClass' : 'removeClass']('selected');
2190        });
2191    };
2192
2193    BootstrapTable.prototype.updateRows = function () {
2194        var that = this;
2195
2196        this.$selectItem.each(function () {
2197            that.data[$(this).data('index')][that.header.stateField] = $(this).prop('checked');
2198        });
2199    };
2200
2201    BootstrapTable.prototype.resetRows = function () {
2202        var that = this;
2203
2204        $.each(this.data, function (i, row) {
2205            that.$selectAll.prop('checked', false);
2206            that.$selectItem.prop('checked', false);
2207            if (that.header.stateField) {
2208                row[that.header.stateField] = false;
2209            }
2210        });
2211        this.initHiddenRows();
2212    };
2213
2214    BootstrapTable.prototype.trigger = function (name) {
2215        var args = Array.prototype.slice.call(arguments, 1);
2216
2217        name += '.bs.table';
2218        this.options[BootstrapTable.EVENTS[name]].apply(this.options, args);
2219        this.$el.trigger($.Event(name), args);
2220
2221        this.options.onAll(name, args);
2222        this.$el.trigger($.Event('all.bs.table'), [name, args]);
2223    };
2224
2225    BootstrapTable.prototype.resetHeader = function () {
2226        // fix #61: the hidden table reset header bug.
2227        // fix bug: get $el.css('width') error sometime (height = 500)
2228        clearTimeout(this.timeoutId_);
2229        this.timeoutId_ = setTimeout($.proxy(this.fitHeader, this), this.$el.is(':hidden') ? 100 : 0);
2230    };
2231
2232    BootstrapTable.prototype.fitHeader = function () {
2233        var that = this,
2234            fixedBody,
2235            scrollWidth,
2236            focused,
2237            focusedTemp;
2238
2239        if (that.$el.is(':hidden')) {
2240            that.timeoutId_ = setTimeout($.proxy(that.fitHeader, that), 100);
2241            return;
2242        }
2243        fixedBody = this.$tableBody.get(0);
2244
2245        scrollWidth = fixedBody.scrollWidth > fixedBody.clientWidth &&
2246        fixedBody.scrollHeight > fixedBody.clientHeight + this.$header.outerHeight() ?
2247            getScrollBarWidth() : 0;
2248
2249        this.$el.css('margin-top', -this.$header.outerHeight());
2250
2251        focused = $(':focus');
2252        if (focused.length > 0) {
2253            var $th = focused.parents('th');
2254            if ($th.length > 0) {
2255                var dataField = $th.attr('data-field');
2256                if (dataField !== undefined) {
2257                    var $headerTh = this.$header.find("[data-field='" + dataField + "']");
2258                    if ($headerTh.length > 0) {
2259                        $headerTh.find(":input").addClass("focus-temp");
2260                    }
2261                }
2262            }
2263        }
2264
2265        this.$header_ = this.$header.clone(true, true);
2266        this.$selectAll_ = this.$header_.find('[name="btSelectAll"]');
2267        this.$tableHeader.css({
2268            'margin-right': scrollWidth
2269        }).find('table').css('width', this.$el.outerWidth())
2270            .html('').attr('class', this.$el.attr('class'))
2271            .append(this.$header_);
2272
2273        focusedTemp = $('.focus-temp:visible:eq(0)');
2274        if (focusedTemp.length > 0) {
2275            focusedTemp.focus();
2276            this.$header.find('.focus-temp').removeClass('focus-temp');
2277        }
2278
2279        // fix bug: $.data() is not working as expected after $.append()
2280        this.$header.find('th[data-field]').each(function (i) {
2281            that.$header_.find(sprintf('th[data-field="%s"]', $(this).data('field'))).data($(this).data());
2282        });
2283
2284        var visibleFields = this.getVisibleFields(),
2285            $ths = this.$header_.find('th');
2286
2287        this.$body.find('>tr:first-child:not(.no-records-found) > *').each(function (i) {
2288            var $this = $(this),
2289                index = i;
2290
2291            if (that.options.detailView && !that.options.cardView) {
2292                if (i === 0) {
2293                    that.$header_.find('th.detail').find('.fht-cell').width($this.innerWidth());
2294                }
2295                index = i - 1;
2296            }
2297
2298            if (index === -1) {
2299                return;
2300            }
2301
2302            var $th = that.$header_.find(sprintf('th[data-field="%s"]', visibleFields[index]));
2303            if ($th.length > 1) {
2304                $th = $($ths[$this[0].cellIndex]);
2305            }
2306
2307            var zoomWidth = $th.width() - $th.find('.fht-cell').width();
2308            $th.find('.fht-cell').width($this.innerWidth() - zoomWidth);
2309        });
2310
2311        this.horizontalScroll();
2312        this.trigger('post-header');
2313    };
2314
2315    BootstrapTable.prototype.resetFooter = function () {
2316        var that = this,
2317            data = that.getData(),
2318            html = [];
2319
2320        if (!this.options.showFooter || this.options.cardView) { //do nothing
2321            return;
2322        }
2323
2324        if (!this.options.cardView && this.options.detailView) {
2325            html.push('<td><div class="th-inner">&nbsp;</div><div class="fht-cell"></div></td>');
2326        }
2327
2328        $.each(this.columns, function (i, column) {
2329            var key,
2330                falign = '', // footer align style
2331                valign = '',
2332                csses = [],
2333                style = {},
2334                class_ = sprintf(' class="%s"', column['class']);
2335
2336            if (!column.visible) {
2337                return;
2338            }
2339
2340            if (that.options.cardView && (!column.cardVisible)) {
2341                return;
2342            }
2343
2344            falign = sprintf('text-align: %s; ', column.falign ? column.falign : column.align);
2345            valign = sprintf('vertical-align: %s; ', column.valign);
2346
2347            style = calculateObjectValue(null, that.options.footerStyle);
2348
2349            if (style && style.css) {
2350                for (key in style.css) {
2351                    csses.push(key + ': ' + style.css[key]);
2352                }
2353            }
2354
2355            html.push('<td', class_, sprintf(' style="%s"', falign + valign + csses.concat().join('; ')), '>');
2356            html.push('<div class="th-inner">');
2357
2358            html.push(calculateObjectValue(column, column.footerFormatter, [data], '&nbsp;') || '&nbsp;');
2359
2360            html.push('</div>');
2361            html.push('<div class="fht-cell"></div>');
2362            html.push('</div>');
2363            html.push('</td>');
2364        });
2365
2366        this.$tableFooter.find('tr').html(html.join(''));
2367        this.$tableFooter.show();
2368        clearTimeout(this.timeoutFooter_);
2369        this.timeoutFooter_ = setTimeout($.proxy(this.fitFooter, this),
2370            this.$el.is(':hidden') ? 100 : 0);
2371    };
2372
2373    BootstrapTable.prototype.fitFooter = function () {
2374        var that = this,
2375            $footerTd,
2376            elWidth,
2377            scrollWidth;
2378
2379        clearTimeout(this.timeoutFooter_);
2380        if (this.$el.is(':hidden')) {
2381            this.timeoutFooter_ = setTimeout($.proxy(this.fitFooter, this), 100);
2382            return;
2383        }
2384
2385        elWidth = this.$el.css('width');
2386        scrollWidth = elWidth > this.$tableBody.width() ? getScrollBarWidth() : 0;
2387
2388        this.$tableFooter.css({
2389            'margin-right': scrollWidth
2390        }).find('table').css('width', elWidth)
2391            .attr('class', this.$el.attr('class'));
2392
2393        $footerTd = this.$tableFooter.find('td');
2394
2395        this.$body.find('>tr:first-child:not(.no-records-found) > *').each(function (i) {
2396            var $this = $(this);
2397
2398            $footerTd.eq(i).find('.fht-cell').width($this.innerWidth());
2399        });
2400
2401        this.horizontalScroll();
2402    };
2403
2404    BootstrapTable.prototype.horizontalScroll = function () {
2405        var that = this;
2406        // horizontal scroll event
2407        // TODO: it's probably better improving the layout than binding to scroll event
2408
2409        that.trigger('scroll-body');
2410        this.$tableBody.off('scroll').on('scroll', function () {
2411            if (that.options.showHeader && that.options.height) {
2412              that.$tableHeader.scrollLeft($(this).scrollLeft());
2413            }
2414
2415            if (that.options.showFooter && !that.options.cardView) {
2416                that.$tableFooter.scrollLeft($(this).scrollLeft());
2417            }
2418        });
2419    };
2420
2421    BootstrapTable.prototype.toggleColumn = function (index, checked, needUpdate) {
2422        if (index === -1) {
2423            return;
2424        }
2425        this.columns[index].visible = checked;
2426        this.initHeader();
2427        this.initSearch();
2428        this.initPagination();
2429        this.initBody();
2430
2431        if (this.options.showColumns) {
2432            var $items = this.$toolbar.find('.keep-open input').prop('disabled', false);
2433
2434            if (needUpdate) {
2435                $items.filter(sprintf('[value="%s"]', index)).prop('checked', checked);
2436            }
2437
2438            if ($items.filter(':checked').length <= this.options.minimumCountColumns) {
2439                $items.filter(':checked').prop('disabled', true);
2440            }
2441        }
2442    };
2443
2444    BootstrapTable.prototype.getVisibleFields = function () {
2445        var that = this,
2446            visibleFields = [];
2447
2448        $.each(this.header.fields, function (j, field) {
2449            var column = that.columns[that.fieldsColumnsIndex[field]];
2450
2451            if (!column.visible) {
2452                return;
2453            }
2454            visibleFields.push(field);
2455        });
2456        return visibleFields;
2457    };
2458
2459    // PUBLIC FUNCTION DEFINITION
2460    // =======================
2461
2462    BootstrapTable.prototype.resetView = function (params) {
2463        var padding = 0;
2464
2465        if (params && params.height) {
2466            this.options.height = params.height;
2467        }
2468
2469        this.$selectAll.prop('checked', this.$selectItem.length > 0 &&
2470            this.$selectItem.length === this.$selectItem.filter(':checked').length);
2471
2472        if (this.options.height) {
2473            var toolbarHeight = this.$toolbar.outerHeight(true),
2474                paginationHeight = this.$pagination.outerHeight(true),
2475                height = this.options.height - toolbarHeight - paginationHeight;
2476
2477            this.$tableContainer.css('height', height + 'px');
2478        }
2479
2480        if (this.options.cardView) {
2481            // remove the element css
2482            this.$el.css('margin-top', '0');
2483            this.$tableContainer.css('padding-bottom', '0');
2484            this.$tableFooter.hide();
2485            return;
2486        }
2487
2488        if (this.options.showHeader && this.options.height) {
2489            this.$tableHeader.show();
2490            this.resetHeader();
2491            padding += this.$header.outerHeight();
2492        } else {
2493            this.$tableHeader.hide();
2494            this.trigger('post-header');
2495        }
2496
2497        if (this.options.showFooter) {
2498            this.resetFooter();
2499            if (this.options.height) {
2500                padding += this.$tableFooter.outerHeight() + 1;
2501            }
2502        }
2503
2504        // Assign the correct sortable arrow
2505        this.getCaret();
2506        this.$tableContainer.css('padding-bottom', padding + 'px');
2507        this.trigger('reset-view');
2508    };
2509
2510    BootstrapTable.prototype.getData = function (useCurrentPage) {
2511        var data = this.options.data;
2512        if (this.searchText || this.options.sortName || !$.isEmptyObject(this.filterColumns) || !$.isEmptyObject(this.filterColumnsPartial)) {
2513            data = this.data;
2514        }
2515
2516        if (useCurrentPage) {
2517            return data.slice(this.pageFrom - 1, this.pageTo);
2518        }
2519
2520        return data;
2521    };
2522
2523    BootstrapTable.prototype.load = function (data) {
2524        var fixedScroll = false;
2525
2526        // #431: support pagination
2527        if (this.options.pagination && this.options.sidePagination === 'server') {
2528            this.options.totalRows = data[this.options.totalField];
2529            fixedScroll = data.fixedScroll;
2530            data = data[this.options.dataField];
2531        } else if (!$.isArray(data)) { // support fixedScroll
2532            fixedScroll = data.fixedScroll;
2533            data = data.data;
2534        }
2535
2536        this.initData(data);
2537        this.initSearch();
2538        this.initPagination();
2539        this.initBody(fixedScroll);
2540    };
2541
2542    BootstrapTable.prototype.append = function (data) {
2543        this.initData(data, 'append');
2544        this.initSearch();
2545        this.initPagination();
2546        this.initSort();
2547        this.initBody(true);
2548    };
2549
2550    BootstrapTable.prototype.prepend = function (data) {
2551        this.initData(data, 'prepend');
2552        this.initSearch();
2553        this.initPagination();
2554        this.initSort();
2555        this.initBody(true);
2556    };
2557
2558    BootstrapTable.prototype.remove = function (params) {
2559        var len = this.options.data.length,
2560            i, row;
2561
2562        if (!params.hasOwnProperty('field') || !params.hasOwnProperty('values')) {
2563            return;
2564        }
2565
2566        for (i = len - 1; i >= 0; i--) {
2567            row = this.options.data[i];
2568
2569            if (!row.hasOwnProperty(params.field)) {
2570                continue;
2571            }
2572            if ($.inArray(row[params.field], params.values) !== -1) {
2573                this.options.data.splice(i, 1);
2574                if (this.options.sidePagination === 'server') {
2575                    this.options.totalRows -= 1;
2576                }
2577            }
2578        }
2579
2580        if (len === this.options.data.length) {
2581            return;
2582        }
2583
2584        this.initSearch();
2585        this.initPagination();
2586        this.initSort();
2587        this.initBody(true);
2588    };
2589
2590    BootstrapTable.prototype.removeAll = function () {
2591        if (this.options.data.length > 0) {
2592            this.options.data.splice(0, this.options.data.length);
2593            this.initSearch();
2594            this.initPagination();
2595            this.initBody(true);
2596        }
2597    };
2598
2599    BootstrapTable.prototype.getRowByUniqueId = function (id) {
2600        var uniqueId = this.options.uniqueId,
2601            len = this.options.data.length,
2602            dataRow = null,
2603            i, row, rowUniqueId;
2604
2605        for (i = len - 1; i >= 0; i--) {
2606            row = this.options.data[i];
2607
2608            if (row.hasOwnProperty(uniqueId)) { // uniqueId is a column
2609                rowUniqueId = row[uniqueId];
2610            } else if(row._data.hasOwnProperty(uniqueId)) { // uniqueId is a row data property
2611                rowUniqueId = row._data[uniqueId];
2612            } else {
2613                continue;
2614            }
2615
2616            if (typeof rowUniqueId === 'string') {
2617                id = id.toString();
2618            } else if (typeof rowUniqueId === 'number') {
2619                if ((Number(rowUniqueId) === rowUniqueId) && (rowUniqueId % 1 === 0)) {
2620                    id = parseInt(id);
2621                } else if ((rowUniqueId === Number(rowUniqueId)) && (rowUniqueId !== 0)) {
2622                    id = parseFloat(id);
2623                }
2624            }
2625
2626            if (rowUniqueId === id) {
2627                dataRow = row;
2628                break;
2629            }
2630        }
2631
2632        return dataRow;
2633    };
2634
2635    BootstrapTable.prototype.removeByUniqueId = function (id) {
2636        var len = this.options.data.length,
2637            row = this.getRowByUniqueId(id);
2638
2639        if (row) {
2640            this.options.data.splice(this.options.data.indexOf(row), 1);
2641        }
2642
2643        if (len === this.options.data.length) {
2644            return;
2645        }
2646
2647        this.initSearch();
2648        this.initPagination();
2649        this.initBody(true);
2650    };
2651
2652    BootstrapTable.prototype.updateByUniqueId = function (params) {
2653        var that = this;
2654        var allParams = $.isArray(params) ? params : [ params ];
2655
2656        $.each(allParams, function(i, params) {
2657            var rowId;
2658
2659            if (!params.hasOwnProperty('id') || !params.hasOwnProperty('row')) {
2660                return;
2661            }
2662
2663            rowId = $.inArray(that.getRowByUniqueId(params.id), that.options.data);
2664
2665            if (rowId === -1) {
2666                return;
2667            }
2668            $.extend(that.options.data[rowId], params.row);
2669        });
2670
2671        this.initSearch();
2672        this.initPagination();
2673        this.initSort();
2674        this.initBody(true);
2675    };
2676
2677    BootstrapTable.prototype.refreshColumnTitle = function (params) {
2678        if (!params.hasOwnProperty('field') || !params.hasOwnProperty('title')) {
2679            return;
2680        }
2681
2682        this.columns[this.fieldsColumnsIndex[params.field]].title =
2683            this.options.escape ? escapeHTML(params.title) : params.title;
2684
2685        if (this.columns[this.fieldsColumnsIndex[params.field]].visible) {
2686            var header = this.options.height !== undefined ? this.$tableHeader : this.$header;
2687            header.find('th[data-field]').each(function (i) {
2688                if ($(this).data('field') === params.field) {
2689                    $($(this).find(".th-inner")[0]).text(params.title);
2690                    return false;
2691                }
2692            });
2693        }
2694    };
2695
2696    BootstrapTable.prototype.insertRow = function (params) {
2697        if (!params.hasOwnProperty('index') || !params.hasOwnProperty('row')) {
2698            return;
2699        }
2700        this.options.data.splice(params.index, 0, params.row);
2701        this.initSearch();
2702        this.initPagination();
2703        this.initSort();
2704        this.initBody(true);
2705    };
2706
2707    BootstrapTable.prototype.updateRow = function (params) {
2708        var that = this;
2709        var allParams = $.isArray(params) ? params : [ params ];
2710
2711        $.each(allParams, function(i, params) {
2712            if (!params.hasOwnProperty('index') || !params.hasOwnProperty('row')) {
2713                return;
2714            }
2715            $.extend(that.options.data[params.index], params.row);
2716        });
2717
2718        this.initSearch();
2719        this.initPagination();
2720        this.initSort();
2721        this.initBody(true);
2722    };
2723
2724    BootstrapTable.prototype.initHiddenRows = function () {
2725        this.hiddenRows = [];
2726    };
2727
2728    BootstrapTable.prototype.showRow = function (params) {
2729        this.toggleRow(params, true);
2730    };
2731
2732    BootstrapTable.prototype.hideRow = function (params) {
2733        this.toggleRow(params, false);
2734    };
2735
2736    BootstrapTable.prototype.toggleRow = function (params, visible) {
2737        var row, index;
2738
2739        if (params.hasOwnProperty('index')) {
2740            row = this.getData()[params.index];
2741        } else if (params.hasOwnProperty('uniqueId')) {
2742            row = this.getRowByUniqueId(params.uniqueId);
2743        }
2744
2745        if (!row) {
2746            return;
2747        }
2748
2749        index = $.inArray(row, this.hiddenRows);
2750
2751        if (!visible && index === -1) {
2752            this.hiddenRows.push(row);
2753        } else if (visible && index > -1) {
2754            this.hiddenRows.splice(index, 1);
2755        }
2756        this.initBody(true);
2757    };
2758
2759    BootstrapTable.prototype.getHiddenRows = function (show) {
2760        var that = this,
2761            data = this.getData(),
2762            rows = [];
2763
2764        $.each(data, function (i, row) {
2765            if ($.inArray(row, that.hiddenRows) > -1) {
2766                rows.push(row);
2767            }
2768        });
2769        this.hiddenRows = rows;
2770        return rows;
2771    };
2772
2773    BootstrapTable.prototype.mergeCells = function (options) {
2774        var row = options.index,
2775            col = $.inArray(options.field, this.getVisibleFields()),
2776            rowspan = options.rowspan || 1,
2777            colspan = options.colspan || 1,
2778            i, j,
2779            $tr = this.$body.find('>tr'),
2780            $td;
2781
2782        if (this.options.detailView && !this.options.cardView) {
2783            col += 1;
2784        }
2785
2786        $td = $tr.eq(row).find('>td').eq(col);
2787
2788        if (row < 0 || col < 0 || row >= this.data.length) {
2789            return;
2790        }
2791
2792        for (i = row; i < row + rowspan; i++) {
2793            for (j = col; j < col + colspan; j++) {
2794                $tr.eq(i).find('>td').eq(j).hide();
2795            }
2796        }
2797
2798        $td.attr('rowspan', rowspan).attr('colspan', colspan).show();
2799    };
2800
2801    BootstrapTable.prototype.updateCell = function (params) {
2802        if (!params.hasOwnProperty('index') ||
2803            !params.hasOwnProperty('field') ||
2804            !params.hasOwnProperty('value')) {
2805            return;
2806        }
2807        this.data[params.index][params.field] = params.value;
2808
2809        if (params.reinit === false) {
2810            return;
2811        }
2812        this.initSort();
2813        this.initBody(true);
2814    };
2815
2816    BootstrapTable.prototype.updateCellById = function (params) {
2817        var that = this;
2818        if (!params.hasOwnProperty('id') ||
2819            !params.hasOwnProperty('field') ||
2820            !params.hasOwnProperty('value')) {
2821            return;
2822        }
2823        var allParams = $.isArray(params) ? params : [ params ];
2824
2825        $.each(allParams, function(i, params) {
2826            var rowId;
2827
2828            rowId = $.inArray(that.getRowByUniqueId(params.id), that.options.data);
2829
2830            if (rowId === -1) {
2831                return;
2832            }
2833            that.data[rowId][params.field] = params.value;
2834        });
2835
2836        if (params.reinit === false) {
2837            return;
2838        }
2839        this.initSort();
2840        this.initBody(true);
2841    };
2842
2843    BootstrapTable.prototype.getOptions = function () {
2844        //Deep copy
2845        return $.extend(true, {}, this.options);
2846    };
2847
2848    BootstrapTable.prototype.getSelections = function () {
2849        var that = this;
2850
2851        return $.grep(this.options.data, function (row) {
2852            // fix #2424: from html with checkbox
2853            return row[that.header.stateField] === true;
2854        });
2855    };
2856
2857    BootstrapTable.prototype.getAllSelections = function () {
2858        var that = this;
2859
2860        return $.grep(this.options.data, function (row) {
2861            return row[that.header.stateField];
2862        });
2863    };
2864
2865    BootstrapTable.prototype.checkAll = function () {
2866        this.checkAll_(true);
2867    };
2868
2869    BootstrapTable.prototype.uncheckAll = function () {
2870        this.checkAll_(false);
2871    };
2872
2873    BootstrapTable.prototype.checkInvert = function () {
2874        var that = this;
2875        var rows = that.$selectItem.filter(':enabled');
2876        var checked = rows.filter(':checked');
2877        rows.each(function() {
2878            $(this).prop('checked', !$(this).prop('checked'));
2879        });
2880        that.updateRows();
2881        that.updateSelected();
2882        that.trigger('uncheck-some', checked);
2883        checked = that.getSelections();
2884        that.trigger('check-some', checked);
2885    };
2886
2887    BootstrapTable.prototype.checkAll_ = function (checked) {
2888        var rows;
2889        if (!checked) {
2890            rows = this.getSelections();
2891        }
2892        this.$selectAll.add(this.$selectAll_).prop('checked', checked);
2893        this.$selectItem.filter(':enabled').prop('checked', checked);
2894        this.updateRows();
2895        if (checked) {
2896            rows = this.getSelections();
2897        }
2898        this.trigger(checked ? 'check-all' : 'uncheck-all', rows);
2899    };
2900
2901    BootstrapTable.prototype.check = function (index) {
2902        this.check_(true, index);
2903    };
2904
2905    BootstrapTable.prototype.uncheck = function (index) {
2906        this.check_(false, index);
2907    };
2908
2909    BootstrapTable.prototype.check_ = function (checked, index) {
2910        var $el = this.$selectItem.filter(sprintf('[data-index="%s"]', index)).prop('checked', checked);
2911        this.data[index][this.header.stateField] = checked;
2912        this.updateSelected();
2913        this.trigger(checked ? 'check' : 'uncheck', this.data[index], $el);
2914    };
2915
2916    BootstrapTable.prototype.checkBy = function (obj) {
2917        this.checkBy_(true, obj);
2918    };
2919
2920    BootstrapTable.prototype.uncheckBy = function (obj) {
2921        this.checkBy_(false, obj);
2922    };
2923
2924    BootstrapTable.prototype.checkBy_ = function (checked, obj) {
2925        if (!obj.hasOwnProperty('field') || !obj.hasOwnProperty('values')) {
2926            return;
2927        }
2928
2929        var that = this,
2930            rows = [];
2931        $.each(this.options.data, function (index, row) {
2932            if (!row.hasOwnProperty(obj.field)) {
2933                return false;
2934            }
2935            if ($.inArray(row[obj.field], obj.values) !== -1) {
2936                var $el = that.$selectItem.filter(':enabled')
2937                    .filter(sprintf('[data-index="%s"]', index)).prop('checked', checked);
2938                row[that.header.stateField] = checked;
2939                rows.push(row);
2940                that.trigger(checked ? 'check' : 'uncheck', row, $el);
2941            }
2942        });
2943        this.updateSelected();
2944        this.trigger(checked ? 'check-some' : 'uncheck-some', rows);
2945    };
2946
2947    BootstrapTable.prototype.destroy = function () {
2948        this.$el.insertBefore(this.$container);
2949        $(this.options.toolbar).insertBefore(this.$el);
2950        this.$container.next().remove();
2951        this.$container.remove();
2952        this.$el.html(this.$el_.html())
2953            .css('margin-top', '0')
2954            .attr('class', this.$el_.attr('class') || ''); // reset the class
2955    };
2956
2957    BootstrapTable.prototype.showLoading = function () {
2958        this.$tableLoading.show();
2959    };
2960
2961    BootstrapTable.prototype.hideLoading = function () {
2962        this.$tableLoading.hide();
2963    };
2964
2965    BootstrapTable.prototype.togglePagination = function () {
2966        this.options.pagination = !this.options.pagination;
2967        var button = this.$toolbar.find('button[name="paginationSwitch"] i');
2968        if (this.options.pagination) {
2969            button.attr("class", this.options.iconsPrefix + " " + this.options.icons.paginationSwitchDown);
2970        } else {
2971            button.attr("class", this.options.iconsPrefix + " " + this.options.icons.paginationSwitchUp);
2972        }
2973        this.updatePagination();
2974    };
2975
2976    BootstrapTable.prototype.toggleFullscreen = function () {
2977        this.$el.closest('.bootstrap-table').toggleClass('fullscreen');
2978    };
2979
2980    BootstrapTable.prototype.refresh = function (params) {
2981        if (params && params.url) {
2982            this.options.url = params.url;
2983        }
2984        if (params && params.pageNumber) {
2985            this.options.pageNumber = params.pageNumber;
2986        }
2987        if (params && params.pageSize) {
2988            this.options.pageSize = params.pageSize;
2989        }
2990        this.initServer(params && params.silent,
2991            params && params.query, params && params.url);
2992        this.trigger('refresh', params);
2993    };
2994
2995    BootstrapTable.prototype.resetWidth = function () {
2996        if (this.options.showHeader && this.options.height) {
2997            this.fitHeader();
2998        }
2999        if (this.options.showFooter && !this.options.cardView) {
3000            this.fitFooter();
3001        }
3002    };
3003
3004    BootstrapTable.prototype.showColumn = function (field) {
3005        this.toggleColumn(this.fieldsColumnsIndex[field], true, true);
3006    };
3007
3008    BootstrapTable.prototype.hideColumn = function (field) {
3009        this.toggleColumn(this.fieldsColumnsIndex[field], false, true);
3010    };
3011
3012    BootstrapTable.prototype.getHiddenColumns = function () {
3013        return $.grep(this.columns, function (column) {
3014            return !column.visible;
3015        });
3016    };
3017
3018    BootstrapTable.prototype.getVisibleColumns = function () {
3019        return $.grep(this.columns, function (column) {
3020            return column.visible;
3021        });
3022    };
3023
3024    BootstrapTable.prototype.toggleAllColumns = function (visible) {
3025        var that = this;
3026        $.each(this.columns, function (i, column) {
3027            that.columns[i].visible = visible;
3028        });
3029
3030        this.initHeader();
3031        this.initSearch();
3032        this.initPagination();
3033        this.initBody();
3034        if (this.options.showColumns) {
3035            var $items = this.$toolbar.find('.keep-open input').prop('disabled', false);
3036
3037            if ($items.filter(':checked').length <= this.options.minimumCountColumns) {
3038                $items.filter(':checked').prop('disabled', true);
3039            }
3040        }
3041    };
3042
3043    BootstrapTable.prototype.showAllColumns = function () {
3044        this.toggleAllColumns(true);
3045    };
3046
3047    BootstrapTable.prototype.hideAllColumns = function () {
3048        this.toggleAllColumns(false);
3049    };
3050
3051    BootstrapTable.prototype.filterBy = function (columns) {
3052        this.filterColumns = $.isEmptyObject(columns) ? {}
3052 : columns;
3053        this.options.pageNumber = 1;
3054        this.initSearch();
3055        this.updatePagination();
3056    };
3057
3058    BootstrapTable.prototype.scrollTo = function (value) {
3059        if (typeof value === 'string') {
3060            value = value === 'bottom' ? this.$tableBody[0].scrollHeight : 0;
3061        }
3062        if (typeof value === 'number') {
3063            this.$tableBody.scrollTop(value);
3064        }
3065        if (typeof value === 'undefined') {
3066            return this.$tableBody.scrollTop();
3067        }
3068    };
3069
3070    BootstrapTable.prototype.getScrollPosition = function () {
3071        return this.scrollTo();
3072    };
3073
3074    BootstrapTable.prototype.selectPage = function (page) {
3075        if (page > 0 && page <= this.options.totalPages) {
3076            this.options.pageNumber = page;
3077            this.updatePagination();
3078        }
3079    };
3080
3081    BootstrapTable.prototype.prevPage = function () {
3082        if (this.options.pageNumber > 1) {
3083            this.options.pageNumber--;
3084            this.updatePagination();
3085        }
3086    };
3087
3088    BootstrapTable.prototype.nextPage = function () {
3089        if (this.options.pageNumber < this.options.totalPages) {
3090            this.options.pageNumber++;
3091            this.updatePagination();
3092        }
3093    };
3094
3095    BootstrapTable.prototype.toggleView = function () {
3096        this.options.cardView = !this.options.cardView;
3097        this.initHeader();
3098        // Fixed remove toolbar when click cardView button.
3099        //that.initToolbar();
3100        var $icon = this.$toolbar.find('button[name="toggle"] i');
3101        if (this.options.cardView) {
3102            $icon.removeClass(this.options.icons.toggleOff);
3103            $icon.addClass(this.options.icons.toggleOn);
3104        } else {
3105            $icon.removeClass(this.options.icons.toggleOn);
3106            $icon.addClass(this.options.icons.toggleOff);
3107        }
3108        this.initBody();
3109        this.trigger('toggle', this.options.cardView);
3110    };
3111
3112    BootstrapTable.prototype.refreshOptions = function (options) {
3113        //If the objects are equivalent then avoid the call of destroy / init methods
3114        if (compareObjects(this.options, options, true)) {
3115            return;
3116        }
3117        this.options = $.extend(this.options, options);
3118        this.trigger('refresh-options', this.options);
3119        this.destroy();
3120        this.init();
3121    };
3122
3123    BootstrapTable.prototype.resetSearch = function (text) {
3124        var $search = this.$toolbar.find('.search input');
3125        $search.val(text || '');
3126        this.onSearch({currentTarget: $search});
3127    };
3128
3129    BootstrapTable.prototype.expandRow_ = function (expand, index) {
3130        var $tr = this.$body.find(sprintf('> tr[data-index="%s"]', index));
3131        if ($tr.next().is('tr.detail-view') === (expand ? false : true)) {
3132            $tr.find('> td > .detail-icon').click();
3133        }
3134    };
3135
3136    BootstrapTable.prototype.expandRow = function (index) {
3137        this.expandRow_(true, index);
3138    };
3139
3140    BootstrapTable.prototype.collapseRow = function (index) {
3141        this.expandRow_(false, index);
3142    };
3143
3144    BootstrapTable.prototype.expandAllRows = function (isSubTable) {
3145        if (isSubTable) {
3146            var $tr = this.$body.find(sprintf('> tr[data-index="%s"]', 0)),
3147                that = this,
3148                detailIcon = null,
3149                executeInterval = false,
3150                idInterval = -1;
3151
3152            if (!$tr.next().is('tr.detail-view')) {
3153                $tr.find('> td > .detail-icon').click();
3154                executeInterval = true;
3155            } else if (!$tr.next().next().is('tr.detail-view')) {
3156                $tr.next().find(".detail-icon").click();
3157                executeInterval = true;
3158            }
3159
3160            if (executeInterval) {
3161                try {
3162                    idInterval = setInterval(function () {
3163                        detailIcon = that.$body.find("tr.detail-view").last().find(".detail-icon");
3164                        if (detailIcon.length > 0) {
3165                            detailIcon.click();
3166                        } else {
3167                            clearInterval(idInterval);
3168                        }
3169                    }, 1);
3170                } catch (ex) {
3171                    clearInterval(idInterval);
3172                }
3173            }
3174        } else {
3175            var trs = this.$body.children();
3176            for (var i = 0; i < trs.length; i++) {
3177                this.expandRow_(true, $(trs[i]).data("index"));
3178            }
3179        }
3180    };
3181
3182    BootstrapTable.prototype.collapseAllRows = function (isSubTable) {
3183        if (isSubTable) {
3184            this.expandRow_(false, 0);
3185        } else {
3186            var trs = this.$body.children();
3187            for (var i = 0; i < trs.length; i++) {
3188                this.expandRow_(false, $(trs[i]).data("index"));
3189            }
3190        }
3191    };
3192
3193    BootstrapTable.prototype.updateFormatText = function (name, text) {
3194        if (this.options[sprintf('format%s', name)]) {
3195            if (typeof text === 'string') {
3196                this.options[sprintf('format%s', name)] = function () {
3197                    return text;
3198                };
3199            } else if (typeof text === 'function') {
3200                this.options[sprintf('format%s', name)] = text;
3201            }
3202        }
3203        this.initToolbar();
3204        this.initPagination();
3205        this.initBody();
3206    };
3207
3208    // BOOTSTRAP TABLE PLUGIN DEFINITION
3209    // =======================
3210
3211    var allowedMethods = [
3212        'getOptions',
3213        'getSelections', 'getAllSelections', 'getData',
3214        'load', 'append', 'prepend', 'remove', 'removeAll',
3215        'insertRow', 'updateRow', 'updateCell', 'updateByUniqueId', 'removeByUniqueId',
3216        'getRowByUniqueId', 'showRow', 'hideRow', 'getHiddenRows',
3217        'mergeCells', 'refreshColumnTitle',
3218        'checkAll', 'uncheckAll', 'checkInvert',
3219        'check', 'uncheck',
3220        'checkBy', 'uncheckBy',
3221        'refresh',
3222        'resetView',
3223        'resetWidth',
3224        'destroy',
3225        'showLoading', 'hideLoading',
3226        'showColumn', 'hideColumn', 'getHiddenColumns', 'getVisibleColumns',
3227        'showAllColumns', 'hideAllColumns',
3228        'filterBy',
3229        'scrollTo',
3230        'getScrollPosition',
3231        'selectPage', 'prevPage', 'nextPage',
3232        'togglePagination',
3233        'toggleView',
3234        'refreshOptions',
3235        'resetSearch',
3236        'expandRow', 'collapseRow', 'expandAllRows', 'collapseAllRows',
3237        'updateFormatText', 'updateCellById'
3238    ];
3239
3240    $.fn.bootstrapTable = function (option) {
3241        var value,
3242            args = Array.prototype.slice.call(arguments, 1);
3243
3244        this.each(function () {
3245            var $this = $(this),
3246                data = $this.data('bootstrap.table'),
3247                options = $.extend({}, BootstrapTable.DEFAULTS, $this.data(),
3248                    typeof option === 'object' && option);
3249
3250            if (typeof option === 'string') {
3251                if ($.inArray(option, allowedMethods) < 0) {
3252                    throw new Error("Unknown method: " + option);
3253                }
3254
3255                if (!data) {
3256                    return;
3257                }
3258
3259                value = data[option].apply(data, args);
3260
3261                if (option === 'destroy') {
3262                    $this.removeData('bootstrap.table');
3263                }
3264            }
3265
3266            if (!data) {
3267                $this.data('bootstrap.table', (data = new BootstrapTable(this, options)));
3268            }
3269        });
3270
3271        return typeof value === 'undefined' ? this : value;
3272    };
3273
3274    $.fn.bootstrapTable.Constructor = BootstrapTable;
3275    $.fn.bootstrapTable.defaults = BootstrapTable.DEFAULTS;
3276    $.fn.bootstrapTable.columnDefaults = BootstrapTable.COLUMN_DEFAULTS;
3277    $.fn.bootstrapTable.locales = BootstrapTable.LOCALES;
3278    $.fn.bootstrapTable.methods = allowedMethods;
3279    $.fn.bootstrapTable.utils = {
3280        bootstrapVersion: bootstrapVersion,
3281        sprintf: sprintf,
3282        compareObjects: compareObjects,
3283        calculateObjectValue: calculateObjectValue,
3284        getItemField: getItemField,
3285        objectKeys: objectKeys,
3286        isIEBrowser: isIEBrowser
3287    };
3288
3289    // BOOTSTRAP TABLE INIT
3290    // =======================
3291
3292    $(function () {
3293        $('[data-toggle="table"]').bootstrapTable();
3294    });
3295})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.