comparison bin/js/bootstrap-table.js @ 8:c61e7cc135c2 draft

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