Browse Source

Merge pull request #93 from wenzhixin/develop

Develop
Dennis Hernández 10 years ago
parent
commit
4395d7cff9

+ 2 - 2
docs/_i18n/en/documentation/methods.md

@@ -30,13 +30,13 @@ The calling method syntax: `$('#table').bootstrapTable('method', parameter);`.
     <tr>
     <tr>
         <td>getSelections</td>
         <td>getSelections</td>
         <td>none</td>
         <td>none</td>
-        <td>Return selected rows, when no record selected, am empty array will return.</td>
+        <td>Return selected rows, when no record selected, an empty array will return.</td>
         <td>getSelections</td>
         <td>getSelections</td>
     </tr>
     </tr>
     <tr>
     <tr>
         <td>getAllSelections</td>
         <td>getAllSelections</td>
         <td>none</td>
         <td>none</td>
-        <td>Return all selected rows contain search or filter, when no record selected, am empty array will return.</td>
+        <td>Return all selected rows contain search or filter, when no record selected, an empty array will return.</td>
         <td>getAllSelections</td>
         <td>getAllSelections</td>
     </tr>
     </tr>
     <tr>
     <tr>

+ 3 - 17
docs/_i18n/en/documentation/table-options.md

@@ -82,7 +82,7 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
         <td>String</td>
         <td>String</td>
         <td>undefined</td>
         <td>undefined</td>
         <td>Defines icon size: <ul><li>undefined => btn</li><li>xs => btn-xs</li><li>sm => btn-sm</li><li>lg => btn-lg</li></ul>
         <td>Defines icon size: <ul><li>undefined => btn</li><li>xs => btn-xs</li><li>sm => btn-sm</li><li>lg => btn-lg</li></ul>
-    </tr>    
+    </tr>
     <tr>
     <tr>
         <td>icons</td>
         <td>icons</td>
         <td>data-icons</td>
         <td>data-icons</td>
@@ -184,7 +184,7 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
         <td>Function</td>
         <td>Function</td>
         <td>function(params) {<br>return params;<br>}</td>
         <td>function(params) {<br>return params;<br>}</td>
         <td>
         <td>
-        When requesting remote data, you can send additional parameters by modifying queryParams. 
+        When requesting remote data, you can send additional parameters by modifying queryParams.
         If queryParamsType = 'limit', the params object contains: <br>
         If queryParamsType = 'limit', the params object contains: <br>
         limit, offset, search, sort, order
         limit, offset, search, sort, order
         Else, it contains: <br>
         Else, it contains: <br>
@@ -438,13 +438,6 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
         <td>Indicate how to align the pagination detail. 'left', 'right' can be used.</td>
         <td>Indicate how to align the pagination detail. 'left', 'right' can be used.</td>
     </tr>
     </tr>
     <tr>
     <tr>
-        <td>paginationFirstText</td>
-        <td>data-pagination-first-text</td>
-        <td>String</td>
-        <td>'&lt;&lt;'</td>
-        <td>Indicate the icon or text to be shown in the pagination detail, the first button of the pagination detail.</td>
-    </tr>
-    <tr>
         <td>paginationPreText</td>
         <td>paginationPreText</td>
         <td>data-pagination-pre-text</td>
         <td>data-pagination-pre-text</td>
         <td>String</td>
         <td>String</td>
@@ -459,13 +452,6 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
         <td>Indicate the icon or text to be shown in the pagination detail, the next button.</td>
         <td>Indicate the icon or text to be shown in the pagination detail, the next button.</td>
     </tr>
     </tr>
     <tr>
     <tr>
-        <td>paginationLastText</td>
-        <td>data-pagination-last-text</td>
-        <td>String</td>
-        <td>'&gt;&gt;'</td>
-        <td>Indicate the icon or text to be shown in the pagination detail, the last button.</td>
-    </tr>
-    <tr>
         <td>clickToSelect</td>
         <td>clickToSelect</td>
         <td>data-click-to-select</td>
         <td>data-click-to-select</td>
         <td>Boolean</td>
         <td>Boolean</td>
@@ -551,7 +537,7 @@ The table options are defined in `jQuery.fn.bootstrapTable.defaults`.
         Allows for fallback locales, if loaded, in the following order:<br>
         Allows for fallback locales, if loaded, in the following order:<br>
         <ol>
         <ol>
         <li>First tries for the locale as specified,</li>
         <li>First tries for the locale as specified,</li>
-        <li>Then tries the locale with <code>'_'</code> translated to 
+        <li>Then tries the locale with <code>'_'</code> translated to
         <code>'-'</code> and the region code upper cased,</li>
         <code>'-'</code> and the region code upper cased,</li>
         <li>Then tries the the short locale code (i.e. <code>'fr'</code> instead of <code>'fr-CA'</code>),</li>
         <li>Then tries the the short locale code (i.e. <code>'fr'</code> instead of <code>'fr-CA'</code>),</li>
         <li>And finally will use the last locale file loaded (or the default locale if no locales loaded).</li>
         <li>And finally will use the last locale file loaded (or the default locale if no locales loaded).</li>

+ 1 - 15
docs/_i18n/es/documentation/table-options.md

@@ -149,7 +149,7 @@ Las opciones de la tabla están definidas en `jQuery.fn.bootstrapTable.defaults`
         <td>Function</td>
         <td>Function</td>
         <td>function(params) {<br>return params;<br>}</td>
         <td>function(params) {<br>return params;<br>}</td>
         <td>
         <td>
-        Cuando se solicita datos remotos, se debe enviar parámetros adicionales para modificar los queryParams. 
+        Cuando se solicita datos remotos, se debe enviar parámetros adicionales para modificar los queryParams.
         Si queryParamsType = 'limit', el objecto params contiene: <br>
         Si queryParamsType = 'limit', el objecto params contiene: <br>
         limit, offset, search, sort, order
         limit, offset, search, sort, order
         Sino, el objeoto contiene: <br>
         Sino, el objeoto contiene: <br>
@@ -385,13 +385,6 @@ Las opciones de la tabla están definidas en `jQuery.fn.bootstrapTable.defaults`
         <td>Indica cómo alinear el detalle de la paginación. Se puede usar: 'left', 'right'.</td>
         <td>Indica cómo alinear el detalle de la paginación. Se puede usar: 'left', 'right'.</td>
     </tr>
     </tr>
     <tr>
     <tr>
-        <td>paginationFirstText</td>
-        <td>data-pagination-first-text</td>
-        <td>String</td>
-        <td>'&lt;&lt;'</td>
-        <td>Indica el icono o el texto a mostrar en la paginación, el botón first del detalle de la paginación.</td>
-    </tr>
-    <tr>
         <td>paginationPreText</td>
         <td>paginationPreText</td>
         <td>data-pagination-pre-text</td>
         <td>data-pagination-pre-text</td>
         <td>String</td>
         <td>String</td>
@@ -406,13 +399,6 @@ Las opciones de la tabla están definidas en `jQuery.fn.bootstrapTable.defaults`
         <td>Indica el icono o el texto a mostrar en la paginación, el botón next del detalle de la paginación.</td>
         <td>Indica el icono o el texto a mostrar en la paginación, el botón next del detalle de la paginación.</td>
     </tr>
     </tr>
     <tr>
     <tr>
-        <td>paginationLastText</td>
-        <td>data-pagination-last-text</td>
-        <td>String</td>
-        <td>'&gt;&gt;'</td>
-        <td>Indica el icono o el texto a mostrar en la paginación, el botón last del detalle de la paginación.</td>
-    </tr>
-    <tr>
         <td>clickToSelect</td>
         <td>clickToSelect</td>
         <td>data-click-to-select</td>
         <td>data-click-to-select</td>
         <td>Boolean</td>
         <td>Boolean</td>

+ 1 - 15
docs/_i18n/zh-cn/documentation/table-options.md

@@ -157,7 +157,7 @@
         <td>Function</td>
         <td>Function</td>
         <td>function(params) {<br>return params;<br>}</td>
         <td>function(params) {<br>return params;<br>}</td>
         <td>
         <td>
-        When requesting remote data, you can send additional parameters by modifying queryParams. 
+        When requesting remote data, you can send additional parameters by modifying queryParams.
         If queryParamsType = 'limit', the params object contains: <br>
         If queryParamsType = 'limit', the params object contains: <br>
         limit, offset, search, sort, order
         limit, offset, search, sort, order
         Else, it contains: <br>
         Else, it contains: <br>
@@ -402,13 +402,6 @@
         <td>Indicate how to align the pagination detail. 'left', 'right' can be used.</td>
         <td>Indicate how to align the pagination detail. 'left', 'right' can be used.</td>
     </tr>
     </tr>
     <tr>
     <tr>
-        <td>paginationFirstText</td>
-        <td>data-pagination-first-text</td>
-        <td>String</td>
-        <td>'&lt;&lt;'</td>
-        <td>Indicate the icon or text to be shown in the pagination detail, the first button of the pagination detail.</td>
-    </tr>
-    <tr>
         <td>paginationPreText</td>
         <td>paginationPreText</td>
         <td>data-pagination-pre-text</td>
         <td>data-pagination-pre-text</td>
         <td>String</td>
         <td>String</td>
@@ -423,13 +416,6 @@
         <td>Indicate the icon or text to be shown in the pagination detail, the next button.</td>
         <td>Indicate the icon or text to be shown in the pagination detail, the next button.</td>
     </tr>
     </tr>
     <tr>
     <tr>
-        <td>paginationLastText</td>
-        <td>data-pagination-last-text</td>
-        <td>String</td>
-        <td>'&gt;&gt;'</td>
-        <td>Indicate the icon or text to be shown in the pagination detail, the last button.</td>
-    </tr>
-    <tr>
         <td>clickToSelect</td>
         <td>clickToSelect</td>
         <td>data-click-to-select</td>
         <td>data-click-to-select</td>
         <td>Boolean</td>
         <td>Boolean</td>

+ 5 - 0
docs/_includes/header.html

@@ -43,6 +43,11 @@
   ga('send', 'pageview');
   ga('send', 'pageview');
 </script>
 </script>
 
 
+<script type="text/javascript">
+    if (window!=top) // 判断当前的window对象是否是top对象
+        top.location.href = window.location.href; // 如果不是,将top对象的网址自动导向被嵌入网页的网址
+</script>
+
 <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
 <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
 
 
 <!-- 将此标记放置在 head 中,或放置在结束 body 标记之前,并使其紧邻此标记。 -->
 <!-- 将此标记放置在 head 中,或放置在结束 body 标记之前,并使其紧邻此标记。 -->

+ 1 - 1
src/bootstrap-table.css

@@ -1,6 +1,6 @@
 /**
 /**
  * @author zhixin wen <wenzhixin2010@gmail.com>
  * @author zhixin wen <wenzhixin2010@gmail.com>
- * version: 1.8.1
+ * version: 1.9.1
  * https://github.com/wenzhixin/bootstrap-table/
  * https://github.com/wenzhixin/bootstrap-table/
  */
  */
 
 

+ 70 - 24
src/bootstrap-table.js

@@ -272,10 +272,8 @@
         paginationHAlign: 'right', //right, left
         paginationHAlign: 'right', //right, left
         paginationVAlign: 'bottom', //bottom, top, both
         paginationVAlign: 'bottom', //bottom, top, both
         paginationDetailHAlign: 'left', //right, left
         paginationDetailHAlign: 'left', //right, left
-        paginationFirstText: '&laquo;',
         paginationPreText: '&lsaquo;',
         paginationPreText: '&lsaquo;',
         paginationNextText: '&rsaquo;',
         paginationNextText: '&rsaquo;',
-        paginationLastText: '&raquo;',
         search: false,
         search: false,
         strictSearch: false,
         strictSearch: false,
         searchAlign: 'right',
         searchAlign: 'right',
@@ -1001,7 +999,7 @@
 
 
         html.push('</div>');
         html.push('</div>');
 
 
-        // Fix #188: this.showToolbar is for extentions
+        // Fix #188: this.showToolbar is for extensions
         if (this.showToolbar || html.length > 2) {
         if (this.showToolbar || html.length > 2) {
             this.$toolbar.append(html.join(''));
             this.$toolbar.append(html.join(''));
         }
         }
@@ -1110,7 +1108,7 @@
                         column = that.columns[getFieldIndex(that.columns, key)],
                         column = that.columns[getFieldIndex(that.columns, key)],
                         j = $.inArray(key, that.header.fields);
                         j = $.inArray(key, that.header.fields);
 
 
-                    // Fix #142: search use formated data
+                    // Fix #142: search use formatted data
                     if (column && column.searchFormatter) {
                     if (column && column.searchFormatter) {
                         value = calculateObjectValue(column,
                         value = calculateObjectValue(column,
                             that.header.formatters[j], [value, item, i], value);
                             that.header.formatters[j], [value, item, i], value);
@@ -1242,7 +1240,6 @@
             html.push('</div>',
             html.push('</div>',
                 '<div class="pull-' + this.options.paginationHAlign + ' pagination">',
                 '<div class="pull-' + this.options.paginationHAlign + ' pagination">',
                 '<ul class="pagination' + sprintf(' pagination-%s', this.options.iconSize) + '">',
                 '<ul class="pagination' + sprintf(' pagination-%s', this.options.iconSize) + '">',
-                '<li class="page-first"><a href="javascript:void(0)">' + this.options.paginationFirstText + '</a></li>',
                 '<li class="page-pre"><a href="javascript:void(0)">' + this.options.paginationPreText + '</a></li>');
                 '<li class="page-pre"><a href="javascript:void(0)">' + this.options.paginationPreText + '</a></li>');
 
 
             if (this.totalPages < 5) {
             if (this.totalPages < 5) {
@@ -1260,18 +1257,71 @@
                     from = to - 4;
                     from = to - 4;
                 }
                 }
             }
             }
+
+            if (this.totalPages >= 6) {
+                if (this.options.pageNumber >= 3) {
+                    html.push('<li class="page-first' + (1 === this.options.pageNumber ? ' active' : '') + '">',
+                        '<a href="javascript:void(0)">', 1, '</a>',
+                        '</li>');
+
+                    from++;
+                }
+
+                if (this.options.pageNumber >= 4) {
+                    if (this.options.pageNumber == 4 || this.totalPages == 6 || this.totalPages == 7) {
+                        from--;
+                    } else {
+                        html.push('<li class="page-first-separator disabled">',
+                            '<a href="javascript:void(0)">...</a>',
+                            '</li>');
+                    }
+
+                    to--;
+                }
+            }
+
+            if (this.totalPages >= 7) {
+                if (this.options.pageNumber >= (this.totalPages - 2)) {
+                    from--;
+                }
+            }
+
+            if (this.totalPages == 6) {
+                if (this.options.pageNumber >= (this.totalPages - 2)) {
+                    to++;
+                }
+            } else if (this.totalPages >= 7) {
+                if (this.totalPages == 7 || this.options.pageNumber >= (this.totalPages - 3)) {
+                    to++;
+                }
+            }
+
             for (i = from; i <= to; i++) {
             for (i = from; i <= to; i++) {
                 html.push('<li class="page-number' + (i === this.options.pageNumber ? ' active' : '') + '">',
                 html.push('<li class="page-number' + (i === this.options.pageNumber ? ' active' : '') + '">',
                     '<a href="javascript:void(0)">', i, '</a>',
                     '<a href="javascript:void(0)">', i, '</a>',
                     '</li>');
                     '</li>');
             }
             }
 
 
+            if (this.totalPages >= 8) {
+                if (this.options.pageNumber <= (this.totalPages - 4)) {
+                    html.push('<li class="page-last-separator disabled">',
+                        '<a href="javascript:void(0)">...</a>',
+                        '</li>');
+                }
+            }
+
+            if (this.totalPages >= 6) {
+                if (this.options.pageNumber <= (this.totalPages - 3)) {
+                    html.push('<li class="page-last' + (this.totalPages === this.options.pageNumber ? ' active' : '') + '">',
+                        '<a href="javascript:void(0)">', this.totalPages, '</a>',
+                        '</li>');
+                }
+            }
+
             html.push(
             html.push(
                 '<li class="page-next"><a href="javascript:void(0)">' + this.options.paginationNextText + '</a></li>',
                 '<li class="page-next"><a href="javascript:void(0)">' + this.options.paginationNextText + '</a></li>',
-                '<li class="page-last"><a href="javascript:void(0)">' + this.options.paginationLastText + '</a></li>',
                 '</ul>',
                 '</ul>',
                 '</div>');
                 '</div>');
-
         }
         }
         this.$pagination.html(html.join(''));
         this.$pagination.html(html.join(''));
 
 
@@ -1283,14 +1333,6 @@
             $last = this.$pagination.find('.page-last');
             $last = this.$pagination.find('.page-last');
             $number = this.$pagination.find('.page-number');
             $number = this.$pagination.find('.page-number');
 
 
-            if (this.options.pageNumber <= 1) {
-                $first.addClass('disabled');
-                $pre.addClass('disabled');
-            }
-            if (this.options.pageNumber >= this.totalPages) {
-                $next.addClass('disabled');
-                $last.addClass('disabled');
-            }
             if (this.options.smartDisplay) {
             if (this.options.smartDisplay) {
                 if (this.totalPages <= 1) {
                 if (this.totalPages <= 1) {
                     this.$pagination.find('div.pagination').hide();
                     this.$pagination.find('div.pagination').hide();
@@ -1351,12 +1393,20 @@
     };
     };
 
 
     BootstrapTable.prototype.onPagePre = function (event) {
     BootstrapTable.prototype.onPagePre = function (event) {
-        this.options.pageNumber--;
+        if ((this.options.pageNumber - 1) == 0) {
+            this.options.pageNumber = this.options.totalPages;
+        } else {
+            this.options.pageNumber--;
+        }
         this.updatePagination(event);
         this.updatePagination(event);
     };
     };
 
 
     BootstrapTable.prototype.onPageNext = function (event) {
     BootstrapTable.prototype.onPageNext = function (event) {
-        this.options.pageNumber++;
+        if ((this.options.pageNumber + 1) > this.options.totalPages) {
+            this.options.pageNumber = 1;
+        } else {
+            this.options.pageNumber++;
+        }
         this.updatePagination(event);
         this.updatePagination(event);
     };
     };
 
 
@@ -1749,14 +1799,11 @@
 
 
                 that.load(res);
                 that.load(res);
                 that.trigger('load-success', res);
                 that.trigger('load-success', res);
+                if (!silent) that.$tableLoading.hide();
             },
             },
             error: function (res) {
             error: function (res) {
                 that.trigger('load-error', res.status, res);
                 that.trigger('load-error', res.status, res);
-            },
-            complete: function () {
-                if (!silent) {
-                    that.$tableLoading.hide();
-                }
+                if (!silent) that.$tableLoading.hide();
             }
             }
         });
         });
 
 
@@ -2685,5 +2732,4 @@
     $(function () {
     $(function () {
         $('[data-toggle="table"]').bootstrapTable();
         $('[data-toggle="table"]').bootstrapTable();
     });
     });
-
-}(jQuery);
+}(jQuery);

+ 1 - 0
src/extensions/editable/bootstrap-table-editable.js

@@ -81,6 +81,7 @@
                         row = data[index],
                         row = data[index],
                         oldValue = row[column.field];
                         oldValue = row[column.field];
 
 
+                    $(this).data('value', params.submitValue);
                     row[column.field] = params.submitValue;
                     row[column.field] = params.submitValue;
                     that.trigger('editable-save', column.field, row, oldValue, $(this));
                     that.trigger('editable-save', column.field, row, oldValue, $(this));
                 });
                 });

+ 6 - 1
src/extensions/filter-control/README.md

@@ -23,6 +23,11 @@ Dependence if you use the datepicker option: [bootstrap-datepicker](https://gith
 * description: Set true to add a button to clear all the controls added by this plugin
 * description: Set true to add a button to clear all the controls added by this plugin
 * default: `false`
 * default: `false`
 
 
+### filterLocal
+
+* type: Boolean
+* description: Set false to disable local data filtering. This allows you to filter the data on server side when using remote data sources.
+* default: `true`
 
 
 ## Column options
 ## Column options
 
 
@@ -49,4 +54,4 @@ Dependence if you use the datepicker option: [bootstrap-datepicker](https://gith
 
 
 ### onColumnSearch(column-search.bs.table)
 ### onColumnSearch(column-search.bs.table)
 
 
-* Fired when we are searching into the column data
+* Fired when we are searching into the column data

+ 5 - 0
src/extensions/filter-control/bootstrap-table-filter-control.js

@@ -238,6 +238,7 @@
             return false;
             return false;
         },
         },
         filterShowClear: false,
         filterShowClear: false,
+        filterLocal: true,
         //internal variables
         //internal variables
         values: []
         values: []
     });
     });
@@ -364,6 +365,10 @@
     BootstrapTable.prototype.initSearch = function () {
     BootstrapTable.prototype.initSearch = function () {
         _initSearch.apply(this, Array.prototype.slice.apply(arguments));
         _initSearch.apply(this, Array.prototype.slice.apply(arguments));
 
 
+        if (! this.options.filterLocal) {
+            return;
+        }
+
         var that = this;
         var that = this;
         var fp = $.isEmptyObject(this.filterColumnsPartial) ? null : this.filterColumnsPartial;
         var fp = $.isEmptyObject(this.filterColumnsPartial) ? null : this.filterColumnsPartial;
 
 

+ 24 - 0
src/extensions/group-by-v2/README.md

@@ -0,0 +1,24 @@
+# Table group-by-v2
+
+Use Plugin: [bootstrap-table-group-by-v2](https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/group-by-v2) </br>
+You must include the bootstrap-table-group-by.css file in order to get the appropriate style
+
+## Usage
+
+```html
+<script src="extensions/group-by-v2/bootstrap-table-group-by.js"></script>
+```
+
+## Options
+
+### groupBy
+
+* type: Boolean
+* description: Set true to group the data by the field passed.
+* default: `false`
+
+### groupByField
+
+* type: String
+* description: Set the fields name that you want to group the data.
+* default: ``

+ 7 - 0
src/extensions/group-by-v2/bootstrap-table-group-by.css

@@ -0,0 +1,7 @@
+.bootstrap-table .table > tbody > tr.groupBy {
+    cursor: pointer;
+}
+
+.bootstrap-table .table > tbody > tr.groupBy.expanded {
+
+}

+ 226 - 0
src/extensions/group-by-v2/bootstrap-table-group-by.js

@@ -0,0 +1,226 @@
+/**
+ * @author: Yura Knoxville
+ * @version: v1.0.0
+ */
+
+!function ($) {
+
+    'use strict';
+
+    var initBodyCaller,
+        tableGroups;
+
+    // it only does '%s', and return '' when arguments are undefined
+    var sprintf = function (str) {
+        var args = arguments,
+            flag = true,
+            i = 1;
+
+        str = str.replace(/%s/g, function () {
+            var arg = args[i++];
+
+            if (typeof arg === 'undefined') {
+                flag = false;
+                return '';
+            }
+            return arg;
+        });
+        return flag ? str : '';
+    };
+
+    var groupBy = function (array , f) {
+        var groups = {};
+        array.forEach(function(o) {
+            var group = f(o);
+            groups[group] = groups[group] || [];
+            groups[group].push(o);
+        });
+
+        return groups;
+    };
+
+    $.extend($.fn.bootstrapTable.defaults, {
+        groupBy: false,
+        groupByField: ''
+    });
+
+    var BootstrapTable = $.fn.bootstrapTable.Constructor,
+        _initSort = BootstrapTable.prototype.initSort,
+        _initBody = BootstrapTable.prototype.initBody,
+        _updateSelected = BootstrapTable.prototype.updateSelected;
+
+    BootstrapTable.prototype.initSort = function () {
+        _initSort.apply(this, Array.prototype.slice.apply(arguments));
+
+        var that = this;
+        tableGroups = [];
+
+        if ((this.options.groupBy) && (this.options.groupByField !== '')) {
+
+            if ((this.options.sortName != this.options.groupByField)) {
+                this.data.sort(function(a, b) {
+                    return a[that.options.groupByField].localeCompare(b[that.options.groupByField]);
+                });
+            }
+
+            var that = this;
+            var groups = groupBy(that.data, function (item) {
+                return [item[that.options.groupByField]];
+            });
+
+            var index = 0;
+            $.each(groups, function(key, value) {
+                tableGroups.push({
+                    id: index,
+                    name: key
+                });
+
+                value.forEach(function(item) {
+                    if (!item._data) {
+                        item._data = {};
+                    }
+
+                    item._data['parent-index'] = index;
+                });
+
+                index++;
+            });
+        }
+    }
+
+    BootstrapTable.prototype.initBody = function () {
+        initBodyCaller = true;
+
+        _initBody.apply(this, Array.prototype.slice.apply(arguments));
+
+        if ((this.options.groupBy) && (this.options.groupByField !== '')) {
+            var that = this,
+                checkBox = false,
+                visibleColumns = 0;
+
+            this.columns.forEach(function(column) {
+                if (column.checkbox) {
+                    checkBox = true;
+                } else {
+                    if (column.visible) {
+                        visibleColumns += 1;
+                    }
+                }
+            });
+
+            if (this.options.detailView && !this.options.cardView) {
+                visibleColumns += 1;
+            }
+
+            tableGroups.forEach(function(item){
+                var html = [];
+
+                html.push(sprintf('<tr class="info groupBy expanded" data-group-index="%s">', item.id));
+
+                if (that.options.detailView && !that.options.cardView) {
+                    html.push('<td class="detail"></td>');
+                }
+
+                if (checkBox) {
+                    html.push('<td class="bs-checkbox">',
+                        '<input name="btSelectGroup" type="checkbox" />',
+                        '</td>'
+                    );
+                }
+
+                html.push('<td',
+                    sprintf(' colspan="%s"', visibleColumns),
+                    '>', item.name, '</td>'
+                );
+
+                html.push('</tr>');
+
+                that.$body.find('tr[data-parent-index='+item.id+']:first').before($(html.join('')));
+            });
+
+            this.$selectGroup = [];
+            this.$body.find('[name="btSelectGroup"]').each(function() {
+                var self = $(this);
+
+                that.$selectGroup.push({
+                    group: self,
+                    item: that.$selectItem.filter(function () {
+                        return ($(this).closest('tr').data('parent-index') ===
+                        self.closest('tr').data('group-index'));
+                    })
+                });
+            });
+
+            this.$container.off('click', '.groupBy')
+                .on('click', '.groupBy', function() {
+                    $(this).toggleClass('expanded');
+                    that.$body.find('tr[data-parent-index='+$(this).closest('tr').data('group-index')+']').toggleClass('hidden');
+                });
+
+            this.$container.off('click', '[name="btSelectGroup"]')
+                .on('click', '[name="btSelectGroup"]', function (event) {
+                    event.stopImmediatePropagation();
+
+                    var self = $(this);
+                    var checked = self.prop('checked');
+                    that[checked ? 'checkGroup' : 'uncheckGroup']($(this).closest('tr').data('group-index'));
+                });
+        }
+
+        initBodyCaller = false;
+        this.updateSelected();
+    };
+
+    BootstrapTable.prototype.updateSelected = function () {
+        if (!initBodyCaller) {
+            _updateSelected.apply(this, Array.prototype.slice.apply(arguments));
+
+            if ((this.options.groupBy) && (this.options.groupByField !== '')) {
+                this.$selectGroup.forEach(function (item) {
+                    var checkGroup = item.item.filter(':enabled').length ===
+                        item.item.filter(':enabled').filter(':checked').length;
+
+                    item.group.prop('checked', checkGroup);
+                });
+            }
+        }
+    };
+
+    BootstrapTable.prototype.getGroupSelections = function (index) {
+        var that = this;
+
+        return $.grep(this.data, function (row) {
+            return (row[that.header.stateField] && (row._data['parent-index'] === index));
+        });
+    };
+
+    BootstrapTable.prototype.checkGroup = function (index) {
+        this.checkGroup_(index, true);
+    };
+
+    BootstrapTable.prototype.uncheckGroup = function (index) {
+        this.checkGroup_(index, false);
+    };
+
+    BootstrapTable.prototype.checkGroup_ = function (index, checked) {
+        var rows;
+        var filter = function() {
+            return ($(this).closest('tr').data('parent-index') === index);
+        };
+
+        if (!checked) {
+            rows = this.getGroupSelections(index);
+        }
+
+        this.$selectItem.filter(filter).prop('checked', checked);
+
+
+        this.updateRows();
+        this.updateSelected();
+        if (checked) {
+            rows = this.getGroupSelections(index);
+        }
+        this.trigger(checked ? 'check-all' : 'uncheck-all', rows);
+    };
+
+}(jQuery);

+ 12 - 0
src/extensions/group-by-v2/extension.json

@@ -0,0 +1,12 @@
+{
+  "name": "Group By",
+  "version": "1.0.0",
+  "description": "Group the data by field",
+  "url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/group-by-v2",
+  "example": "",
+  "plugins": [],
+  "author": {
+    "name": "Knoxvillekm",
+    "image": "https://avatars3.githubusercontent.com/u/11072464"
+  }
+}

+ 1 - 1
src/locale/bootstrap-table-zh-TW.js

@@ -19,7 +19,7 @@
             return '搜尋';
             return '搜尋';
         },
         },
         formatNoMatches: function () {
         formatNoMatches: function () {
-            return '沒有找符合的結果';
+            return '沒有找符合的結果';
         },
         },
         formatPaginationSwitch: function () {
         formatPaginationSwitch: function () {
             return '隱藏/顯示分頁';
             return '隱藏/顯示分頁';