jquery-datatables-rails 2.2.2 → 2.2.3

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: d5f212953d1541c80c223fc8b30b3c034eea8474
4
- data.tar.gz: c42274576cf87f64e3090feff9c08e10e5bd2496
3
+ metadata.gz: 809f7c045592c04bbd407dc7d2f7296807fd3f78
4
+ data.tar.gz: 67c2a1f408d9d8cddce1c6334af9849d4cdb5ac8
5
5
  SHA512:
6
- metadata.gz: 7ef688e522397c430b04bbbf81a432e3a40c328bdb5fe6fe07f7afc174b16a96dd82366327864db458bdef4a571d9cebee92fcabca86bb689856449957ed2ca4
7
- data.tar.gz: 6b1270bdfe70fd8a8b927205dcd868e504d5210f6204e7859737bdf2ce17de5a9a2e074dcf051b25af78a2376e4b02546649dd1d18f7e36123132e3b6399e7b9
6
+ metadata.gz: 518fbe26ee4fa7e56c165f28e0e24788fd1af7a76c9f00cb1b0ca0c2292349a158a25fdf48094a28a4106f118fdce3c8200406bf1efd3021dd6358b796d2b65f
7
+ data.tar.gz: b5240c8f4ac85ac8a560f3455dde501ab9c3e19fce8fca500935d2afe7d10ef9e89c2b0e3df42b238781d4e5dc176c9d8b39a156f604105925dc0883c5e13119
@@ -1,751 +1,650 @@
1
- /*! Responsive 1.0.1
2
- * 2014 SpryMedia Ltd - datatables.net/license
3
- */
4
-
5
1
  /**
6
- * @summary Responsive
7
- * @description Responsive tables plug-in for DataTables
8
- * @version 1.0.1
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.
2
+ * File: datatables.responsive.js
3
+ * Version: 0.2.0
4
+ * Author: Seen Sai Yang
5
+ * Info: https://github.com/Comanche/datatables-responsive
13
6
  *
14
- * This source file is free software, available under the following license:
15
- * MIT license - http://datatables.net/license/mit
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.
16
11
  *
17
12
  * This source file is distributed in the hope that it will be useful, but
18
13
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19
14
  * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20
15
  *
21
- * For details please refer to: http://www.datatables.net
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
22
20
  */
23
21
 
24
- (function(window, document, undefined) {
25
-
26
-
27
- var factory = function( $, DataTable ) {
28
- "use strict";
22
+ 'use strict';
29
23
 
30
24
  /**
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.
25
+ * Constructor for responsive datables helper.
36
26
  *
37
- * Responsive for DataTables has two modes of operation, which can used
38
- * individually or combined:
27
+ * This helper class makes datatables responsive to the window size.
39
28
  *
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.
29
+ * The parameter, breakpoints, is an object for each breakpoint key/value pair
30
+ * with the following format: { breakpoint_name: pixel_width_at_breakpoint }.
44
31
  *
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:
32
+ * An example is as follows:
49
33
  *
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.
34
+ * {
35
+ * tablet: 1024,
36
+ * phone: 480
37
+ * }
53
38
  *
54
- * Initialisation of Responsive is performed by:
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.
55
42
  *
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.
43
+ * The parameter, options, is an object of options supported by the responsive
44
+ * helper. The following options are supported:
62
45
  *
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+
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
+ * }
68
52
  *
69
- * @example
70
- * $('#example').DataTable( {
71
- * responsive: true
72
- * } );
73
- * } );
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.
74
58
  */
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
- else if ( settings.responsive ) {
81
- return;
82
- }
83
-
84
- this.s = {
85
- dt: new DataTable.Api( settings ),
86
- columns: []
87
- };
88
-
89
- // details is an object, but for simplicity the user can give it as a string
90
- if ( opts && typeof opts.details === 'string' ) {
91
- opts.details = { type: opts.details };
92
- }
93
-
94
- this.c = $.extend( true, {}, Responsive.defaults, opts );
95
- settings.responsive = this;
96
- this._constructor();
97
- };
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
+ }
98
137
 
99
- Responsive.prototype = {
100
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
101
- * Constructor
102
- */
103
-
104
- /**
105
- * Initialise the Responsive instance
106
- *
107
- * @private
108
- */
109
- _constructor: function ()
110
- {
111
- var that = this;
112
- var dt = this.s.dt;
113
-
114
- dt.settings()[0]._responsive = this;
115
-
116
- // Use DataTables' private throttle function to avoid processor thrashing
117
- $(window).on( 'resize.dtr orientationchange.dtr', dt.settings()[0].oApi._fnThrottle( function () {
118
- that._resize();
119
- } ) );
120
-
121
- // Destroy event handler
122
- dt.on( 'destroy.dtr', function () {
123
- $(window).off( 'resize.dtr orientationchange.dtr' );
124
- } );
125
-
126
- // Reorder the breakpoints array here in case they have been added out
127
- // of order
128
- this.c.breakpoints.sort( function (a, b) {
129
- return a.width < b.width ? 1 :
130
- a.width > b.width ? -1 : 0;
131
- } );
132
-
133
- this._classLogic();
134
- this._resizeAuto();
135
-
136
- // First pass - draw the table for the current viewport size
137
- this._resize();
138
-
139
- // Details handler
140
- var details = this.c.details;
141
- if ( details.type ) {
142
- that._detailsInit();
143
- this._detailsVis();
144
-
145
- dt.on( 'column-visibility.dtr', function () {
146
- that._detailsVis();
147
- } );
148
-
149
- $(dt.table().node()).addClass( 'dtr-'+details.type );
150
- }
151
- },
152
-
153
-
154
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
155
- * Private methods
156
- */
157
-
158
- /**
159
- * Calculate the visibility for the columns in a table for a given
160
- * breakpoint. The result is pre-determined based on the class logic if
161
- * class names are used to control all columns, but the width of the table
162
- * is also used if there are columns which are to be automatically shown
163
- * and hidden.
164
- *
165
- * @param {string} breakpoint Breakpoint name to use for the calculation
166
- * @return {array} Array of boolean values initiating the visibility of each
167
- * column.
168
- * @private
169
- */
170
- _columnsVisiblity: function ( breakpoint )
171
- {
172
- var dt = this.s.dt;
173
- var columns = this.s.columns;
174
- var i, ien;
175
-
176
- // Class logic - determine which columns are in this breakpoint based
177
- // on the classes. If no class control (i.e. `auto`) then `-` is used
178
- // to indicate this to the rest of the function
179
- var display = $.map( columns, function ( col ) {
180
- return col.auto && col.minWidth === null ?
181
- false :
182
- col.auto === true ?
183
- '-' :
184
- col.includeIn.indexOf( breakpoint ) !== -1;
185
- } );
186
-
187
- // Auto column control - first pass: how much width is taken by the
188
- // ones that must be included from the non-auto columns
189
- var requiredWidth = 0;
190
- for ( i=0, ien=display.length ; i<ien ; i++ ) {
191
- if ( display[i] === true ) {
192
- requiredWidth += columns[i].minWidth;
193
- }
194
- }
195
-
196
- // Second pass, use up any remaining width for other columns
197
- var widthAvailable = dt.table().container().offsetWidth;
198
- var usedWidth = widthAvailable - requiredWidth;
199
-
200
- for ( i=0, ien=display.length ; i<ien ; i++ ) {
201
- // Control column needs to always be included. This makes it sub-
202
- // optimal in terms of using the available with, but to stop layout
203
- // thrashing or overflow
204
- if ( columns[i].control ) {
205
- usedWidth -= columns[i].minWidth;
206
- }
207
- else if ( display[i] === '-' ) {
208
- // Otherwise, remove the width
209
- display[i] = usedWidth - columns[i].minWidth < 0 ?
210
- false :
211
- true;
212
-
213
- // Continue counting down the width, so a smaller column to the
214
- // left won't be shown
215
- usedWidth -= columns[i].minWidth;
216
- }
217
- }
218
-
219
- // Determine if the 'control' column should be shown (if there is one).
220
- // This is the case when there is a hidden column (that is not the
221
- // control column). The two loops look inefficient here, but they are
222
- // trivial and will fly through. We need to know the outcome from the
223
- // first , before the action in the second can be taken
224
- var showControl = false;
225
-
226
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
227
- if ( ! columns[i].control && ! display[i] ) {
228
- showControl = true;
229
- break;
230
- }
231
- }
232
-
233
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
234
- if ( columns[i].control ) {
235
- display[i] = showControl;
236
- }
237
- }
238
-
239
- return display;
240
- },
241
-
242
-
243
- /**
244
- * Create the internal `columns` array with information about the columns
245
- * for the table. This includes determining which breakpoints the column
246
- * will appear in, based upon class names in the column, which makes up the
247
- * vast majority of this method.
248
- *
249
- * @private
250
- */
251
- _classLogic: function ()
252
- {
253
- var that = this;
254
- var calc = {};
255
- var breakpoints = this.c.breakpoints;
256
- var columns = this.s.dt.columns().eq(0).map( function (i) {
257
- return {
258
- className: this.column(i).header().className,
259
- includeIn: [],
260
- auto: false,
261
- control: false
262
- };
263
- } );
264
-
265
- // Simply add a breakpoint to `includeIn` array, ensuring that there are
266
- // no duplicates
267
- var add = function ( colIdx, name ) {
268
- var includeIn = columns[ colIdx ].includeIn;
269
-
270
- if ( includeIn.indexOf( name ) === -1 ) {
271
- includeIn.push( name );
272
- }
273
- };
274
-
275
- var column = function ( colIdx, name, operator, matched ) {
276
- var size, i, ien;
277
-
278
- if ( ! operator ) {
279
- columns[ colIdx ].includeIn.push( name );
280
- }
281
- else if ( operator === 'max-' ) {
282
- // Add this breakpoint and all smaller
283
- size = that._find( name ).width;
284
-
285
- for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
286
- if ( breakpoints[i].width <= size ) {
287
- add( colIdx, breakpoints[i].name );
288
- }
289
- }
290
- }
291
- else if ( operator === 'min-' ) {
292
- // Add this breakpoint and all larger
293
- size = that._find( name ).width;
294
-
295
- for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
296
- if ( breakpoints[i].width >= size ) {
297
- add( colIdx, breakpoints[i].name );
298
- }
299
- }
300
- }
301
- else if ( operator === 'not-' ) {
302
- // Add all but this breakpoint (xxx need extra information)
303
-
304
- for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
305
- if ( breakpoints[i].name.indexOf( matched ) === -1 ) {
306
- add( colIdx, breakpoints[i].name );
307
- }
308
- }
309
- }
310
- };
311
-
312
- // Loop over each column and determine if it has a responsive control
313
- // class
314
- columns.each( function ( col, i ) {
315
- var classNames = col.className.split(' ');
316
- var hasClass = false;
317
-
318
- // Split the class name up so multiple rules can be applied if needed
319
- for ( var k=0, ken=classNames.length ; k<ken ; k++ ) {
320
- var className = $.trim( classNames[k] );
321
-
322
- if ( className === 'all' ) {
323
- // Include in all
324
- hasClass = true;
325
- col.includeIn = $.map( breakpoints, function (a) {
326
- return a.name;
327
- } );
328
- return;
329
- }
330
- else if ( className === 'none' ) {
331
- // Include in none (default) and no auto
332
- hasClass = true;
333
- return;
334
- }
335
- else if ( className === 'control' ) {
336
- // Special column that is only visible, when one of the other
337
- // columns is hidden. This is used for the details control
338
- hasClass = true;
339
- col.control = true;
340
- return;
341
- }
342
-
343
- $.each( breakpoints, function ( j, breakpoint ) {
344
- // Does this column have a class that matches this breakpoint?
345
- var brokenPoint = breakpoint.name.split('-');
346
- var re = new RegExp( '(min\\-|max\\-|not\\-)?('+brokenPoint[0]+')(\\-[_a-zA-Z0-9])?' );
347
- var match = className.match( re );
348
-
349
- if ( match ) {
350
- hasClass = true;
351
-
352
- if ( match[2] === brokenPoint[0] && match[3] === '-'+brokenPoint[1] ) {
353
- // Class name matches breakpoint name fully
354
- column( i, breakpoint.name, match[1], match[2]+match[3] );
355
- }
356
- else if ( match[2] === brokenPoint[0] && ! match[3] ) {
357
- // Class name matched primary breakpoint name with no qualifier
358
- column( i, breakpoint.name, match[1], match[2] );
359
- }
360
- }
361
- } );
362
- }
363
-
364
- // If there was no control class, then automatic sizing is used
365
- if ( ! hasClass ) {
366
- col.auto = true;
367
- }
368
- } );
369
-
370
- this.s.columns = columns;
371
- },
372
-
373
-
374
- /**
375
- * Initialisation for the details handler
376
- *
377
- * @private
378
- */
379
- _detailsInit: function ()
380
- {
381
- var that = this;
382
- var dt = this.s.dt;
383
- var details = this.c.details;
384
-
385
- // The inline type always uses the first child as the target
386
- if ( details.type === 'inline' ) {
387
- details.target = 'td:first-child';
388
- }
389
-
390
- // type.target can be a string jQuery selector or a column index
391
- var target = details.target;
392
- var selector = typeof target === 'string' ? target : 'td';
393
-
394
- // Click handler to show / hide the details rows when they are available
395
- $( dt.table().body() ).on( 'click', selector, function (e) {
396
- // If the table is not collapsed (i.e. there is no hidden columns)
397
- // then take no action
398
- if ( ! $(dt.table().node()).hasClass('collapsed' ) ) {
399
- return;
400
- }
401
-
402
- // For column index, we determine if we should act or not in the
403
- // handler - otherwise it is already okay
404
- if ( typeof target === 'number' ) {
405
- var targetIdx = target < 0 ?
406
- dt.columns().eq(0).length + target :
407
- target;
408
-
409
- if ( dt.cell( this ).index().column !== targetIdx ) {
410
- return;
411
- }
412
- }
413
-
414
- // $().closest() includes itself in its check
415
- var row = dt.row( $(this).closest('tr') );
416
-
417
- if ( row.child.isShown() ) {
418
- row.child( false );
419
- $( row.node() ).removeClass( 'parent' );
420
- }
421
- else {
422
- var info = that.c.details.renderer( dt, row[0] );
423
- row.child( info, 'child' ).show();
424
- $( row.node() ).addClass( 'parent' );
425
- }
426
- } );
427
- },
428
-
429
-
430
- /**
431
- * Update the child rows in the table whenever the column visibility changes
432
- *
433
- * @private
434
- */
435
- _detailsVis: function ()
436
- {
437
- var that = this;
438
- var dt = this.s.dt;
439
-
440
- var hiddenColumns = dt.columns(':hidden').indexes().flatten();
441
- var haveHidden = true;
442
-
443
- if ( hiddenColumns.length === 0 || ( hiddenColumns.length === 1 && this.s.columns[ hiddenColumns[0] ].control ) ) {
444
- haveHidden = false;
445
- }
446
-
447
- if ( haveHidden ) {
448
- // Got hidden columns
449
- $( dt.table().node() ).addClass('collapsed');
450
-
451
- // Show all existing child rows
452
- dt.rows().eq(0).each( function (idx) {
453
- var row = dt.row( idx );
454
-
455
- if ( row.child() ) {
456
- var info = that.c.details.renderer( dt, row[0] );
457
-
458
- // The renderer can return false to have no child row
459
- if ( info === false ) {
460
- row.child.hide();
461
- }
462
- else {
463
- row.child( info, 'child' ).show();
464
- }
465
- }
466
- } );
467
- }
468
- else {
469
- // No hidden columns
470
- $( dt.table().node() ).removeClass('collapsed');
471
-
472
- // Hide all existing child rows
473
- dt.rows().eq(0).each( function (idx) {
474
- dt.row( idx ).child.hide();
475
- } );
476
- }
477
- },
478
-
479
-
480
- /**
481
- * Find a breakpoint object from a name
482
- * @param {string} name Breakpoint name to find
483
- * @return {object} Breakpoint description object
484
- */
485
- _find: function ( name )
486
- {
487
- var breakpoints = this.c.breakpoints;
488
-
489
- for ( var i=0, ien=breakpoints.length ; i<ien ; i++ ) {
490
- if ( breakpoints[i].name === name ) {
491
- return breakpoints[i];
492
- }
493
- }
494
- },
495
-
496
-
497
- /**
498
- * Alter the table display for a resized viewport. This involves first
499
- * determining what breakpoint the window currently is in, getting the
500
- * column visibilities to apply and then setting them.
501
- *
502
- * @private
503
- */
504
- _resize: function ()
505
- {
506
- var dt = this.s.dt;
507
- var width = $(window).width();
508
- var breakpoints = this.c.breakpoints;
509
- var breakpoint = breakpoints[0].name;
510
-
511
- // Determine what breakpoint we are currently at
512
- for ( var i=breakpoints.length-1 ; i>=0 ; i-- ) {
513
- if ( width <= breakpoints[i].width ) {
514
- breakpoint = breakpoints[i].name;
515
- break;
516
- }
517
- }
518
-
519
- // Show the columns for that break point
520
- var columns = this._columnsVisiblity( breakpoint );
521
-
522
- dt.columns().eq(0).each( function ( colIdx, i ) {
523
- dt.column( colIdx ).visible( columns[i] );
524
- } );
525
- },
526
-
527
-
528
- /**
529
- * Determine the width of each column in the table so the auto column hiding
530
- * has that information to work with. This method is never going to be 100%
531
- * perfect since column widths can change slightly per page, but without
532
- * seriously compromising performance this is quite effective.
533
- *
534
- * @private
535
- */
536
- _resizeAuto: function ()
537
- {
538
- var dt = this.s.dt;
539
- var columns = this.s.columns;
540
-
541
- // Are we allowed to do auto sizing?
542
- if ( ! this.c.auto ) {
543
- return;
544
- }
545
-
546
- // Are there any columns that actually need auto-sizing, or do they all
547
- // have classes defined
548
- if ( $.inArray( true, $.map( columns, function (c) { return c.auto; } ) ) === -1 ) {
549
- return;
550
- }
551
-
552
- // Clone the table with the current data in it
553
- var tableWidth = dt.table().node().offsetWidth;
554
- var columnWidths = dt.columns;
555
- var clonedTable = dt.table().node().cloneNode( false );
556
- var clonedHeader = $( dt.table().header().cloneNode( false ) ).appendTo( clonedTable );
557
- var clonedBody = $( dt.table().body().cloneNode( false ) ).appendTo( clonedTable );
558
-
559
- // This is a bit slow, but we need to get a clone of each row that
560
- // includes all columns. As such, try to do this as little as possible.
561
- dt.rows( { page: 'current' } ).indexes().each( function ( idx ) {
562
- var clone = dt.row( idx ).node().cloneNode( true );
563
-
564
- if ( dt.columns( ':hidden' ).flatten().length ) {
565
- $(clone).append( dt.cells( idx, ':hidden' ).nodes().to$().clone() );
566
- }
567
-
568
- $(clone).appendTo( clonedBody );
569
- } );
570
-
571
- var cells = dt.columns().header().to$().clone( false ).wrapAll('tr').appendTo( clonedHeader );
572
- var inserted = $('<div/>')
573
- .css( {
574
- width: 1,
575
- height: 1,
576
- overflow: 'hidden'
577
- } )
578
- .append( clonedTable )
579
- .insertBefore( dt.table().node() );
580
-
581
- // The cloned header now contains the smallest that each column can be
582
- dt.columns().eq(0).each( function ( idx ) {
583
- columns[idx].minWidth = cells[ idx ].offsetWidth || 0;
584
- } );
585
-
586
- inserted.remove();
587
- }
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);
588
157
  };
589
158
 
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
+ }
255
+ };
590
256
 
591
257
  /**
592
- * List of default breakpoints. Each item in the array is an object with two
593
- * properties:
258
+ * Sets or removes window resize handler.
594
259
  *
595
- * * `name` - the breakpoint name.
596
- * * `width` - the breakpoint width
597
- *
598
- * @name Responsive.breakpoints
599
- * @static
260
+ * @param {Boolean} bindFlag
600
261
  */
601
- Responsive.breakpoints = [
602
- { name: 'desktop', width: Infinity },
603
- { name: 'tablet-l', width: 1024 },
604
- { name: 'tablet-p', width: 768 },
605
- { name: 'mobile-l', width: 480 },
606
- { name: 'mobile-p', width: 320 }
607
- ];
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
+ };
608
276
 
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
+ };
609
357
 
610
358
  /**
611
- * Responsive default settings for initialisation
612
- *
613
- * @namespace
614
- * @name Responsive.defaults
615
- * @static
359
+ * Show/hide datatables columns.
616
360
  */
617
- Responsive.defaults = {
618
- /**
619
- * List of breakpoints for the instance. Note that this means that each
620
- * instance can have its own breakpoints. Additionally, the breakpoints
621
- * cannot be changed once an instance has been creased.
622
- *
623
- * @type {Array}
624
- * @default Takes the value of `Responsive.breakpoints`
625
- */
626
- breakpoints: Responsive.breakpoints,
627
-
628
- /**
629
- * Enable / disable auto hiding calculations. It can help to increase
630
- * performance slightly if you disable this option, but all columns would
631
- * need to have breakpoint classes assigned to them
632
- *
633
- * @type {Boolean}
634
- * @default `true`
635
- */
636
- auto: true,
637
-
638
- /**
639
- * Details control. If given as a string value, the `type` property of the
640
- * default object is set to that value, and the defaults used for the rest
641
- * of the object - this is for ease of implementation.
642
- *
643
- * The object consists of the following properties:
644
- *
645
- * * `renderer` - function that is called for display of the child row data.
646
- * The default function will show the data from the hidden columns
647
- * * `target` - Used as the selector for what objects to attach the child
648
- * open / close to
649
- * * `type` - `false` to disable the details display, `inline` or `column`
650
- * for the two control types
651
- *
652
- * @type {Object|string}
653
- */
654
- details: {
655
- renderer: function ( api, rowIdx ) {
656
- var data = api.cells( rowIdx, ':hidden' ).eq(0).map( function ( cell ) {
657
- var header = $( api.column( cell.column ).header() );
658
-
659
- if ( header.hasClass( 'control' ) ) {
660
- return '';
661
- }
662
-
663
- return '<li>'+
664
- '<span class="dtr-title">'+
665
- header.text()+':'+
666
- '</span> '+
667
- '<span class="dtr-data">'+
668
- api.cell( cell ).data()+
669
- '</span>'+
670
- '</li>';
671
- } ).toArray().join('');
672
-
673
- return data ?
674
- $('<ul/>').append( data ) :
675
- false;
676
- },
677
-
678
- target: 0,
679
-
680
- type: 'inline'
681
- }
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
+ }
682
383
  };
683
384
 
385
+ /**
386
+ * Create the expand icon on the column with the data-class="expand" attribute
387
+ * defined for it's header.
388
+ *
389
+ * @param {Object} tr table row object
390
+ */
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
+ }
426
+ };
684
427
 
685
- /*
686
- * API
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
687
435
  */
688
- var Api = $.fn.dataTable.Api;
436
+ ResponsiveDatatablesHelper.prototype.showRowDetailEventHandler = function (event) {
437
+ var responsiveDatatablesHelperInstance = event.data.responsiveDatatablesHelperInstance;
438
+ if (responsiveDatatablesHelperInstance.disabled) {
439
+ return;
440
+ }
689
441
 
690
- // Doesn't do anything - work around for a bug in DT... Not documented
691
- Api.register( 'responsive()', function () {
692
- return this;
693
- } );
442
+ var td = $(this);
694
443
 
695
- Api.register( 'responsive.recalc()', function ( rowIdx, intParse, virtual ) {
696
- this.iterator( 'table', function ( ctx ) {
697
- if ( ctx._responsive ) {
698
- ctx._responsive._resizeAuto();
699
- ctx._responsive._resize();
700
- }
701
- } );
702
- } );
444
+ // Nothing to do if there are no columns hidden.
445
+ if (!td.closest('table').hasClass('has-columns-hidden')) {
446
+ return;
447
+ }
703
448
 
449
+ // Get the parent tr of which this td belongs to.
450
+ var tr = td.closest('tr');
451
+
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
+ }
458
+
459
+ tr.toggleClass('detail-show');
460
+
461
+ // Prevent click event from bubbling up to higher-level DOM elements.
462
+ event.stopPropagation();
463
+ };
704
464
 
705
465
  /**
706
- * Version information
466
+ * Show row details.
707
467
  *
708
- * @name Responsive.version
709
- * @static
468
+ * @param {ResponsiveDatatablesHelper} responsiveDatatablesHelperInstance instance of ResponsiveDatatablesHelper
469
+ * @param {Object} tr jQuery wrapped set
710
470
  */
711
- Responsive.version = '1.0.1';
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
+ };
712
534
 
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
+ };
713
556
 
714
- $.fn.dataTable.Responsive = Responsive;
715
- $.fn.DataTable.Responsive = Responsive;
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;
716
564
 
717
- // Attach a listener to the document which listens for DataTables initialisation
718
- // events so we can automatically initialise
719
- $(document).on( 'init.dt.dtr', function (e, settings, json) {
720
- if ( $(settings.nTable).hasClass( 'responsive' ) ||
721
- $(settings.nTable).hasClass( 'dt-responsive' ) ||
722
- settings.oInit.responsive
723
- ) {
724
- var init = settings.oInit.responsive;
565
+ if (this.disabled) {
566
+ // Remove windows resize handler.
567
+ this.setWindowsResizeHandler(false);
725
568
 
726
- if ( init !== false ) {
727
- new Responsive( settings, $.isPlainObject( init ) ? init : {} );
728
- }
729
- }
730
- } );
569
+ // Remove all trs that have row details.
570
+ $('tbody tr.row-detail', this.tableElement).remove();
731
571
 
732
- return Responsive;
733
- }; // /factory
572
+ // Remove all trs that are marked to have row details shown.
573
+ $('tbody tr', this.tableElement).removeClass('detail-show');
734
574
 
575
+ // Remove all expander icons.
576
+ $('tbody tr span.responsiveExpander', this.tableElement).remove();
735
577
 
736
- // Define as an AMD module if possible
737
- if ( typeof define === 'function' && define.amd ) {
738
- define( ['jquery', 'datatables'], factory );
739
- }
740
- else if ( typeof exports === 'object' ) {
741
- // Node/CommonJS
742
- factory( require('jquery'), require('datatables') );
743
- }
744
- else if ( jQuery && !jQuery.fn.dataTable.Responsive ) {
745
- // Otherwise simply initialise as normal, stopping multiple evaluation
746
- factory( jQuery, jQuery.fn.dataTable );
747
- }
578
+ this.columnsHiddenIndexes = [];
579
+ this.columnsShownIndexes = this.columnIndexes;
580
+ this.showHideColumns();
581
+ this.tableElement.removeClass('has-columns-hidden');
748
582
 
583
+ this.tableElement.off('click', 'span.responsiveExpander', this.showRowDetailEventHandler);
584
+ } else {
585
+ // Add windows resize handler.
586
+ this.setWindowsResizeHandler();
587
+ }
588
+ };
749
589
 
750
- })(window, document);
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
+ };
751
608
 
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
+ };
635
+
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
+ };