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