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