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