jquery-datatables-rails 2.2.3 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 809f7c045592c04bbd407dc7d2f7296807fd3f78
4
- data.tar.gz: 67c2a1f408d9d8cddce1c6334af9849d4cdb5ac8
3
+ metadata.gz: 4ca11d8aeb50210204b7e77bb8e1e97f896c2cb1
4
+ data.tar.gz: ca675097df780258be6de80ebbce4e638893a6bf
5
5
  SHA512:
6
- metadata.gz: 518fbe26ee4fa7e56c165f28e0e24788fd1af7a76c9f00cb1b0ca0c2292349a158a25fdf48094a28a4106f118fdce3c8200406bf1efd3021dd6358b796d2b65f
7
- data.tar.gz: b5240c8f4ac85ac8a560f3455dde501ab9c3e19fce8fca500935d2afe7d10ef9e89c2b0e3df42b238781d4e5dc176c9d8b39a156f604105925dc0883c5e13119
6
+ metadata.gz: 272b904d03a7ccbf8bb94af7addd12a7c6e01e8012d17c6ba027e696c914496228f7a9d6286dec386e61146732e963206e314aedbf487846676ab4b61e81fedb
7
+ data.tar.gz: 9e64a787991fdd21f07b327cff8da3a972803d6d83c8a7ea4a9179c39a452860ca2fb879573ffa5307b0877b03091cf7590e7c1e913b8148f8cca942b0be973f
@@ -1,8 +1,19 @@
1
+ /* Set the defaults for DataTables initialisation */
2
+ $.extend( true, $.fn.dataTable.defaults, {
3
+ "sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>",
4
+ "sPaginationType": "bootstrap",
5
+ "oLanguage": {
6
+ "sLengthMenu": "_MENU_ records per page"
7
+ }
8
+ } );
9
+
10
+
1
11
  /* Default class modification */
2
12
  $.extend( $.fn.dataTableExt.oStdClasses, {
3
13
  "sWrapper": "dataTables_wrapper form-inline"
4
14
  } );
5
15
 
16
+
6
17
  /* API method to get paging information */
7
18
  $.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings )
8
19
  {
@@ -12,10 +23,13 @@ $.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings )
12
23
  "iLength": oSettings._iDisplayLength,
13
24
  "iTotal": oSettings.fnRecordsTotal(),
14
25
  "iFilteredTotal": oSettings.fnRecordsDisplay(),
15
- "iPage": Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
16
- "iTotalPages": Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
26
+ "iPage": oSettings._iDisplayLength === -1 ?
27
+ 0 : Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
28
+ "iTotalPages": oSettings._iDisplayLength === -1 ?
29
+ 0 : Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
17
30
  };
18
- }
31
+ };
32
+
19
33
 
20
34
  /* Bootstrap style pagination control */
21
35
  $.extend( $.fn.dataTableExt.oPagination, {
@@ -41,10 +55,10 @@ $.extend( $.fn.dataTableExt.oPagination, {
41
55
  },
42
56
 
43
57
  "fnUpdate": function ( oSettings, fnDraw ) {
44
- var iListLength = jQuery.fn.dataTableExt.oPagination.iFullNumbersShowPages;
58
+ var iListLength = 5;
45
59
  var oPaging = oSettings.oInstance.fnPagingInfo();
46
60
  var an = oSettings.aanFeatures.p;
47
- var i, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2);
61
+ var i, ien, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2);
48
62
 
49
63
  if ( oPaging.iTotalPages < iListLength) {
50
64
  iStart = 1;
@@ -61,7 +75,7 @@ $.extend( $.fn.dataTableExt.oPagination, {
61
75
  iEnd = iStart + iListLength - 1;
62
76
  }
63
77
 
64
- for ( i=0, iLen=an.length ; i<iLen ; i++ ) {
78
+ for ( i=0, ien=an.length ; i<ien ; i++ ) {
65
79
  // Remove the middle elements
66
80
  $('li:gt(0)', an[i]).filter(':not(:last)').remove();
67
81
 
@@ -93,3 +107,42 @@ $.extend( $.fn.dataTableExt.oPagination, {
93
107
  }
94
108
  }
95
109
  } );
110
+
111
+
112
+ /*
113
+ * TableTools Bootstrap compatibility
114
+ * Required TableTools 2.1+
115
+ */
116
+ if ( $.fn.DataTable.TableTools ) {
117
+ // Set the classes that TableTools uses to something suitable for Bootstrap
118
+ $.extend( true, $.fn.DataTable.TableTools.classes, {
119
+ "container": "DTTT btn-group",
120
+ "buttons": {
121
+ "normal": "btn",
122
+ "disabled": "disabled"
123
+ },
124
+ "collection": {
125
+ "container": "DTTT_dropdown dropdown-menu",
126
+ "buttons": {
127
+ "normal": "",
128
+ "disabled": "disabled"
129
+ }
130
+ },
131
+ "print": {
132
+ "info": "DTTT_print_info modal"
133
+ },
134
+ "select": {
135
+ "row": "active"
136
+ }
137
+ } );
138
+
139
+ // Have the collection use a bootstrap compatible dropdown
140
+ $.extend( true, $.fn.DataTable.TableTools.DEFAULTS.oTags, {
141
+ "collection": {
142
+ "container": "ul",
143
+ "button": "li",
144
+ "liner": "a"
145
+ }
146
+ } );
147
+ }
148
+
@@ -1,650 +1,801 @@
1
+ /*! Responsive 1.0.2
2
+ * 2014 SpryMedia Ltd - datatables.net/license
3
+ */
4
+
1
5
  /**
2
- * File: datatables.responsive.js
3
- * Version: 0.2.0
4
- * Author: Seen Sai Yang
5
- * Info: https://github.com/Comanche/datatables-responsive
6
+ * @summary Responsive
7
+ * @description Responsive tables plug-in for DataTables
8
+ * @version 1.0.2
9
+ * @file dataTables.responsive.js
10
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
11
+ * @contact www.sprymedia.co.uk/contact
12
+ * @copyright Copyright 2014 SpryMedia Ltd.
6
13
  *
7
- * Copyright 2013 Seen Sai Yang, all rights reserved.
8
- *
9
- * This source file is free software, under either the GPL v2 license or a
10
- * BSD style license.
14
+ * This source file is free software, available under the following license:
15
+ * MIT license - http://datatables.net/license/mit
11
16
  *
12
17
  * This source file is distributed in the hope that it will be useful, but
13
18
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
19
  * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
15
20
  *
16
- * You should have received a copy of the GNU General Public License and the
17
- * BSD license along with this program. These licenses are also available at:
18
- * https://raw.github.com/Comanche/datatables-responsive/master/license-gpl2.txt
19
- * https://raw.github.com/Comanche/datatables-responsive/master/license-bsd.txt
21
+ * For details please refer to: http://www.datatables.net
20
22
  */
21
23
 
22
- 'use strict';
24
+ (function(window, document, undefined) {
25
+
26
+
27
+ var factory = function( $, DataTable ) {
28
+ "use strict";
23
29
 
24
30
  /**
25
- * Constructor for responsive datables helper.
31
+ * Responsive is a plug-in for the DataTables library that makes use of
32
+ * DataTables' ability to change the visibility of columns, changing the
33
+ * visibility of columns so the displayed columns fit into the table container.
34
+ * The end result is that complex tables will be dynamically adjusted to fit
35
+ * into the viewport, be it on a desktop, tablet or mobile browser.
26
36
  *
27
- * This helper class makes datatables responsive to the window size.
37
+ * Responsive for DataTables has two modes of operation, which can used
38
+ * individually or combined:
28
39
  *
29
- * The parameter, breakpoints, is an object for each breakpoint key/value pair
30
- * with the following format: { breakpoint_name: pixel_width_at_breakpoint }.
40
+ * * Class name based control - columns assigned class names that match the
41
+ * breakpoint logic can be shown / hidden as required for each breakpoint.
42
+ * * Automatic control - columns are automatically hidden when there is no
43
+ * room left to display them. Columns removed from the right.
31
44
  *
32
- * An example is as follows:
45
+ * In additional to column visibility control, Responsive also has built into
46
+ * options to use DataTables' child row display to show / hide the information
47
+ * from the table that has been hidden. There are also two modes of operation
48
+ * for this child row display:
33
49
  *
34
- * {
35
- * tablet: 1024,
36
- * phone: 480
37
- * }
50
+ * * Inline - when the control element that the user can use to show / hide
51
+ * child rows is displayed inside the first column of the table.
52
+ * * Column - where a whole column is dedicated to be the show / hide control.
38
53
  *
39
- * These breakpoint name may be used as possible values for the data-hide
40
- * attribute. The data-hide attribute is optional and may be defined for each
41
- * th element in the table header.
54
+ * Initialisation of Responsive is performed by:
42
55
  *
43
- * The parameter, options, is an object of options supported by the responsive
44
- * helper. The following options are supported:
56
+ * * Adding the class `responsive` or `dt-responsive` to the table. In this case
57
+ * Responsive will automatically be initialised with the default configuration
58
+ * options when the DataTable is created.
59
+ * * Using the `responsive` option in the DataTables configuration options. This
60
+ * can also be used to specify the configuration options, or simply set to
61
+ * `true` to use the defaults.
45
62
  *
46
- * {
47
- * hideEmptyColumnsInRowDetail - Boolean, default: false.
48
- * clickOn - icon|cell|row, default: icon
49
- * showDetail - function called when detail row shown
50
- * hideDetail - function called when detail row hidden
51
- * }
63
+ * @class
64
+ * @param {object} settings DataTables settings object for the host table
65
+ * @param {object} [opts] Configuration options
66
+ * @requires jQuery 1.7+
67
+ * @requires DataTables 1.10.1+
52
68
  *
53
- * @param {Object|string} tableSelector jQuery wrapped set or selector for
54
- * datatables container element.
55
- * @param {Object} breakpoints Object defining the responsive
56
- * breakpoint for datatables.
57
- * @param {Object} options Object of options.
69
+ * @example
70
+ * $('#example').DataTable( {
71
+ * responsive: true
72
+ * } );
73
+ * } );
58
74
  */
59
- function ResponsiveDatatablesHelper(tableSelector, breakpoints, options) {
60
- if (typeof tableSelector === 'string') {
61
- this.tableElement = $(tableSelector);
62
- } else {
63
- this.tableElement = tableSelector;
64
- }
65
-
66
- // Get data table API.
67
- this.api = this.tableElement.dataTable().api();
68
-
69
- // State of column indexes and which are shown or hidden.
70
- this.columnIndexes = [];
71
- this.columnsShownIndexes = [];
72
- this.columnsHiddenIndexes = [];
73
- this.currentBreakpoint = '';
74
- this.lastBreakpoint = '';
75
- this.lastColumnsHiddenIndexes = [];
76
-
77
- // Save state
78
- var fileName = window.location.pathname.split("/").pop();
79
- var context = this.api.settings().context[0];
80
-
81
- this.tableId = context.sTableId;
82
- this.saveState = context.oInit.bStateSave;
83
- this.cookieName = 'DataTablesResponsiveHelper_' + this.tableId + (fileName ? '_' + fileName : '');
84
- this.lastStateExists = false;
85
-
86
- // Index of the th in the header tr that stores where the attribute
87
- // data-class="expand"
88
- // is defined.
89
- this.expandColumn = undefined;
90
- // Stores original breakpoint defitions
91
- this.origBreakpointsDefs = undefined;
92
- // Stores the break points defined in the table header.
93
- // Each th in the header tr may contain an optional attribute like
94
- // data-hide="phone,tablet"
95
- // These attributes and the breakpoints object will be used to create this
96
- // object.
97
- this.breakpoints = {
98
- /**
99
- * We will be generating data in the following format:
100
- * phone : {
101
- * lowerLimit : undefined,
102
- * upperLimit : 320,
103
- * columnsToHide: []
104
- * },
105
- * tablet: {
106
- * lowerLimit : 320,
107
- * upperLimit : 724,
108
- * columnsToHide: []
109
- * }
110
- */
111
- };
112
-
113
- // Store default options
114
- this.options = {
115
- hideEmptyColumnsInRowDetail: false,
116
- clickOn: 'icon',
117
- showDetail: null,
118
- hideDetail: null
119
- };
120
-
121
- // Expand icon template
122
- this.expandIconTemplate = '<span class="responsiveExpander"></span>';
123
-
124
- // Row template
125
- this.rowTemplate = '<tr class="row-detail"><td><ul><!--column item--></ul></td></tr>';
126
- this.rowLiTemplate = '<li><span class="columnTitle"><!--column title--></span>: <span class="columnValue"><!--column value--></span></li>';
127
-
128
- // Responsive behavior on/off flag
129
- this.disabled = true;
130
-
131
- // Skip next windows width change flag
132
- this.skipNextWindowsWidthChange = false;
133
-
134
- // Initialize settings
135
- this.init(breakpoints, options);
136
- }
137
-
138
- /**
139
- * Responsive datatables helper init function.
140
- * Builds breakpoint limits for columns and begins to listen to window resize
141
- * event.
142
- *
143
- * See constructor for the breakpoints parameter.
144
- *
145
- * @param {Object} breakpoints
146
- * @param {Object} options
147
- */
148
- ResponsiveDatatablesHelper.prototype.init = function (breakpoints, options) {
149
- this.origBreakpointsDefs = breakpoints;
150
- this.initBreakpoints();
151
-
152
- // Enable responsive behavior.
153
- this.disable(false);
154
-
155
- // Extend options
156
- $.extend(this.options, options);
75
+ var Responsive = function ( settings, opts ) {
76
+ // Sanity check that we are using DataTables 1.10 or newer
77
+ if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.1' ) ) {
78
+ throw 'DataTables Responsive requires DataTables 1.10.1 or newer';
79
+ }
80
+
81
+ this.s = {
82
+ dt: new DataTable.Api( settings ),
83
+ columns: []
84
+ };
85
+
86
+ // Check if responsive has already been initialised on this table
87
+ if ( this.s.dt.settings()[0].responsive ) {
88
+ return;
89
+ }
90
+
91
+ // details is an object, but for simplicity the user can give it as a string
92
+ if ( opts && typeof opts.details === 'string' ) {
93
+ opts.details = { type: opts.details };
94
+ }
95
+
96
+ this.c = $.extend( true, {}, Responsive.defaults, DataTable.defaults.responsive, opts );
97
+ settings.responsive = this;
98
+ this._constructor();
157
99
  };
158
100
 
159
- ResponsiveDatatablesHelper.prototype.initBreakpoints = function () {
160
- // Get last state if it exists
161
- if (this.saveState) {
162
- this.getState();
163
- }
164
-
165
- if (!this.lastStateExists) {
166
- /** Generate breakpoints in the format we need. ***********************/
167
- // First, we need to create a sorted array of the breakpoints given.
168
- var breakpointsSorted = [];
169
-
170
- for (var prop in this.origBreakpointsDefs) {
171
- breakpointsSorted.push({
172
- name: prop,
173
- upperLimit: this.origBreakpointsDefs[prop],
174
- columnsToHide: []
175
- });
176
- }
177
-
178
- breakpointsSorted.sort(function (a, b) {
179
- return a.upperLimit - b.upperLimit;
180
- });
181
-
182
- // Set lower and upper limits for each breakpoint.
183
- var lowerLimit = 0;
184
- for (var i = 0; i < breakpointsSorted.length; i++) {
185
- breakpointsSorted[i].lowerLimit = lowerLimit;
186
- lowerLimit = breakpointsSorted[i].upperLimit;
187
- }
188
-
189
- // Add the default breakpoint which shows all (has no upper limit).
190
- breakpointsSorted.push({
191
- name : 'always',
192
- lowerLimit : lowerLimit,
193
- upperLimit : Infinity,
194
- columnsToHide: []
195
- });
196
-
197
- // Copy the sorted breakpoint array into the breakpoints object using the
198
- // name as the key.
199
- this.breakpoints = {};
200
- var i, l;
201
- for (i = 0, l = breakpointsSorted.length; i < l; i++) {
202
- this.breakpoints[breakpointsSorted[i].name] = breakpointsSorted[i];
203
- }
204
-
205
- /** Create range of visible columns and their indexes *****************/
206
- // We need the range of all visible column indexes to calculate the
207
- // columns to show:
208
- // Columns to show = all visible columns - columns to hide
209
- var columns = this.api.columns().header();
210
- var visibleColumnsHeadersTds = [];
211
- for (i = 0, l = columns.length; i < l; i++) {
212
- if (this.api.column(i).visible()) {
213
- this.columnIndexes.push(i);
214
- visibleColumnsHeadersTds.push(columns[i]);
215
- }
216
- }
217
-
218
- /** Sort columns into breakpoints respectively ************************/
219
- // Read column headers' attributes and get needed info
220
- for (var index = 0; index < visibleColumnsHeadersTds.length; index++) {
221
- // Get the column with the attribute data-class="expand" so we know
222
- // where to display the expand icon.
223
- var col = $(visibleColumnsHeadersTds[index]);
224
-
225
- if (col.attr('data-class') === 'expand') {
226
- this.expandColumn = this.columnIndexes[index];
227
- }
228
-
229
- // The data-hide attribute has the breakpoints that this column
230
- // is associated with.
231
- // If it's defined, get the data-hide attribute and sort this
232
- // column into the appropriate breakpoint's columnsToHide array.
233
- var dataHide = col.attr('data-hide');
234
- if (dataHide !== undefined) {
235
- var splitBreakingPoints = dataHide.split(/,\s*/);
236
- for (var i = 0; i < splitBreakingPoints.length; i++) {
237
- var bp = splitBreakingPoints[i];
238
- if (bp === 'always') {
239
- // A column with an 'always' breakpoint is always hidden.
240
- // Loop through all breakpoints and add it to each except the
241
- // default breakpoint.
242
- for (var prop in this.breakpoints) {
243
- if (this.breakpoints[prop].name !== 'default') {
244
- this.breakpoints[prop].columnsToHide.push(this.columnIndexes[index]);
245
- }
246
- }
247
- } else if (this.breakpoints[bp] !== undefined) {
248
- // Translate visible column index to internal column index.
249
- this.breakpoints[bp].columnsToHide.push(this.columnIndexes[index]);
250
- }
251
- }
252
- }
253
- }
254
- }
101
+ Responsive.prototype = {
102
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
103
+ * Constructor
104
+ */
105
+
106
+ /**
107
+ * Initialise the Responsive instance
108
+ *
109
+ * @private
110
+ */
111
+ _constructor: function ()
112
+ {
113
+ var that = this;
114
+ var dt = this.s.dt;
115
+
116
+ dt.settings()[0]._responsive = this;
117
+
118
+ // Use DataTables' private throttle function to avoid processor thrashing
119
+ $(window).on( 'resize.dtr orientationchange.dtr', dt.settings()[0].oApi._fnThrottle( function () {
120
+ that._resize();
121
+ } ) );
122
+
123
+ // Destroy event handler
124
+ dt.on( 'destroy.dtr', function () {
125
+ $(window).off( 'resize.dtr orientationchange.dtr' );
126
+ } );
127
+
128
+ // Reorder the breakpoints array here in case they have been added out
129
+ // of order
130
+ this.c.breakpoints.sort( function (a, b) {
131
+ return a.width < b.width ? 1 :
132
+ a.width > b.width ? -1 : 0;
133
+ } );
134
+
135
+ // Determine which columns are already hidden, and should therefore
136
+ // remain hidden. TODO - should this be done? See thread 22677
137
+ //
138
+ // this.s.alwaysHidden = dt.columns(':hidden').indexes();
139
+
140
+ this._classLogic();
141
+ this._resizeAuto();
142
+
143
+ // First pass - draw the table for the current viewport size
144
+ this._resize();
145
+
146
+ // Details handler
147
+ var details = this.c.details;
148
+ if ( details.type ) {
149
+ that._detailsInit();
150
+ this._detailsVis();
151
+
152
+ dt.on( 'column-visibility.dtr', function () {
153
+ that._detailsVis();
154
+ } );
155
+
156
+ $(dt.table().node()).addClass( 'dtr-'+details.type );
157
+ }
158
+ },
159
+
160
+
161
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
162
+ * Private methods
163
+ */
164
+
165
+ /**
166
+ * Calculate the visibility for the columns in a table for a given
167
+ * breakpoint. The result is pre-determined based on the class logic if
168
+ * class names are used to control all columns, but the width of the table
169
+ * is also used if there are columns which are to be automatically shown
170
+ * and hidden.
171
+ *
172
+ * @param {string} breakpoint Breakpoint name to use for the calculation
173
+ * @return {array} Array of boolean values initiating the visibility of each
174
+ * column.
175
+ * @private
176
+ */
177
+ _columnsVisiblity: function ( breakpoint )
178
+ {
179
+ var dt = this.s.dt;
180
+ var columns = this.s.columns;
181
+ var i, ien;
182
+
183
+ // Class logic - determine which columns are in this breakpoint based
184
+ // on the classes. If no class control (i.e. `auto`) then `-` is used
185
+ // to indicate this to the rest of the function
186
+ var display = $.map( columns, function ( col ) {
187
+ return col.auto && col.minWidth === null ?
188
+ false :
189
+ col.auto === true ?
190
+ '-' :
191
+ $.inArray( breakpoint, col.includeIn ) !== -1;
192
+ } );
193
+
194
+ // Auto column control - first pass: how much width is taken by the
195
+ // ones that must be included from the non-auto columns
196
+ var requiredWidth = 0;
197
+ for ( i=0, ien=display.length ; i<ien ; i++ ) {
198
+ if ( display[i] === true ) {
199
+ requiredWidth += columns[i].minWidth;
200
+ }
201
+ }
202
+
203
+ // Second pass, use up any remaining width for other columns
204
+ var widthAvailable = dt.table().container().offsetWidth;
205
+ var usedWidth = widthAvailable - requiredWidth;
206
+
207
+ for ( i=0, ien=display.length ; i<ien ; i++ ) {
208
+ // Control column needs to always be included. This makes it sub-
209
+ // optimal in terms of using the available with, but to stop layout
210
+ // thrashing or overflow
211
+ if ( columns[i].control ) {
212
+ usedWidth -= columns[i].minWidth;
213
+ }
214
+ else if ( display[i] === '-' ) {
215
+ // Otherwise, remove the width
216
+ display[i] = usedWidth - columns[i].minWidth < 0 ?
217
+ false :
218
+ true;
219
+
220
+ // Continue counting down the width, so a smaller column to the
221
+ // left won't be shown
222
+ usedWidth -= columns[i].minWidth;
223
+ }
224
+ }
225
+
226
+ // Determine if the 'control' column should be shown (if there is one).
227
+ // This is the case when there is a hidden column (that is not the
228
+ // control column). The two loops look inefficient here, but they are
229
+ // trivial and will fly through. We need to know the outcome from the
230
+ // first , before the action in the second can be taken
231
+ var showControl = false;
232
+
233
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
234
+ if ( ! columns[i].control && ! columns[i].never && ! display[i] ) {
235
+ showControl = true;
236
+ break;
237
+ }
238
+ }
239
+
240
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
241
+ if ( columns[i].control ) {
242
+ display[i] = showControl;
243
+ }
244
+ }
245
+
246
+ // Finally we need to make sure that there is at least one column that
247
+ // is visible
248
+ if ( $.inArray( true, display ) === -1 ) {
249
+ display[0] = true;
250
+ }
251
+
252
+ return display;
253
+ },
254
+
255
+
256
+ /**
257
+ * Create the internal `columns` array with information about the columns
258
+ * for the table. This includes determining which breakpoints the column
259
+ * will appear in, based upon class names in the column, which makes up the
260
+ * vast majority of this method.
261
+ *
262
+ * @private
263
+ */
264
+ _classLogic: function ()
265
+ {
266
+ var that = this;
267
+ var calc = {};
268
+ var breakpoints = this.c.breakpoints;
269
+ var columns = this.s.dt.columns().eq(0).map( function (i) {
270
+ var className = this.column(i).header().className;
271
+
272
+ return {
273
+ className: className,
274
+ includeIn: [],
275
+ auto: false,
276
+ control: false,
277
+ never: className.match(/\bnever\b/) ? true : false
278
+ };
279
+ } );
280
+
281
+ // Simply add a breakpoint to `includeIn` array, ensuring that there are
282
+ // no duplicates
283
+ var add = function ( colIdx, name ) {
284
+ var includeIn = columns[ colIdx ].includeIn;
285
+
286
+ if ( $.inArray( name, includeIn ) === -1 ) {
287
+ includeIn.push( name );
288
+ }
289
+ };
290
+
291
+ var column = function ( colIdx, name, operator, matched ) {
292
+ var size, i, ien;
293
+
294
+ if ( ! operator ) {
295
+ columns[ colIdx ].includeIn.push( name );
296
+ }
297
+ else if ( operator === 'max-' ) {
298
+ // Add this breakpoint and all smaller
299
+ size = that._find( name ).width;
300
+
301
+ for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
302
+ if ( breakpoints[i].width <= size ) {
303
+ add( colIdx, breakpoints[i].name );
304
+ }
305
+ }
306
+ }
307
+ else if ( operator === 'min-' ) {
308
+ // Add this breakpoint and all larger
309
+ size = that._find( name ).width;
310
+
311
+ for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
312
+ if ( breakpoints[i].width >= size ) {
313
+ add( colIdx, breakpoints[i].name );
314
+ }
315
+ }
316
+ }
317
+ else if ( operator === 'not-' ) {
318
+ // Add all but this breakpoint (xxx need extra information)
319
+
320
+ for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
321
+ if ( breakpoints[i].name.indexOf( matched ) === -1 ) {
322
+ add( colIdx, breakpoints[i].name );
323
+ }
324
+ }
325
+ }
326
+ };
327
+
328
+ // Loop over each column and determine if it has a responsive control
329
+ // class
330
+ columns.each( function ( col, i ) {
331
+ var classNames = col.className.split(' ');
332
+ var hasClass = false;
333
+
334
+ // Split the class name up so multiple rules can be applied if needed
335
+ for ( var k=0, ken=classNames.length ; k<ken ; k++ ) {
336
+ var className = $.trim( classNames[k] );
337
+
338
+ if ( className === 'all' ) {
339
+ // Include in all
340
+ hasClass = true;
341
+ col.includeIn = $.map( breakpoints, function (a) {
342
+ return a.name;
343
+ } );
344
+ return;
345
+ }
346
+ else if ( className === 'none' || className === 'never' ) {
347
+ // Include in none (default) and no auto
348
+ hasClass = true;
349
+ return;
350
+ }
351
+ else if ( className === 'control' ) {
352
+ // Special column that is only visible, when one of the other
353
+ // columns is hidden. This is used for the details control
354
+ hasClass = true;
355
+ col.control = true;
356
+ return;
357
+ }
358
+
359
+ $.each( breakpoints, function ( j, breakpoint ) {
360
+ // Does this column have a class that matches this breakpoint?
361
+ var brokenPoint = breakpoint.name.split('-');
362
+ var re = new RegExp( '(min\\-|max\\-|not\\-)?('+brokenPoint[0]+')(\\-[_a-zA-Z0-9])?' );
363
+ var match = className.match( re );
364
+
365
+ if ( match ) {
366
+ hasClass = true;
367
+
368
+ if ( match[2] === brokenPoint[0] && match[3] === '-'+brokenPoint[1] ) {
369
+ // Class name matches breakpoint name fully
370
+ column( i, breakpoint.name, match[1], match[2]+match[3] );
371
+ }
372
+ else if ( match[2] === brokenPoint[0] && ! match[3] ) {
373
+ // Class name matched primary breakpoint name with no qualifier
374
+ column( i, breakpoint.name, match[1], match[2] );
375
+ }
376
+ }
377
+ } );
378
+ }
379
+
380
+ // If there was no control class, then automatic sizing is used
381
+ if ( ! hasClass ) {
382
+ col.auto = true;
383
+ }
384
+ } );
385
+
386
+ this.s.columns = columns;
387
+ },
388
+
389
+
390
+ /**
391
+ * Initialisation for the details handler
392
+ *
393
+ * @private
394
+ */
395
+ _detailsInit: function ()
396
+ {
397
+ var that = this;
398
+ var dt = this.s.dt;
399
+ var details = this.c.details;
400
+
401
+ // The inline type always uses the first child as the target
402
+ if ( details.type === 'inline' ) {
403
+ details.target = 'td:first-child';
404
+ }
405
+
406
+ // type.target can be a string jQuery selector or a column index
407
+ var target = details.target;
408
+ var selector = typeof target === 'string' ? target : 'td';
409
+
410
+ // Click handler to show / hide the details rows when they are available
411
+ $( dt.table().body() ).on( 'click', selector, function (e) {
412
+ // If the table is not collapsed (i.e. there is no hidden columns)
413
+ // then take no action
414
+ if ( ! $(dt.table().node()).hasClass('collapsed' ) ) {
415
+ return;
416
+ }
417
+
418
+ // Check that the row is actually a DataTable's controlled node
419
+ if ( ! dt.row( $(this).closest('tr') ).length ) {
420
+ return;
421
+ }
422
+
423
+ // For column index, we determine if we should act or not in the
424
+ // handler - otherwise it is already okay
425
+ if ( typeof target === 'number' ) {
426
+ var targetIdx = target < 0 ?
427
+ dt.columns().eq(0).length + target :
428
+ target;
429
+
430
+ if ( dt.cell( this ).index().column !== targetIdx ) {
431
+ return;
432
+ }
433
+ }
434
+
435
+ // $().closest() includes itself in its check
436
+ var row = dt.row( $(this).closest('tr') );
437
+
438
+ if ( row.child.isShown() ) {
439
+ row.child( false );
440
+ $( row.node() ).removeClass( 'parent' );
441
+ }
442
+ else {
443
+ var info = that.c.details.renderer( dt, row[0] );
444
+ row.child( info, 'child' ).show();
445
+ $( row.node() ).addClass( 'parent' );
446
+ }
447
+ } );
448
+ },
449
+
450
+
451
+ /**
452
+ * Update the child rows in the table whenever the column visibility changes
453
+ *
454
+ * @private
455
+ */
456
+ _detailsVis: function ()
457
+ {
458
+ var that = this;
459
+ var dt = this.s.dt;
460
+
461
+ // Find how many columns are hidden
462
+ var hiddenColumns = dt.columns().indexes().filter( function ( idx ) {
463
+ var col = dt.column( idx );
464
+
465
+ if ( col.visible() ) {
466
+ return null;
467
+ }
468
+
469
+ // Only counts as hidden if it doesn't have the `never` class
470
+ return $( col.header() ).hasClass( 'never' ) ? null : idx;
471
+ } );
472
+ var haveHidden = true;
473
+
474
+ if ( hiddenColumns.length === 0 || ( hiddenColumns.length === 1 && this.s.columns[ hiddenColumns[0] ].control ) ) {
475
+ haveHidden = false;
476
+ }
477
+
478
+ if ( haveHidden ) {
479
+ // Got hidden columns
480
+ $( dt.table().node() ).addClass('collapsed');
481
+
482
+ // Show all existing child rows
483
+ dt.rows().eq(0).each( function (idx) {
484
+ var row = dt.row( idx );
485
+
486
+ if ( row.child() ) {
487
+ var info = that.c.details.renderer( dt, row[0] );
488
+
489
+ // The renderer can return false to have no child row
490
+ if ( info === false ) {
491
+ row.child.hide();
492
+ }
493
+ else {
494
+ row.child( info, 'child' ).show();
495
+ }
496
+ }
497
+ } );
498
+ }
499
+ else {
500
+ // No hidden columns
501
+ $( dt.table().node() ).removeClass('collapsed');
502
+
503
+ // Hide all existing child rows
504
+ dt.rows().eq(0).each( function (idx) {
505
+ dt.row( idx ).child.hide();
506
+ } );
507
+ }
508
+ },
509
+
510
+
511
+ /**
512
+ * Find a breakpoint object from a name
513
+ * @param {string} name Breakpoint name to find
514
+ * @return {object} Breakpoint description object
515
+ */
516
+ _find: function ( name )
517
+ {
518
+ var breakpoints = this.c.breakpoints;
519
+
520
+ for ( var i=0, ien=breakpoints.length ; i<ien ; i++ ) {
521
+ if ( breakpoints[i].name === name ) {
522
+ return breakpoints[i];
523
+ }
524
+ }
525
+ },
526
+
527
+
528
+ /**
529
+ * Alter the table display for a resized viewport. This involves first
530
+ * determining what breakpoint the window currently is in, getting the
531
+ * column visibilities to apply and then setting them.
532
+ *
533
+ * @private
534
+ */
535
+ _resize: function ()
536
+ {
537
+ var dt = this.s.dt;
538
+ var width = $(window).width();
539
+ var breakpoints = this.c.breakpoints;
540
+ var breakpoint = breakpoints[0].name;
541
+
542
+ // Determine what breakpoint we are currently at
543
+ for ( var i=breakpoints.length-1 ; i>=0 ; i-- ) {
544
+ if ( width <= breakpoints[i].width ) {
545
+ breakpoint = breakpoints[i].name;
546
+ break;
547
+ }
548
+ }
549
+
550
+ // Show the columns for that break point
551
+ var columns = this._columnsVisiblity( breakpoint );
552
+
553
+ dt.columns().eq(0).each( function ( colIdx, i ) {
554
+ dt.column( colIdx ).visible( columns[i] );
555
+ } );
556
+ },
557
+
558
+
559
+ /**
560
+ * Determine the width of each column in the table so the auto column hiding
561
+ * has that information to work with. This method is never going to be 100%
562
+ * perfect since column widths can change slightly per page, but without
563
+ * seriously compromising performance this is quite effective.
564
+ *
565
+ * @private
566
+ */
567
+ _resizeAuto: function ()
568
+ {
569
+ var dt = this.s.dt;
570
+ var columns = this.s.columns;
571
+
572
+ // Are we allowed to do auto sizing?
573
+ if ( ! this.c.auto ) {
574
+ return;
575
+ }
576
+
577
+ // Are there any columns that actually need auto-sizing, or do they all
578
+ // have classes defined
579
+ if ( $.inArray( true, $.map( columns, function (c) { return c.auto; } ) ) === -1 ) {
580
+ return;
581
+ }
582
+
583
+ // Clone the table with the current data in it
584
+ var tableWidth = dt.table().node().offsetWidth;
585
+ var columnWidths = dt.columns;
586
+ var clonedTable = dt.table().node().cloneNode( false );
587
+ var clonedHeader = $( dt.table().header().cloneNode( false ) ).appendTo( clonedTable );
588
+ var clonedBody = $( dt.table().body().cloneNode( false ) ).appendTo( clonedTable );
589
+
590
+ // This is a bit slow, but we need to get a clone of each row that
591
+ // includes all columns. As such, try to do this as little as possible.
592
+ dt.rows( { page: 'current' } ).indexes().flatten().each( function ( idx ) {
593
+ var clone = dt.row( idx ).node().cloneNode( true );
594
+
595
+ if ( dt.columns( ':hidden' ).flatten().length ) {
596
+ $(clone).append( dt.cells( idx, ':hidden' ).nodes().to$().clone() );
597
+ }
598
+
599
+ $(clone).appendTo( clonedBody );
600
+ } );
601
+
602
+ var cells = dt.columns().header().to$().clone( false ).wrapAll('tr').appendTo( clonedHeader );
603
+ var inserted = $('<div/>')
604
+ .css( {
605
+ width: 1,
606
+ height: 1,
607
+ overflow: 'hidden'
608
+ } )
609
+ .append( clonedTable )
610
+ .insertBefore( dt.table().node() );
611
+
612
+ // The cloned header now contains the smallest that each column can be
613
+ dt.columns().eq(0).each( function ( idx ) {
614
+ columns[idx].minWidth = cells[ idx ].offsetWidth || 0;
615
+ } );
616
+
617
+ inserted.remove();
618
+ }
255
619
  };
256
620
 
621
+
257
622
  /**
258
- * Sets or removes window resize handler.
623
+ * List of default breakpoints. Each item in the array is an object with two
624
+ * properties:
625
+ *
626
+ * * `name` - the breakpoint name.
627
+ * * `width` - the breakpoint width
259
628
  *
260
- * @param {Boolean} bindFlag
629
+ * @name Responsive.breakpoints
630
+ * @static
261
631
  */
262
- ResponsiveDatatablesHelper.prototype.setWindowsResizeHandler = function(bindFlag) {
263
- if (bindFlag === undefined) {
264
- bindFlag = true;
265
- }
266
-
267
- if (bindFlag) {
268
- var that = this;
269
- $(window).bind("resize", function () {
270
- that.respond();
271
- });
272
- } else {
273
- $(window).unbind("resize");
274
- }
275
- };
632
+ Responsive.breakpoints = [
633
+ { name: 'desktop', width: Infinity },
634
+ { name: 'tablet-l', width: 1024 },
635
+ { name: 'tablet-p', width: 768 },
636
+ { name: 'mobile-l', width: 480 },
637
+ { name: 'mobile-p', width: 320 }
638
+ ];
276
639
 
277
- /**
278
- * Respond window size change. This helps make datatables responsive.
279
- */
280
- ResponsiveDatatablesHelper.prototype.respond = function () {
281
- if (this.disabled) {
282
- return;
283
- }
284
- var that = this;
285
-
286
- // Get new windows width
287
- var newWindowWidth = $(window).width();
288
-
289
- // Loop through breakpoints to see which columns need to be shown/hidden.
290
- var newColumnsToHide = [];
291
-
292
- for (var prop in this.breakpoints) {
293
- var element = this.breakpoints[prop];
294
- if ((!element.lowerLimit || newWindowWidth > element.lowerLimit) && (!element.upperLimit || newWindowWidth <= element.upperLimit)) {
295
- this.currentBreakpoint = element.name;
296
- newColumnsToHide = element.columnsToHide;
297
- }
298
- }
299
-
300
- // Find out if a column show/hide should happen.
301
- // Skip column show/hide if this window width change follows immediately
302
- // after a previous column show/hide. This will help prevent a loop.
303
- var columnShowHide = false;
304
- if (!this.skipNextWindowsWidthChange) {
305
- // Check difference in length
306
- if (this.lastBreakpoint.length === 0 && newColumnsToHide.length) {
307
- // No previous breakpoint and new breakpoint
308
- columnShowHide = true;
309
- } else if (this.lastBreakpoint != this.currentBreakpoint) {
310
- // Different breakpoints
311
- columnShowHide = true;
312
- } else if (this.columnsHiddenIndexes.length !== newColumnsToHide.length) {
313
- // Difference in number of hidden columns
314
- columnShowHide = true;
315
- } else {
316
- // Possible same number of columns but check for difference in columns
317
- var d1 = this.difference(this.columnsHiddenIndexes, newColumnsToHide).length;
318
- var d2 = this.difference(newColumnsToHide, this.columnsHiddenIndexes).length;
319
- columnShowHide = d1 + d2 > 0;
320
- }
321
- }
322
-
323
- if (columnShowHide) {
324
- // Showing/hiding a column at breakpoint may cause a windows width
325
- // change. Let's flag to skip the column show/hide that may be
326
- // caused by the next windows width change.
327
- this.skipNextWindowsWidthChange = true;
328
- this.columnsHiddenIndexes = newColumnsToHide;
329
- this.columnsShownIndexes = this.difference(this.columnIndexes, this.columnsHiddenIndexes);
330
- this.showHideColumns();
331
- this.lastBreakpoint = this.currentBreakpoint;
332
- this.setState();
333
- this.skipNextWindowsWidthChange = false;
334
- }
335
-
336
-
337
- // We don't skip this part.
338
- // If one or more columns have been hidden, add the has-columns-hidden class to table.
339
- // This class will show what state the table is in.
340
- if (this.columnsHiddenIndexes.length) {
341
- this.tableElement.addClass('has-columns-hidden');
342
-
343
- // Show details for each row that is tagged with the class .detail-show.
344
- $('tr.detail-show', this.tableElement).each(function (index, element) {
345
- var tr = $(element);
346
- if (tr.next('.row-detail').length === 0) {
347
- ResponsiveDatatablesHelper.prototype.showRowDetail(that, tr);
348
- }
349
- });
350
- } else {
351
- this.tableElement.removeClass('has-columns-hidden');
352
- $('tr.row-detail').each(function (event) {
353
- ResponsiveDatatablesHelper.prototype.hideRowDetail(that, $(this).prev());
354
- });
355
- }
356
- };
357
640
 
358
641
  /**
359
- * Show/hide datatables columns.
360
- */
361
- ResponsiveDatatablesHelper.prototype.showHideColumns = function () {
362
- // Calculate the columns to show
363
- // Show columns that may have been previously hidden.
364
- for (var i = 0, l = this.columnsShownIndexes.length; i < l; i++) {
365
- this.api.column(this.columnsShownIndexes[i]).visible(true);
366
- }
367
-
368
- // Hide columns that may have been previously shown.
369
- for (var i = 0, l = this.columnsHiddenIndexes.length; i < l; i++) {
370
- this.api.column(this.columnsHiddenIndexes[i]).visible(false);
371
- }
372
-
373
- // Rebuild details to reflect shown/hidden column changes.
374
- var that = this;
375
- $('tr.row-detail').each(function () {
376
- ResponsiveDatatablesHelper.prototype.hideRowDetail(that, $(this).prev());
377
- });
378
- if (this.tableElement.hasClass('has-columns-hidden')) {
379
- $('tr.detail-show', this.tableElement).each(function (index, element) {
380
- ResponsiveDatatablesHelper.prototype.showRowDetail(that, $(element));
381
- });
382
- }
383
- };
384
-
385
- /**
386
- * Create the expand icon on the column with the data-class="expand" attribute
387
- * defined for it's header.
642
+ * Responsive default settings for initialisation
388
643
  *
389
- * @param {Object} tr table row object
644
+ * @namespace
645
+ * @name Responsive.defaults
646
+ * @static
390
647
  */
391
- ResponsiveDatatablesHelper.prototype.createExpandIcon = function (tr) {
392
- if (this.disabled) {
393
- return;
394
- }
395
-
396
- // Get the td for tr with the same index as the th in the header tr
397
- // that has the data-class="expand" attribute defined.
398
- var tds = $('td', tr);
399
- // Loop through tds and create an expand icon on the td that has a column
400
- // index equal to the expand column given.
401
- for (var i = 0, l = tds.length; i < l; i++) {
402
- var td = tds[i];
403
- var tdIndex = this.api.cell(td).index().column;
404
- td = $(td);
405
- if (tdIndex === this.expandColumn) {
406
- // Create expand icon if there isn't one already.
407
- if ($('span.responsiveExpander', td).length == 0) {
408
- td.prepend(this.expandIconTemplate);
409
-
410
- // Respond to click event on expander icon.
411
- switch (this.options.clickOn) {
412
- case 'cell':
413
- td.on('click', {responsiveDatatablesHelperInstance: this}, this.showRowDetailEventHandler);
414
- break;
415
- case 'row':
416
- $(tr).on('click', {responsiveDatatablesHelperInstance: this}, this.showRowDetailEventHandler);
417
- break;
418
- default:
419
- td.on('click', 'span.responsiveExpander', {responsiveDatatablesHelperInstance: this}, this.showRowDetailEventHandler);
420
- break;
421
- }
422
- }
423
- break;
424
- }
425
- }
648
+ Responsive.defaults = {
649
+ /**
650
+ * List of breakpoints for the instance. Note that this means that each
651
+ * instance can have its own breakpoints. Additionally, the breakpoints
652
+ * cannot be changed once an instance has been creased.
653
+ *
654
+ * @type {Array}
655
+ * @default Takes the value of `Responsive.breakpoints`
656
+ */
657
+ breakpoints: Responsive.breakpoints,
658
+
659
+ /**
660
+ * Enable / disable auto hiding calculations. It can help to increase
661
+ * performance slightly if you disable this option, but all columns would
662
+ * need to have breakpoint classes assigned to them
663
+ *
664
+ * @type {Boolean}
665
+ * @default `true`
666
+ */
667
+ auto: true,
668
+
669
+ /**
670
+ * Details control. If given as a string value, the `type` property of the
671
+ * default object is set to that value, and the defaults used for the rest
672
+ * of the object - this is for ease of implementation.
673
+ *
674
+ * The object consists of the following properties:
675
+ *
676
+ * * `renderer` - function that is called for display of the child row data.
677
+ * The default function will show the data from the hidden columns
678
+ * * `target` - Used as the selector for what objects to attach the child
679
+ * open / close to
680
+ * * `type` - `false` to disable the details display, `inline` or `column`
681
+ * for the two control types
682
+ *
683
+ * @type {Object|string}
684
+ */
685
+ details: {
686
+ renderer: function ( api, rowIdx ) {
687
+ var data = api.cells( rowIdx, ':hidden' ).eq(0).map( function ( cell ) {
688
+ var header = $( api.column( cell.column ).header() );
689
+ var idx = api.cell( cell ).index();
690
+
691
+ if ( header.hasClass( 'control' ) || header.hasClass( 'never' ) ) {
692
+ return '';
693
+ }
694
+
695
+ // Use a non-public DT API method to render the data for display
696
+ // This needs to be updated when DT adds a suitable method for
697
+ // this type of data retrieval
698
+ var dtPrivate = api.settings()[0];
699
+ var cellData = dtPrivate.oApi._fnGetCellData(
700
+ dtPrivate, idx.row, idx.column, 'display'
701
+ );
702
+
703
+ return '<li data-dtr-index="'+idx.column+'">'+
704
+ '<span class="dtr-title">'+
705
+ header.text()+':'+
706
+ '</span> '+
707
+ '<span class="dtr-data">'+
708
+ cellData+
709
+ '</span>'+
710
+ '</li>';
711
+ } ).toArray().join('');
712
+
713
+ return data ?
714
+ $('<ul data-dtr-index="'+rowIdx+'"/>').append( data ) :
715
+ false;
716
+ },
717
+
718
+ target: 0,
719
+
720
+ type: 'inline'
721
+ }
426
722
  };
427
723
 
428
- /**
429
- * Show row detail event handler.
430
- *
431
- * This handler is used to handle the click event of the expand icon defined in
432
- * the table row data element.
433
- *
434
- * @param {Object} event jQuery event object
435
- */
436
- ResponsiveDatatablesHelper.prototype.showRowDetailEventHandler = function (event) {
437
- var responsiveDatatablesHelperInstance = event.data.responsiveDatatablesHelperInstance;
438
- if (responsiveDatatablesHelperInstance.disabled) {
439
- return;
440
- }
441
724
 
442
- var td = $(this);
725
+ /*
726
+ * API
727
+ */
728
+ var Api = $.fn.dataTable.Api;
443
729
 
444
- // Nothing to do if there are no columns hidden.
445
- if (!td.closest('table').hasClass('has-columns-hidden')) {
446
- return;
447
- }
730
+ // Doesn't do anything - work around for a bug in DT... Not documented
731
+ Api.register( 'responsive()', function () {
732
+ return this;
733
+ } );
448
734
 
449
- // Get the parent tr of which this td belongs to.
450
- var tr = td.closest('tr');
735
+ Api.register( 'responsive.recalc()', function () {
736
+ this.iterator( 'table', function ( ctx ) {
737
+ if ( ctx._responsive ) {
738
+ ctx._responsive._resizeAuto();
739
+ ctx._responsive._resize();
740
+ }
741
+ } );
742
+ } );
451
743
 
452
- // Show/hide row details
453
- if (tr.hasClass('detail-show')) {
454
- ResponsiveDatatablesHelper.prototype.hideRowDetail(responsiveDatatablesHelperInstance, tr);
455
- } else {
456
- ResponsiveDatatablesHelper.prototype.showRowDetail(responsiveDatatablesHelperInstance, tr);
457
- }
744
+ Api.register( 'responsive.index()', function ( li ) {
745
+ li = $(li);
458
746
 
459
- tr.toggleClass('detail-show');
747
+ return {
748
+ column: li.data('dtr-index'),
749
+ row: li.parent().data('dtr-index')
750
+ };
751
+ } );
460
752
 
461
- // Prevent click event from bubbling up to higher-level DOM elements.
462
- event.stopPropagation();
463
- };
464
753
 
465
754
  /**
466
- * Show row details.
755
+ * Version information
467
756
  *
468
- * @param {ResponsiveDatatablesHelper} responsiveDatatablesHelperInstance instance of ResponsiveDatatablesHelper
469
- * @param {Object} tr jQuery wrapped set
757
+ * @name Responsive.version
758
+ * @static
470
759
  */
471
- ResponsiveDatatablesHelper.prototype.showRowDetail = function (responsiveDatatablesHelperInstance, tr) {
472
- // Get column because we need their titles.
473
- var api = responsiveDatatablesHelperInstance.api;
474
- var columns = api.columns().header();
475
-
476
- // Create the new tr.
477
- var newTr = $(responsiveDatatablesHelperInstance.rowTemplate);
478
-
479
- // Get the ul that we'll insert li's into.
480
- var ul = $('ul', newTr);
481
-
482
- // Loop through hidden columns and create an li for each of them.
483
- for (var i = 0; i < responsiveDatatablesHelperInstance.columnsHiddenIndexes.length; i++) {
484
- var index = responsiveDatatablesHelperInstance.columnsHiddenIndexes[i];
485
-
486
- // Get row td
487
- var rowIndex = api.row(tr).index();
488
- var td = api.cell(rowIndex, index).node();
489
-
490
- // Don't create li if contents are empty (depends on hideEmptyColumnsInRowDetail option).
491
- if (!responsiveDatatablesHelperInstance.options.hideEmptyColumnsInRowDetail || td.innerHTML.trim().length) {
492
- var li = $(responsiveDatatablesHelperInstance.rowLiTemplate);
493
- var hiddenColumnName = $(columns[index]).attr('data-name');
494
- $('.columnTitle', li).html(hiddenColumnName !== undefined ? hiddenColumnName : columns[index].innerHTML);
495
- var contents = $(td).contents();
496
- var clonedContents = contents.clone();
497
-
498
- // Select elements' selectedIndex are not cloned. Do it manually.
499
- for (var n = 0, m = contents.length; n < m; n++) {
500
- var node = contents[n];
501
- if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'SELECT') {
502
- clonedContents[n].selectedIndex = node.selectedIndex
503
- }
504
- }
505
-
506
- // Set the column contents and save the original td source.
507
- $('.columnValue', li).append(clonedContents).data('originalTdSource', td);
508
-
509
- // Copy index to data attribute, so we'll know where to put the value when the tr.row-detail is removed.
510
- li.attr('data-column', index);
511
-
512
- // Copy td class to new li.
513
- var tdClass = $(td).attr('class');
514
- if (tdClass !== 'undefined' && tdClass !== false && tdClass !== '') {
515
- li.addClass(tdClass)
516
- }
517
-
518
- ul.append(li);
519
- }
520
- }
521
-
522
- // Create tr colspan attribute.
523
- var colspan = responsiveDatatablesHelperInstance.columnIndexes.length - responsiveDatatablesHelperInstance.columnsHiddenIndexes.length;
524
- newTr.find('> td').attr('colspan', colspan);
525
-
526
- // Append the new tr after the current tr.
527
- tr.after(newTr);
528
-
529
- // call the showDetail function if needbe
530
- if (responsiveDatatablesHelperInstance.options.showDetail){
531
- responsiveDatatablesHelperInstance.options.showDetail(newTr);
532
- }
533
- };
760
+ Responsive.version = '1.0.2';
534
761
 
535
- /**
536
- * Hide row details.
537
- *
538
- * @param {ResponsiveDatatablesHelper} responsiveDatatablesHelperInstance instance of ResponsiveDatatablesHelper
539
- * @param {Object} tr jQuery wrapped set
540
- */
541
- ResponsiveDatatablesHelper.prototype.hideRowDetail = function (responsiveDatatablesHelperInstance, tr) {
542
- // If the value of an input has changed while in row detail, we need to copy its state back
543
- // to the DataTables object so that value will persist when the tr.row-detail is removed.
544
- var rowDetail = tr.next('.row-detail');
545
- if (responsiveDatatablesHelperInstance.options.hideDetail){
546
- responsiveDatatablesHelperInstance.options.hideDetail(rowDetail);
547
- }
548
- rowDetail.find('li').each(function () {
549
- var columnValueContainer = $(this).find('span.columnValue');
550
- var tdContents = columnValueContainer.contents();
551
- var td = columnValueContainer.data('originalTdSource');
552
- $(td).empty().append(tdContents);
553
- });
554
- rowDetail.remove();
555
- };
556
-
557
- /**
558
- * Enable/disable responsive behavior and restores changes made.
559
- *
560
- * @param {Boolean} disable, default is true
561
- */
562
- ResponsiveDatatablesHelper.prototype.disable = function (disable) {
563
- this.disabled = (disable === undefined) || disable;
564
762
 
565
- if (this.disabled) {
566
- // Remove windows resize handler.
567
- this.setWindowsResizeHandler(false);
763
+ $.fn.dataTable.Responsive = Responsive;
764
+ $.fn.DataTable.Responsive = Responsive;
568
765
 
569
- // Remove all trs that have row details.
570
- $('tbody tr.row-detail', this.tableElement).remove();
766
+ // Attach a listener to the document which listens for DataTables initialisation
767
+ // events so we can automatically initialise
768
+ $(document).on( 'init.dt.dtr', function (e, settings, json) {
769
+ if ( $(settings.nTable).hasClass( 'responsive' ) ||
770
+ $(settings.nTable).hasClass( 'dt-responsive' ) ||
771
+ settings.oInit.responsive ||
772
+ DataTable.defaults.responsive
773
+ ) {
774
+ var init = settings.oInit.responsive;
571
775
 
572
- // Remove all trs that are marked to have row details shown.
573
- $('tbody tr', this.tableElement).removeClass('detail-show');
776
+ if ( init !== false ) {
777
+ new Responsive( settings, $.isPlainObject( init ) ? init : {} );
778
+ }
779
+ }
780
+ } );
574
781
 
575
- // Remove all expander icons.
576
- $('tbody tr span.responsiveExpander', this.tableElement).remove();
782
+ return Responsive;
783
+ }; // /factory
577
784
 
578
- this.columnsHiddenIndexes = [];
579
- this.columnsShownIndexes = this.columnIndexes;
580
- this.showHideColumns();
581
- this.tableElement.removeClass('has-columns-hidden');
582
785
 
583
- this.tableElement.off('click', 'span.responsiveExpander', this.showRowDetailEventHandler);
584
- } else {
585
- // Add windows resize handler.
586
- this.setWindowsResizeHandler();
587
- }
588
- };
786
+ // Define as an AMD module if possible
787
+ if ( typeof define === 'function' && define.amd ) {
788
+ define( ['jquery', 'datatables'], factory );
789
+ }
790
+ else if ( typeof exports === 'object' ) {
791
+ // Node/CommonJS
792
+ factory( require('jquery'), require('datatables') );
793
+ }
794
+ else if ( jQuery && !jQuery.fn.dataTable.Responsive ) {
795
+ // Otherwise simply initialise as normal, stopping multiple evaluation
796
+ factory( jQuery, jQuery.fn.dataTable );
797
+ }
589
798
 
590
- /**
591
- * Get state from cookie.
592
- */
593
- ResponsiveDatatablesHelper.prototype.getState = function () {
594
- if (typeof(Storage)) {
595
- // Use local storage
596
- var value = JSON.parse(localStorage.getItem(this.cookieName));
597
- if (value) {
598
- this.columnIndexes = value.columnIndexes;
599
- this.breakpoints = value.breakpoints;
600
- this.expandColumn = value.expandColumn;
601
- this.lastBreakpoint = value.lastBreakpoint;
602
- this.lastStateExists = true;
603
- }
604
- } else {
605
- // No local storage.
606
- }
607
- };
608
799
 
609
- /**
610
- * Saves state to cookie.
611
- */
612
- ResponsiveDatatablesHelper.prototype.setState = function () {
613
- if (typeof(Storage)) {
614
- // Use local storage
615
- var d1 = this.difference(this.lastColumnsHiddenIndexes, this.columnsHiddenIndexes).length;
616
- var d2 = this.difference(this.columnsHiddenIndexes, this.lastColumnsHiddenIndexes).length;
617
-
618
- if (d1 + d2 > 0) {
619
- var tt;
620
- var value = {
621
- columnIndexes: this.columnIndexes, // array
622
- columnsHiddenIndexes: this.columnsHiddenIndexes, // array
623
- breakpoints: this.breakpoints, // object
624
- expandColumn: this.expandColumn, // int|undefined
625
- lastBreakpoint: this.lastBreakpoint // string
626
- };
627
-
628
- localStorage.setItem(this.cookieName, JSON.stringify(value));
629
- this.lastColumnsHiddenIndexes = this.columnsHiddenIndexes.slice(0);
630
- }
631
- } else {
632
- // No local storage.
633
- }
634
- };
800
+ })(window, document);
635
801
 
636
- /**
637
- * Get Difference.
638
- */
639
- ResponsiveDatatablesHelper.prototype.difference = function (a, b) {
640
- var arr = [], i, hash = {};
641
- for (i = b.length - 1; i >= 0; i--) {
642
- hash[b[i]] = true;
643
- }
644
- for (i = a.length - 1; i >= 0; i--) {
645
- if (hash[a[i]] !== true) {
646
- arr.push(a[i]);
647
- }
648
- }
649
- return arr;
650
- };