jquery-datatables-rails 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/dataTables/bootstrap/3/jquery.dataTables.bootstrap.js +64 -29
  3. data/app/assets/javascripts/dataTables/extras/dataTables.autoFill.js +806 -648
  4. data/app/assets/javascripts/dataTables/extras/dataTables.buttons.js +1607 -0
  5. data/app/assets/javascripts/dataTables/extras/dataTables.colReorder.js +220 -267
  6. data/app/assets/javascripts/dataTables/extras/dataTables.fixedColumns.js +164 -69
  7. data/app/assets/javascripts/dataTables/extras/dataTables.fixedHeader.js +469 -870
  8. data/app/assets/javascripts/dataTables/extras/dataTables.keyTable.js +636 -972
  9. data/app/assets/javascripts/dataTables/extras/dataTables.responsive.js +472 -187
  10. data/app/assets/javascripts/dataTables/extras/dataTables.rowReorder.js +619 -0
  11. data/app/assets/javascripts/dataTables/extras/dataTables.scroller.js +146 -111
  12. data/app/assets/javascripts/dataTables/extras/dataTables.select.js +1038 -0
  13. data/app/assets/javascripts/dataTables/jquery.dataTables.api.fnGetColumnData.js +0 -0
  14. data/app/assets/javascripts/dataTables/jquery.dataTables.api.fnReloadAjax.js +0 -0
  15. data/app/assets/javascripts/dataTables/jquery.dataTables.foundation.js +37 -61
  16. data/app/assets/javascripts/dataTables/jquery.dataTables.js +720 -387
  17. data/app/assets/javascripts/dataTables/jquery.dataTables.sorting.ipAddress.js +44 -0
  18. data/app/assets/javascripts/dataTables/jquery.dataTables.sorting.numbersHtml.js +0 -0
  19. data/app/assets/javascripts/dataTables/jquery.dataTables.typeDetection.numbersHtml.js +0 -0
  20. data/app/assets/stylesheets/dataTables/jquery.dataTables.scss +34 -66
  21. data/app/assets/stylesheets/dataTables/src/demo_table.css +1 -1
  22. data/app/assets/stylesheets/dataTables/src/demo_table_jui.css.scss +4 -4
  23. data/lib/jquery/datatables/rails/version.rb +1 -1
  24. metadata +24 -19
@@ -1,15 +1,15 @@
1
- /*! Scroller 1.2.2
2
- * ©2011-2014 SpryMedia Ltd - datatables.net/license
1
+ /*! Scroller 1.4.0
2
+ * ©2011-2015 SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
5
5
  /**
6
6
  * @summary Scroller
7
7
  * @description Virtual rendering for DataTables
8
- * @version 1.2.2
8
+ * @version 1.4.0
9
9
  * @file dataTables.scroller.js
10
10
  * @author SpryMedia Ltd (www.sprymedia.co.uk)
11
11
  * @contact www.sprymedia.co.uk/contact
12
- * @copyright Copyright 2011-2014 SpryMedia Ltd.
12
+ * @copyright Copyright 2011-2015 SpryMedia Ltd.
13
13
  *
14
14
  * This source file is free software, available under the following license:
15
15
  * MIT license - http://datatables.net/license/mit
@@ -21,11 +21,35 @@
21
21
  * For details please refer to: http://www.datatables.net
22
22
  */
23
23
 
24
- (function(window, document, undefined){
24
+ (function( factory ){
25
+ if ( typeof define === 'function' && define.amd ) {
26
+ // AMD
27
+ define( ['jquery', 'datatables.net'], function ( $ ) {
28
+ return factory( $, window, document );
29
+ } );
30
+ }
31
+ else if ( typeof exports === 'object' ) {
32
+ // CommonJS
33
+ module.exports = function (root, $) {
34
+ if ( ! root ) {
35
+ root = window;
36
+ }
25
37
 
38
+ if ( ! $ || ! $.fn.dataTable ) {
39
+ $ = require('datatables.net')(root, $).$;
40
+ }
41
+
42
+ return factory( $, root, root.document );
43
+ };
44
+ }
45
+ else {
46
+ // Browser
47
+ factory( jQuery, window, document );
48
+ }
49
+ }(function( $, window, document, undefined ) {
50
+ 'use strict';
51
+ var DataTable = $.fn.dataTable;
26
52
 
27
- var factory = function( $, DataTable ) {
28
- "use strict";
29
53
 
30
54
  /**
31
55
  * Scroller is a virtual rendering plug-in for DataTables which allows large
@@ -48,7 +72,7 @@ var factory = function( $, DataTable ) {
48
72
  * Key features include:
49
73
  * <ul class="limit_length">
50
74
  * <li>Speed! The aim of Scroller for DataTables is to make rendering large data sets fast</li>
51
- * <li>Full compatibility with deferred rendering in DataTables 1.9 for maximum speed</li>
75
+ * <li>Full compatibility with deferred rendering in DataTables for maximum speed</li>
52
76
  * <li>Display millions of rows</li>
53
77
  * <li>Integration with state saving in DataTables (scrolling position is saved)</li>
54
78
  * <li>Easy to use</li>
@@ -57,34 +81,32 @@ var factory = function( $, DataTable ) {
57
81
  * @class
58
82
  * @constructor
59
83
  * @global
60
- * @param {object} oDT DataTables settings object
61
- * @param {object} [oOpts={}] Configuration object for FixedColumns. Options
84
+ * @param {object} dt DataTables settings object or API instance
85
+ * @param {object} [opts={}] Configuration object for FixedColumns. Options
62
86
  * are defined by {@link Scroller.defaults}
63
87
  *
64
88
  * @requires jQuery 1.7+
65
- * @requires DataTables 1.9.0+
89
+ * @requires DataTables 1.10.0+
66
90
  *
67
91
  * @example
68
92
  * $(document).ready(function() {
69
- * $('#example').dataTable( {
70
- * "sScrollY": "200px",
71
- * "sAjaxSource": "media/dataset/large.txt",
72
- * "sDom": "frtiS",
73
- * "bDeferRender": true
93
+ * $('#example').DataTable( {
94
+ * "scrollY": "200px",
95
+ * "ajax": "media/dataset/large.txt",
96
+ * "dom": "frtiS",
97
+ * "deferRender": true
74
98
  * } );
75
99
  * } );
76
100
  */
77
- var Scroller = function ( oDTSettings, oOpts ) {
101
+ var Scroller = function ( dt, opts ) {
78
102
  /* Sanity check - you just know it will happen */
79
- if ( ! this instanceof Scroller )
80
- {
103
+ if ( ! (this instanceof Scroller) ) {
81
104
  alert( "Scroller warning: Scroller must be initialised with the 'new' keyword." );
82
105
  return;
83
106
  }
84
107
 
85
- if ( typeof oOpts == 'undefined' )
86
- {
87
- oOpts = {};
108
+ if ( opts === undefined ) {
109
+ opts = {};
88
110
  }
89
111
 
90
112
  /**
@@ -99,7 +121,7 @@ var Scroller = function ( oDTSettings, oOpts ) {
99
121
  * @type object
100
122
  * @default Passed in as first parameter to constructor
101
123
  */
102
- "dt": oDTSettings,
124
+ "dt": $.fn.dataTable.Api( dt ).settings()[0],
103
125
 
104
126
  /**
105
127
  * Pixel location of the top of the drawn table in the viewport
@@ -193,7 +215,7 @@ var Scroller = function ( oDTSettings, oOpts ) {
193
215
 
194
216
  // @todo The defaults should extend a `c` property and the internal settings
195
217
  // only held in the `s` property. At the moment they are mixed
196
- this.s = $.extend( this.s, Scroller.oDefaults, oOpts );
218
+ this.s = $.extend( this.s, Scroller.oDefaults, opts );
197
219
 
198
220
  // Workaround for row height being read from height object (see above comment)
199
221
  this.s.heights.row = this.s.rowHeight;
@@ -211,7 +233,12 @@ var Scroller = function ( oDTSettings, oOpts ) {
211
233
  "loader": null
212
234
  };
213
235
 
214
- /* Attach the instance to the DataTables instance so it can be accessed */
236
+ // Attach the instance to the DataTables instance so it can be accessed in
237
+ // future. Don't initialise Scroller twice on the same table
238
+ if ( this.s.dt.oScroller ) {
239
+ return;
240
+ }
241
+
215
242
  this.s.dt.oScroller = this;
216
243
 
217
244
  /* Let's do it */
@@ -220,7 +247,7 @@ var Scroller = function ( oDTSettings, oOpts ) {
220
247
 
221
248
 
222
249
 
223
- Scroller.prototype = /** @lends Scroller.prototype */{
250
+ $.extend( Scroller.prototype, {
224
251
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
225
252
  * Public methods
226
253
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@@ -410,7 +437,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
410
437
 
411
438
  if ( bRedraw === undefined || bRedraw )
412
439
  {
413
- this.s.dt.oInstance.fnDraw();
440
+ this.s.dt.oInstance.fnDraw( false );
414
441
  }
415
442
  },
416
443
 
@@ -438,7 +465,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
438
465
  /* Insert a div element that we can use to force the DT scrolling container to
439
466
  * the height that would be required if the whole table was being displayed
440
467
  */
441
- this.dom.force.style.position = "absolute";
468
+ this.dom.force.style.position = "relative";
442
469
  this.dom.force.style.top = "0px";
443
470
  this.dom.force.style.left = "0px";
444
471
  this.dom.force.style.width = "1px";
@@ -458,7 +485,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
458
485
  // Add a 'loading' indicator
459
486
  if ( this.s.loadingIndicator )
460
487
  {
461
- this.dom.loader = $('<div class="DTS_Loading">'+this.s.dt.oLanguage.sLoadingRecords+'</div>')
488
+ this.dom.loader = $('<div class="dataTables_processing DTS_Loading">'+this.s.dt.oLanguage.sLoadingRecords+'</div>')
462
489
  .css('display', 'none');
463
490
 
464
491
  $(this.dom.scroller.parentNode)
@@ -529,6 +556,10 @@ Scroller.prototype = /** @lends Scroller.prototype */{
529
556
  this.s.topRowFloat = this.s.dt.oLoadedState.iScrollerTopRow || 0;
530
557
  }
531
558
 
559
+ $(this.s.dt.nTable).on( 'init.dt', function () {
560
+ that.fnMeasure();
561
+ } );
562
+
532
563
  /* Destructor */
533
564
  this.s.dt.aoDestroyCallback.push( {
534
565
  "sName": "Scroller",
@@ -537,6 +568,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
537
568
  $(that.dom.scroller).off('touchstart.DTS scroll.DTS');
538
569
  $(that.s.dt.nTableWrapper).removeClass('DTS');
539
570
  $('div.DTS_Loading', that.dom.scroller.parentNode).remove();
571
+ $(that.s.dt.nTable).off( 'init.dt' );
540
572
 
541
573
  that.dom.table.style.position = "";
542
574
  that.dom.table.style.top = "";
@@ -635,9 +667,6 @@ Scroller.prototype = /** @lends Scroller.prototype */{
635
667
  }
636
668
 
637
669
  that.s.dt._iDisplayStart = iTopRow;
638
- if ( that.s.dt.oApi._fnCalculateEnd ) { // Removed in 1.10
639
- that.s.dt.oApi._fnCalculateEnd( that.s.dt );
640
- }
641
670
  that.s.dt.oApi._fnDraw( that.s.dt );
642
671
  };
643
672
 
@@ -682,14 +711,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
682
711
  // If the virtual and physical height match, then we use a linear
683
712
  // transform between the two, allowing the scrollbar to be linear
684
713
  if ( heights.virtual === heights.scroll ) {
685
- coeff = (heights.virtual-heights.viewport) / (heights.scroll-heights.viewport);
686
-
687
- if ( dir === 'virtualToPhysical' ) {
688
- return val / coeff;
689
- }
690
- else if ( dir === 'physicalToVirtual' ) {
691
- return val * coeff;
692
- }
714
+ return val;
693
715
  }
694
716
 
695
717
  // Otherwise, we want a non-linear scrollbar to take account of the
@@ -868,7 +890,11 @@ Scroller.prototype = /** @lends Scroller.prototype */{
868
890
  heights.scroll = max;
869
891
  }
870
892
 
871
- this.dom.force.style.height = heights.scroll+"px";
893
+ // Minimum height so there is always a row visible (the 'no rows found'
894
+ // if reduced to zero filtering)
895
+ this.dom.force.style.height = heights.scroll > this.s.heights.row ?
896
+ heights.scroll+'px' :
897
+ this.s.heights.row+'px';
872
898
  },
873
899
 
874
900
 
@@ -903,18 +929,10 @@ Scroller.prototype = /** @lends Scroller.prototype */{
903
929
 
904
930
  $('div.'+dt.oClasses.sScrollBody, container).append( nTable );
905
931
 
906
- var appendTo;
907
- if (dt._bInitComplete) {
908
- appendTo = origTable.parentNode;
909
- } else {
910
- if (!this.s.dt.nHolding) {
911
- this.s.dt.nHolding = $( '<div></div>' ).insertBefore( this.s.dt.nTable );
912
- }
913
- appendTo = this.s.dt.nHolding;
914
- }
915
-
916
- container.appendTo( appendTo );
932
+ // If initialised using `dom`, use the holding element as the insert point
933
+ container.appendTo( this.s.dt.nHolding || origTable.parentNode );
917
934
  this.s.heights.row = $('tr', tbody).eq(1).outerHeight();
935
+
918
936
  container.remove();
919
937
  },
920
938
 
@@ -1002,7 +1020,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
1002
1020
  }
1003
1021
  }
1004
1022
  }
1005
- };
1023
+ } );
1006
1024
 
1007
1025
 
1008
1026
 
@@ -1155,7 +1173,7 @@ Scroller.oDefaults = Scroller.defaults;
1155
1173
  * @name Scroller.version
1156
1174
  * @static
1157
1175
  */
1158
- Scroller.version = "1.2.2";
1176
+ Scroller.version = "1.4.0";
1159
1177
 
1160
1178
 
1161
1179
 
@@ -1163,19 +1181,17 @@ Scroller.version = "1.2.2";
1163
1181
  * Initialisation
1164
1182
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1165
1183
 
1166
- /*
1167
- * Register a new feature with DataTables
1168
- */
1184
+ // Legacy `dom` parameter initialisation support
1169
1185
  if ( typeof $.fn.dataTable == "function" &&
1170
1186
  typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
1171
- $.fn.dataTableExt.fnVersionCheck('1.9.0') )
1187
+ $.fn.dataTableExt.fnVersionCheck('1.10.0') )
1172
1188
  {
1173
1189
  $.fn.dataTableExt.aoFeatures.push( {
1174
1190
  "fnInit": function( oDTSettings ) {
1175
1191
  var init = oDTSettings.oInit;
1176
1192
  var opts = init.scroller || init.oScroller || {};
1177
- var oScroller = new Scroller( oDTSettings, opts );
1178
- return oScroller.dom.wrapper;
1193
+
1194
+ new Scroller( oDTSettings, opts );
1179
1195
  },
1180
1196
  "cFeature": "S",
1181
1197
  "sFeature": "Scroller"
@@ -1183,9 +1199,28 @@ if ( typeof $.fn.dataTable == "function" &&
1183
1199
  }
1184
1200
  else
1185
1201
  {
1186
- alert( "Warning: Scroller requires DataTables 1.9.0 or greater - www.datatables.net/download");
1202
+ alert( "Warning: Scroller requires DataTables 1.10.0 or greater - www.datatables.net/download");
1187
1203
  }
1188
1204
 
1205
+ // Attach a listener to the document which listens for DataTables initialisation
1206
+ // events so we can automatically initialise
1207
+ $(document).on( 'preInit.dt.dtscroller', function (e, settings) {
1208
+ if ( e.namespace !== 'dt' ) {
1209
+ return;
1210
+ }
1211
+
1212
+ var init = settings.oInit.scroller;
1213
+ var defaults = DataTable.defaults.scroller;
1214
+
1215
+ if ( init || defaults ) {
1216
+ var opts = $.extend( {}, init, defaults );
1217
+
1218
+ if ( init !== false ) {
1219
+ new Scroller( settings, opts );
1220
+ }
1221
+ }
1222
+ } );
1223
+
1189
1224
 
1190
1225
  // Attach Scroller to DataTables so it can be accessed as an 'extra'
1191
1226
  $.fn.dataTable.Scroller = Scroller;
@@ -1193,70 +1228,70 @@ $.fn.DataTable.Scroller = Scroller;
1193
1228
 
1194
1229
 
1195
1230
  // DataTables 1.10 API method aliases
1196
- if ( $.fn.dataTable.Api ) {
1197
- var Api = $.fn.dataTable.Api;
1231
+ var Api = $.fn.dataTable.Api;
1198
1232
 
1199
- Api.register( 'scroller()', function () {
1200
- return this;
1201
- } );
1233
+ Api.register( 'scroller()', function () {
1234
+ return this;
1235
+ } );
1202
1236
 
1203
- Api.register( 'scroller().rowToPixels()', function ( rowIdx, intParse, virtual ) {
1204
- var ctx = this.context;
1237
+ // Undocumented and deprecated - is it actually useful at all?
1238
+ Api.register( 'scroller().rowToPixels()', function ( rowIdx, intParse, virtual ) {
1239
+ var ctx = this.context;
1205
1240
 
1206
- if ( ctx.length && ctx[0].oScroller ) {
1207
- return ctx[0].oScroller.fnRowToPixels( rowIdx, intParse, virtual );
1208
- }
1209
- // undefined
1210
- } );
1241
+ if ( ctx.length && ctx[0].oScroller ) {
1242
+ return ctx[0].oScroller.fnRowToPixels( rowIdx, intParse, virtual );
1243
+ }
1244
+ // undefined
1245
+ } );
1211
1246
 
1212
- Api.register( 'scroller().pixelsToRow()', function ( pixels, intParse, virtual ) {
1213
- var ctx = this.context;
1247
+ // Undocumented and deprecated - is it actually useful at all?
1248
+ Api.register( 'scroller().pixelsToRow()', function ( pixels, intParse, virtual ) {
1249
+ var ctx = this.context;
1214
1250
 
1215
- if ( ctx.length && ctx[0].oScroller ) {
1216
- return ctx[0].oScroller.fnPixelsToRow( pixels, intParse, virtual );
1251
+ if ( ctx.length && ctx[0].oScroller ) {
1252
+ return ctx[0].oScroller.fnPixelsToRow( pixels, intParse, virtual );
1253
+ }
1254
+ // undefined
1255
+ } );
1256
+
1257
+ // Undocumented and deprecated - use `row().scrollTo()` instead
1258
+ Api.register( 'scroller().scrollToRow()', function ( row, ani ) {
1259
+ this.iterator( 'table', function ( ctx ) {
1260
+ if ( ctx.oScroller ) {
1261
+ ctx.oScroller.fnScrollToRow( row, ani );
1217
1262
  }
1218
- // undefined
1219
1263
  } );
1220
1264
 
1221
- Api.register( 'scroller().scrollToRow()', function ( row, ani ) {
1222
- this.iterator( 'table', function ( ctx ) {
1223
- if ( ctx.oScroller ) {
1224
- ctx.oScroller.fnScrollToRow( row, ani );
1225
- }
1226
- } );
1265
+ return this;
1266
+ } );
1227
1267
 
1228
- return this;
1229
- } );
1268
+ Api.register( 'row().scrollTo()', function ( ani ) {
1269
+ var that = this;
1230
1270
 
1231
- Api.register( 'scroller().measure()', function ( redraw ) {
1232
- this.iterator( 'table', function ( ctx ) {
1233
- if ( ctx.oScroller ) {
1234
- ctx.oScroller.fnMeasure( redraw );
1235
- }
1236
- } );
1271
+ this.iterator( 'row', function ( ctx, rowIdx ) {
1272
+ if ( ctx.oScroller ) {
1273
+ var displayIdx = that
1274
+ .rows( { order: 'applied', search: 'applied' } )
1275
+ .indexes()
1276
+ .indexOf( rowIdx );
1237
1277
 
1238
- return this;
1278
+ ctx.oScroller.fnScrollToRow( displayIdx, ani );
1279
+ }
1239
1280
  } );
1240
- }
1241
-
1242
-
1243
- return Scroller;
1244
- }; // /factory
1245
1281
 
1282
+ return this;
1283
+ } );
1246
1284
 
1247
- // Define as an AMD module if possible
1248
- if ( typeof define === 'function' && define.amd ) {
1249
- define( ['jquery', 'datatables'], factory );
1250
- }
1251
- else if ( typeof exports === 'object' ) {
1252
- // Node/CommonJS
1253
- factory( require('jquery'), require('datatables') );
1254
- }
1255
- else if ( jQuery && !jQuery.fn.dataTable.Scroller ) {
1256
- // Otherwise simply initialise as normal, stopping multiple evaluation
1257
- factory( jQuery, jQuery.fn.dataTable );
1258
- }
1285
+ Api.register( 'scroller.measure()', function ( redraw ) {
1286
+ this.iterator( 'table', function ( ctx ) {
1287
+ if ( ctx.oScroller ) {
1288
+ ctx.oScroller.fnMeasure( redraw );
1289
+ }
1290
+ } );
1259
1291
 
1292
+ return this;
1293
+ } );
1260
1294
 
1261
- })(window, document);
1262
1295
 
1296
+ return Scroller;
1297
+ }));
@@ -0,0 +1,1038 @@
1
+ /*! Select for DataTables 1.1.0
2
+ * 2015 SpryMedia Ltd - datatables.net/license/mit
3
+ */
4
+
5
+ /**
6
+ * @summary Select for DataTables
7
+ * @description A collection of API methods, events and buttons for DataTables
8
+ * that provides selection options of the items in a DataTable
9
+ * @version 1.1.0
10
+ * @file dataTables.select.js
11
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
12
+ * @contact datatables.net/forums
13
+ * @copyright Copyright 2015 SpryMedia Ltd.
14
+ *
15
+ * This source file is free software, available under the following license:
16
+ * MIT license - http://datatables.net/license/mit
17
+ *
18
+ * This source file is distributed in the hope that it will be useful, but
19
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
21
+ *
22
+ * For details please refer to: http://www.datatables.net/extensions/select
23
+ */
24
+ (function( factory ){
25
+ if ( typeof define === 'function' && define.amd ) {
26
+ // AMD
27
+ define( ['jquery', 'datatables.net'], function ( $ ) {
28
+ return factory( $, window, document );
29
+ } );
30
+ }
31
+ else if ( typeof exports === 'object' ) {
32
+ // CommonJS
33
+ module.exports = function (root, $) {
34
+ if ( ! root ) {
35
+ root = window;
36
+ }
37
+
38
+ if ( ! $ || ! $.fn.dataTable ) {
39
+ $ = require('datatables.net')(root, $).$;
40
+ }
41
+
42
+ return factory( $, root, root.document );
43
+ };
44
+ }
45
+ else {
46
+ // Browser
47
+ factory( jQuery, window, document );
48
+ }
49
+ }(function( $, window, document, undefined ) {
50
+ 'use strict';
51
+ var DataTable = $.fn.dataTable;
52
+
53
+
54
+ // Version information for debugger
55
+ DataTable.select = {};
56
+ DataTable.select.version = '1.1.0';
57
+
58
+ /*
59
+
60
+ Select is a collection of API methods, event handlers, event emitters and
61
+ buttons (for the `Buttons` extension) for DataTables. It provides the following
62
+ features, with an overview of how they are implemented:
63
+
64
+ ## Selection of rows, columns and cells. Whether an item is selected or not is
65
+ stored in:
66
+
67
+ * rows: a `_select_selected` property which contains a boolean value of the
68
+ DataTables' `aoData` object for each row
69
+ * columns: a `_select_selected` property which contains a boolean value of the
70
+ DataTables' `aoColumns` object for each column
71
+ * cells: a `_selected_cells` property which contains an array of boolean values
72
+ of the `aoData` object for each row. The array is the same length as the
73
+ columns array, with each element of it representing a cell.
74
+
75
+ This method of using boolean flags allows Select to operate when nodes have not
76
+ been created for rows / cells (DataTables' defer rendering feature).
77
+
78
+ ## API methods
79
+
80
+ A range of API methods are available for triggering selection and de-selection
81
+ of rows. Methods are also available to configure the selection events that can
82
+ be triggered by an end user (such as which items are to be selected). To a large
83
+ extent, these of API methods *is* Select. It is basically a collection of helper
84
+ functions that can be used to select items in a DataTable.
85
+
86
+ Configuration of select is held in the object `_select` which is attached to the
87
+ DataTables settings object on initialisation. Select being available on a table
88
+ is not optional when Select is loaded, but its default is for selection only to
89
+ be available via the API - so the end user wouldn't be able to select rows
90
+ without additional configuration.
91
+
92
+ The `_select` object contains the following properties:
93
+
94
+ ```
95
+ {
96
+ items:string - Can be `rows`, `columns` or `cells`. Defines what item
97
+ will be selected if the user is allowed to activate row
98
+ selection using the mouse.
99
+ style:string - Can be `none`, `single`, `multi` or `os`. Defines the
100
+ interaction style when selecting items
101
+ blurable:boolean - If row selection can be cleared by clicking outside of
102
+ the table
103
+ info:boolean - If the selection summary should be shown in the table
104
+ information elements
105
+ }
106
+ ```
107
+
108
+ In addition to the API methods, Select also extends the DataTables selector
109
+ options for rows, columns and cells adding a `selected` option to the selector
110
+ options object, allowing the developer to select only selected items or
111
+ unselected items.
112
+
113
+ ## Mouse selection of items
114
+
115
+ Clicking on items can be used to select items. This is done by a simple event
116
+ handler that will select the items using the API methods.
117
+
118
+ */
119
+
120
+
121
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
122
+ * Local functions
123
+ */
124
+
125
+ /**
126
+ * Add one or more cells to the selection when shift clicking in OS selection
127
+ * style cell selection.
128
+ *
129
+ * Cell range is more complicated than row and column as we want to select
130
+ * in the visible grid rather than by index in sequence. For example, if you
131
+ * click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1
132
+ * should also be selected (and not 1-3, 1-4. etc)
133
+ *
134
+ * @param {DataTable.Api} dt DataTable
135
+ * @param {object} idx Cell index to select to
136
+ * @param {object} last Cell index to select from
137
+ * @private
138
+ */
139
+ function cellRange( dt, idx, last )
140
+ {
141
+ var indexes;
142
+ var columnIndexes;
143
+ var rowIndexes;
144
+ var selectColumns = function ( start, end ) {
145
+ if ( start > end ) {
146
+ var tmp = end;
147
+ end = start;
148
+ start = tmp;
149
+ }
150
+
151
+ var record = false;
152
+ return dt.columns( ':visible' ).indexes().filter( function (i) {
153
+ if ( i === start ) {
154
+ record = true;
155
+ }
156
+
157
+ if ( i === end ) { // not else if, as start might === end
158
+ record = false;
159
+ return true;
160
+ }
161
+
162
+ return record;
163
+ } );
164
+ };
165
+
166
+ var selectRows = function ( start, end ) {
167
+ var indexes = dt.rows( { search: 'applied' } ).indexes();
168
+
169
+ // Which comes first - might need to swap
170
+ if ( indexes.indexOf( start ) > indexes.indexOf( end ) ) {
171
+ var tmp = end;
172
+ end = start;
173
+ start = tmp;
174
+ }
175
+
176
+ var record = false;
177
+ return indexes.filter( function (i) {
178
+ if ( i === start ) {
179
+ record = true;
180
+ }
181
+
182
+ if ( i === end ) {
183
+ record = false;
184
+ return true;
185
+ }
186
+
187
+ return record;
188
+ } );
189
+ };
190
+
191
+ if ( ! dt.cells( { selected: true } ).any() && ! last ) {
192
+ // select from the top left cell to this one
193
+ columnIndexes = selectColumns( 0, idx.column );
194
+ rowIndexes = selectRows( 0 , idx.row );
195
+ }
196
+ else {
197
+ // Get column indexes between old and new
198
+ columnIndexes = selectColumns( last.column, idx.column );
199
+ rowIndexes = selectRows( last.row , idx.row );
200
+ }
201
+
202
+ indexes = dt.cells( rowIndexes, columnIndexes ).flatten();
203
+
204
+ if ( ! dt.cells( idx, { selected: true } ).any() ) {
205
+ // Select range
206
+ dt.cells( indexes ).select();
207
+ }
208
+ else {
209
+ // Deselect range
210
+ dt.cells( indexes ).deselect();
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Disable mouse selection by removing the selectors
216
+ *
217
+ * @param {DataTable.Api} dt DataTable to remove events from
218
+ * @private
219
+ */
220
+ function disableMouseSelection( dt )
221
+ {
222
+ var ctx = dt.settings()[0];
223
+ var selector = ctx._select.selector;
224
+
225
+ $( dt.table().body() )
226
+ .off( 'mousedown.dtSelect', selector )
227
+ .off( 'mouseup.dtSelect', selector )
228
+ .off( 'click.dtSelect', selector );
229
+
230
+ $('body').off( 'click.dtSelect' );
231
+ }
232
+
233
+ /**
234
+ * Attach mouse listeners to the table to allow mouse selection of items
235
+ *
236
+ * @param {DataTable.Api} dt DataTable to remove events from
237
+ * @private
238
+ */
239
+ function enableMouseSelection ( dt )
240
+ {
241
+ var body = $( dt.table().body() );
242
+ var ctx = dt.settings()[0];
243
+ var selector = ctx._select.selector;
244
+
245
+ body
246
+ .on( 'mousedown.dtSelect', selector, function(e) {
247
+ // Disallow text selection for shift clicking on the table so multi
248
+ // element selection doesn't look terrible!
249
+ if ( e.shiftKey ) {
250
+ body
251
+ .css( '-moz-user-select', 'none' )
252
+ .one('selectstart.dtSelect', selector, function () {
253
+ return false;
254
+ } );
255
+ }
256
+ } )
257
+ .on( 'mouseup.dtSelect', selector, function(e) {
258
+ // Allow text selection to occur again, Mozilla style (tested in FF
259
+ // 35.0.1 - still required)
260
+ body.css( '-moz-user-select', '' );
261
+ } )
262
+ .on( 'click.dtSelect', selector, function ( e ) {
263
+ var items = dt.select.items();
264
+ var idx;
265
+
266
+ var ctx = dt.settings()[0];
267
+
268
+ // Ignore clicks inside a sub-table
269
+ if ( $(e.target).closest('tbody')[0] != body[0] ) {
270
+ return;
271
+ }
272
+
273
+ var cell = $(e.target).closest('td, th');
274
+ var cellIndex = dt.cell( cell ).index();
275
+
276
+ // Check the cell actually belongs to the host DataTable (so child rows,
277
+ // etc, are ignored)
278
+ if ( ! dt.cell( cell ).any() ) {
279
+ return;
280
+ }
281
+
282
+ if ( items === 'row' ) {
283
+ idx = cellIndex.row;
284
+ typeSelect( e, dt, ctx, 'row', idx );
285
+ }
286
+ else if ( items === 'column' ) {
287
+ idx = dt.cell( cell ).index().column;
288
+ typeSelect( e, dt, ctx, 'column', idx );
289
+ }
290
+ else if ( items === 'cell' ) {
291
+ idx = dt.cell( cell ).index();
292
+ typeSelect( e, dt, ctx, 'cell', idx );
293
+ }
294
+
295
+ ctx._select_lastCell = cellIndex;
296
+ } );
297
+
298
+ // Blurable
299
+ $('body').on( 'click.dtSelect', function ( e ) {
300
+ if ( ctx._select.blurable ) {
301
+ // If the click was inside the DataTables container, don't blur
302
+ if ( $(e.target).parents().filter( dt.table().container() ).length ) {
303
+ return;
304
+ }
305
+
306
+ // Don't blur in Editor form
307
+ if ( $(e.target).parents('div.DTE').length ) {
308
+ return;
309
+ }
310
+
311
+ clear( ctx, true );
312
+ }
313
+ } );
314
+ }
315
+
316
+ /**
317
+ * Trigger an event on a DataTable
318
+ *
319
+ * @param {DataTable.Api} api DataTable to trigger events on
320
+ * @param {boolean} selected true if selected, false if deselected
321
+ * @param {string} type Item type acting on
322
+ * @param {boolean} any Require that there are values before
323
+ * triggering
324
+ * @private
325
+ */
326
+ function eventTrigger ( api, type, args, any )
327
+ {
328
+ if ( any && ! api.flatten().length ) {
329
+ return;
330
+ }
331
+
332
+ args.unshift( api );
333
+
334
+ $(api.table().node()).triggerHandler( type+'.dt', args );
335
+ }
336
+
337
+ /**
338
+ * Update the information element of the DataTable showing information about the
339
+ * items selected. This is done by adding tags to the existing text
340
+ *
341
+ * @param {DataTable.Api} api DataTable to update
342
+ * @private
343
+ */
344
+ function info ( api )
345
+ {
346
+ var ctx = api.settings()[0];
347
+
348
+ if ( ! ctx._select.info || ! ctx.aanFeatures.i ) {
349
+ return;
350
+ }
351
+
352
+ var output = $('<span class="select-info"/>');
353
+ var add = function ( name, num ) {
354
+ output.append( $('<span class="select-item"/>').append( api.i18n(
355
+ 'select.'+name+'s',
356
+ { _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' },
357
+ num
358
+ ) ) );
359
+ };
360
+
361
+ add( 'row', api.rows( { selected: true } ).flatten().length );
362
+ add( 'column', api.columns( { selected: true } ).flatten().length );
363
+ add( 'cell', api.cells( { selected: true } ).flatten().length );
364
+
365
+ // Internal knowledge of DataTables to loop over all information elements
366
+ $.each( ctx.aanFeatures.i, function ( i, el ) {
367
+ el = $(el);
368
+
369
+ var exisiting = el.children('span.select-info');
370
+ if ( exisiting.length ) {
371
+ exisiting.remove();
372
+ }
373
+
374
+ if ( output.text() !== '' ) {
375
+ el.append( output );
376
+ }
377
+ } );
378
+ }
379
+
380
+ /**
381
+ * Initialisation of a new table. Attach event handlers and callbacks to allow
382
+ * Select to operate correctly.
383
+ *
384
+ * This will occur _after_ the initial DataTables initialisation, although
385
+ * before Ajax data is rendered, if there is ajax data
386
+ *
387
+ * @param {DataTable.settings} ctx Settings object to operate on
388
+ * @private
389
+ */
390
+ function init ( ctx ) {
391
+ var api = new DataTable.Api( ctx );
392
+
393
+ // Row callback so that classes can be added to rows and cells if the item
394
+ // was selected before the element was created. This will happen with the
395
+ // `deferRender` option enabled.
396
+ //
397
+ // This method of attaching to `aoRowCreatedCallback` is a hack until
398
+ // DataTables has proper events for row manipulation If you are reviewing
399
+ // this code to create your own plug-ins, please do not do this!
400
+ ctx.aoRowCreatedCallback.push( {
401
+ fn: function ( row, data, index ) {
402
+ var i, ien;
403
+ var d = ctx.aoData[ index ];
404
+
405
+ // Row
406
+ if ( d._select_selected ) {
407
+ $( row ).addClass( ctx._select.className );
408
+ }
409
+
410
+ // Cells and columns - if separated out, we would need to do two
411
+ // loops, so it makes sense to combine them into a single one
412
+ for ( i=0, ien=ctx.aoColumns.length ; i<ien ; i++ ) {
413
+ if ( ctx.aoColumns[i]._select_selected || (d._selected_cells && d._selected_cells[i]) ) {
414
+ $(d.anCells[i]).addClass( ctx._select.className );
415
+ }
416
+ }
417
+ },
418
+ sName: 'select-deferRender'
419
+ } );
420
+
421
+ // On Ajax reload we want to reselect all rows which are currently selected,
422
+ // if there is an rowId (i.e. a unique value to identify each row with)
423
+ api.on( 'preXhr.dt.dtSelect', function () {
424
+ // note that column selection doesn't need to be cached and then
425
+ // reselected, as they are already selected
426
+ var rows = api.rows( { selected: true } ).ids( true ).filter( function ( d ) {
427
+ return d !== undefined;
428
+ } );
429
+
430
+ var cells = api.cells( { selected: true } ).eq(0).map( function ( cellIdx ) {
431
+ var id = api.row( cellIdx.row ).id( true );
432
+ return id ?
433
+ { row: id, column: cellIdx.column } :
434
+ undefined;
435
+ } ).filter( function ( d ) {
436
+ return d !== undefined;
437
+ } );
438
+
439
+ // On the next draw, reselect the currently selected items
440
+ api.one( 'draw.dt.dtSelect', function () {
441
+ api.rows( rows ).select();
442
+
443
+ // `cells` is not a cell index selector, so it needs a loop
444
+ if ( cells.any() ) {
445
+ cells.each( function ( id ) {
446
+ api.cells( id.row, id.column ).select();
447
+ } );
448
+ }
449
+ } );
450
+ } );
451
+
452
+ // Update the table information element with selected item summary
453
+ api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt', function () {
454
+ info( api );
455
+ } );
456
+
457
+ // Clean up and release
458
+ api.on( 'destroy.dtSelect', function () {
459
+ disableMouseSelection( api );
460
+ api.off( '.dtSelect' );
461
+ } );
462
+ }
463
+
464
+ /**
465
+ * Add one or more items (rows or columns) to the selection when shift clicking
466
+ * in OS selection style
467
+ *
468
+ * @param {DataTable.Api} dt DataTable
469
+ * @param {string} type Row or column range selector
470
+ * @param {object} idx Item index to select to
471
+ * @param {object} last Item index to select from
472
+ * @private
473
+ */
474
+ function rowColumnRange( dt, type, idx, last )
475
+ {
476
+ // Add a range of rows from the last selected row to this one
477
+ var indexes = dt[type+'s']( { search: 'applied' } ).indexes();
478
+ var idx1 = $.inArray( last, indexes );
479
+ var idx2 = $.inArray( idx, indexes );
480
+
481
+ if ( ! dt[type+'s']( { selected: true } ).any() && idx1 === -1 ) {
482
+ // select from top to here - slightly odd, but both Windows and Mac OS
483
+ // do this
484
+ indexes.splice( $.inArray( idx, indexes )+1, indexes.length );
485
+ }
486
+ else {
487
+ // reverse so we can shift click 'up' as well as down
488
+ if ( idx1 > idx2 ) {
489
+ var tmp = idx2;
490
+ idx2 = idx1;
491
+ idx1 = tmp;
492
+ }
493
+
494
+ indexes.splice( idx2+1, indexes.length );
495
+ indexes.splice( 0, idx1 );
496
+ }
497
+
498
+ if ( ! dt[type]( idx, { selected: true } ).any() ) {
499
+ // Select range
500
+ dt[type+'s']( indexes ).select();
501
+ }
502
+ else {
503
+ // Deselect range - need to keep the clicked on row selected
504
+ indexes.splice( $.inArray( idx, indexes ), 1 );
505
+ dt[type+'s']( indexes ).deselect();
506
+ }
507
+ }
508
+
509
+ /**
510
+ * Clear all selected items
511
+ *
512
+ * @param {DataTable.settings} ctx Settings object of the host DataTable
513
+ * @param {boolean} [force=false] Force the de-selection to happen, regardless
514
+ * of selection style
515
+ * @private
516
+ */
517
+ function clear( ctx, force )
518
+ {
519
+ if ( force || ctx._select.style === 'single' ) {
520
+ var api = new DataTable.Api( ctx );
521
+
522
+ api.rows( { selected: true } ).deselect();
523
+ api.columns( { selected: true } ).deselect();
524
+ api.cells( { selected: true } ).deselect();
525
+ }
526
+ }
527
+
528
+ /**
529
+ * Select items based on the current configuration for style and items.
530
+ *
531
+ * @param {object} e Mouse event object
532
+ * @param {DataTables.Api} dt DataTable
533
+ * @param {DataTable.settings} ctx Settings object of the host DataTable
534
+ * @param {string} type Items to select
535
+ * @param {int|object} idx Index of the item to select
536
+ * @private
537
+ */
538
+ function typeSelect ( e, dt, ctx, type, idx )
539
+ {
540
+ var style = dt.select.style();
541
+ var isSelected = dt[type]( idx, { selected: true } ).any();
542
+
543
+ if ( style === 'os' ) {
544
+ if ( e.ctrlKey || e.metaKey ) {
545
+ // Add or remove from the selection
546
+ dt[type]( idx ).select( ! isSelected );
547
+ }
548
+ else if ( e.shiftKey ) {
549
+ if ( type === 'cell' ) {
550
+ cellRange( dt, idx, ctx._select_lastCell || null );
551
+ }
552
+ else {
553
+ rowColumnRange( dt, type, idx, ctx._select_lastCell ?
554
+ ctx._select_lastCell[type] :
555
+ null
556
+ );
557
+ }
558
+ }
559
+ else {
560
+ // No cmd or shift click - deselect if selected, or select
561
+ // this row only
562
+ var selected = dt[type+'s']( { selected: true } );
563
+
564
+ if ( isSelected && selected.flatten().length === 1 ) {
565
+ dt[type]( idx ).deselect();
566
+ }
567
+ else {
568
+ selected.deselect();
569
+ dt[type]( idx ).select();
570
+ }
571
+ }
572
+ }
573
+ else {
574
+ dt[ type ]( idx ).select( ! isSelected );
575
+ }
576
+ }
577
+
578
+
579
+
580
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
581
+ * DataTables selectors
582
+ */
583
+
584
+ // row and column are basically identical just assigned to different properties
585
+ // and checking a different array, so we can dynamically create the functions to
586
+ // reduce the code size
587
+ $.each( [
588
+ { type: 'row', prop: 'aoData' },
589
+ { type: 'column', prop: 'aoColumns' }
590
+ ], function ( i, o ) {
591
+ DataTable.ext.selector[ o.type ].push( function ( settings, opts, indexes ) {
592
+ var selected = opts.selected;
593
+ var data;
594
+ var out = [];
595
+
596
+ if ( selected === undefined ) {
597
+ return indexes;
598
+ }
599
+
600
+ for ( var i=0, ien=indexes.length ; i<ien ; i++ ) {
601
+ data = settings[ o.prop ][ indexes[i] ];
602
+
603
+ if ( (selected === true && data._select_selected === true) ||
604
+ (selected === false && ! data._select_selected )
605
+ ) {
606
+ out.push( indexes[i] );
607
+ }
608
+ }
609
+
610
+ return out;
611
+ } );
612
+ } );
613
+
614
+ DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
615
+ var selected = opts.selected;
616
+ var rowData;
617
+ var out = [];
618
+
619
+ if ( selected === undefined ) {
620
+ return cells;
621
+ }
622
+
623
+ for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
624
+ rowData = settings.aoData[ cells[i].row ];
625
+
626
+ if ( (selected === true && rowData._selected_cells && rowData._selected_cells[ cells[i].column ] === true) ||
627
+ (selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) )
628
+ ) {
629
+ out.push( cells[i] );
630
+ }
631
+ }
632
+
633
+ return out;
634
+ } );
635
+
636
+
637
+
638
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
639
+ * DataTables API
640
+ *
641
+ * For complete documentation, please refer to the docs/api directory or the
642
+ * DataTables site
643
+ */
644
+
645
+ // Local variables to improve compression
646
+ var apiRegister = DataTable.Api.register;
647
+ var apiRegisterPlural = DataTable.Api.registerPlural;
648
+
649
+ apiRegister( 'select()', function () {} );
650
+
651
+ apiRegister( 'select.blurable()', function ( flag ) {
652
+ if ( flag === undefined ) {
653
+ return this.context[0]._select.blurable;
654
+ }
655
+
656
+ return this.iterator( 'table', function ( ctx ) {
657
+ ctx._select.blurable = flag;
658
+ } );
659
+ } );
660
+
661
+ apiRegister( 'select.info()', function ( flag ) {
662
+ if ( info === undefined ) {
663
+ return this.context[0]._select.info;
664
+ }
665
+
666
+ return this.iterator( 'table', function ( ctx ) {
667
+ ctx._select.info = flag;
668
+ } );
669
+ } );
670
+
671
+ apiRegister( 'select.items()', function ( items ) {
672
+ if ( items === undefined ) {
673
+ return this.context[0]._select.items;
674
+ }
675
+
676
+ return this.iterator( 'table', function ( ctx ) {
677
+ ctx._select.items = items;
678
+
679
+ eventTrigger( new DataTable.Api( ctx ), 'selectItems', [ items ] );
680
+ } );
681
+ } );
682
+
683
+ // Takes effect from the _next_ selection. None disables future selection, but
684
+ // does not clear the current selection. Use the `deselect` methods for that
685
+ apiRegister( 'select.style()', function ( style ) {
686
+ if ( style === undefined ) {
687
+ return this.context[0]._select.style;
688
+ }
689
+
690
+ return this.iterator( 'table', function ( ctx ) {
691
+ ctx._select.style = style;
692
+
693
+ if ( ! ctx._select_init ) {
694
+ init( ctx );
695
+ }
696
+
697
+ // Add / remove mouse event handlers. They aren't required when only
698
+ // API selection is available
699
+ var dt = new DataTable.Api( ctx );
700
+ disableMouseSelection( dt );
701
+
702
+ if ( style !== 'api' ) {
703
+ enableMouseSelection( dt );
704
+ }
705
+
706
+ eventTrigger( new DataTable.Api( ctx ), 'selectStyle', [ style ] );
707
+ } );
708
+ } );
709
+
710
+ apiRegister( 'select.selector()', function ( selector ) {
711
+ if ( selector === undefined ) {
712
+ return this.context[0]._select.selector;
713
+ }
714
+
715
+ return this.iterator( 'table', function ( ctx ) {
716
+ disableMouseSelection( new DataTable.Api( ctx ) );
717
+
718
+ ctx._select.selector = selector;
719
+
720
+ if ( ctx._select.style !== 'api' ) {
721
+ enableMouseSelection( new DataTable.Api( ctx ) );
722
+ }
723
+ } );
724
+ } );
725
+
726
+
727
+
728
+ apiRegisterPlural( 'rows().select()', 'row().select()', function ( select ) {
729
+ var api = this;
730
+
731
+ if ( select === false ) {
732
+ return this.deselect();
733
+ }
734
+
735
+ this.iterator( 'row', function ( ctx, idx ) {
736
+ clear( ctx );
737
+
738
+ ctx.aoData[ idx ]._select_selected = true;
739
+ $( ctx.aoData[ idx ].nTr ).addClass( ctx._select.className );
740
+ } );
741
+
742
+ this.iterator( 'table', function ( ctx, i ) {
743
+ eventTrigger( api, 'select', [ 'row', api[i] ], true );
744
+ } );
745
+
746
+ return this;
747
+ } );
748
+
749
+ apiRegisterPlural( 'columns().select()', 'column().select()', function ( select ) {
750
+ var api = this;
751
+
752
+ if ( select === false ) {
753
+ return this.deselect();
754
+ }
755
+
756
+ this.iterator( 'column', function ( ctx, idx ) {
757
+ clear( ctx );
758
+
759
+ ctx.aoColumns[ idx ]._select_selected = true;
760
+
761
+ var column = new DataTable.Api( ctx ).column( idx );
762
+
763
+ $( column.header() ).addClass( ctx._select.className );
764
+ $( column.footer() ).addClass( ctx._select.className );
765
+
766
+ column.nodes().to$().addClass( ctx._select.className );
767
+ } );
768
+
769
+ this.iterator( 'table', function ( ctx, i ) {
770
+ eventTrigger( api, 'select', [ 'column', api[i] ], true );
771
+ } );
772
+
773
+ return this;
774
+ } );
775
+
776
+ apiRegisterPlural( 'cells().select()', 'cell().select()', function ( select ) {
777
+ var api = this;
778
+
779
+ if ( select === false ) {
780
+ return this.deselect();
781
+ }
782
+
783
+ this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
784
+ clear( ctx );
785
+
786
+ var data = ctx.aoData[ rowIdx ];
787
+
788
+ if ( data._selected_cells === undefined ) {
789
+ data._selected_cells = [];
790
+ }
791
+
792
+ data._selected_cells[ colIdx ] = true;
793
+
794
+ if ( data.anCells ) {
795
+ $( data.anCells[ colIdx ] ).addClass( ctx._select.className );
796
+ }
797
+ } );
798
+
799
+ this.iterator( 'table', function ( ctx, i ) {
800
+ eventTrigger( api, 'select', [ 'cell', api[i] ], true );
801
+ } );
802
+
803
+ return this;
804
+ } );
805
+
806
+
807
+ apiRegisterPlural( 'rows().deselect()', 'row().deselect()', function () {
808
+ var api = this;
809
+
810
+ this.iterator( 'row', function ( ctx, idx ) {
811
+ ctx.aoData[ idx ]._select_selected = false;
812
+ $( ctx.aoData[ idx ].nTr ).removeClass( ctx._select.className );
813
+ } );
814
+
815
+ this.iterator( 'table', function ( ctx, i ) {
816
+ eventTrigger( api, 'deselect', [ 'row', api[i] ], true );
817
+ } );
818
+
819
+ return this;
820
+ } );
821
+
822
+ apiRegisterPlural( 'columns().deselect()', 'column().deselect()', function () {
823
+ var api = this;
824
+
825
+ this.iterator( 'column', function ( ctx, idx ) {
826
+ ctx.aoColumns[ idx ]._select_selected = false;
827
+
828
+ var api = new DataTable.Api( ctx );
829
+ var column = api.column( idx );
830
+
831
+ $( column.header() ).removeClass( ctx._select.className );
832
+ $( column.footer() ).removeClass( ctx._select.className );
833
+
834
+ // Need to loop over each cell, rather than just using
835
+ // `column().nodes()` as cells which are individually selected should
836
+ // not have the `selected` class removed from them
837
+ api.cells( null, idx ).indexes().each( function (cellIdx) {
838
+ var data = ctx.aoData[ cellIdx.row ];
839
+ var cellSelected = data._selected_cells;
840
+
841
+ if ( data.anCells && (! cellSelected || ! cellSelected[ cellIdx.column ]) ) {
842
+ $( data.anCells[ cellIdx.column ] ).removeClass( ctx._select.className );
843
+ }
844
+ } );
845
+ } );
846
+
847
+ this.iterator( 'table', function ( ctx, i ) {
848
+ eventTrigger( api, 'deselect', [ 'column', api[i] ], true );
849
+ } );
850
+
851
+ return this;
852
+ } );
853
+
854
+ apiRegisterPlural( 'cells().deselect()', 'cell().deselect()', function () {
855
+ var api = this;
856
+
857
+ this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
858
+ var data = ctx.aoData[ rowIdx ];
859
+
860
+ data._selected_cells[ colIdx ] = false;
861
+
862
+ // Remove class only if the cells exist, and the cell is not column
863
+ // selected, in which case the class should remain (since it is selected
864
+ // in the column)
865
+ if ( data.anCells && ! ctx.aoColumns[ colIdx ]._select_selected ) {
866
+ $( data.anCells[ colIdx ] ).removeClass( ctx._select.className );
867
+ }
868
+ } );
869
+
870
+ this.iterator( 'table', function ( ctx, i ) {
871
+ eventTrigger( api, 'deselect', [ 'cell', api[i] ], true );
872
+ } );
873
+
874
+ return this;
875
+ } );
876
+
877
+
878
+
879
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
880
+ * Buttons
881
+ */
882
+ function i18n( label, def ) {
883
+ return function (dt) {
884
+ return dt.i18n( 'buttons.'+label, def );
885
+ };
886
+ }
887
+
888
+ $.extend( DataTable.ext.buttons, {
889
+ selected: {
890
+ text: i18n( 'selected', 'Selected' ),
891
+ className: 'buttons-selected',
892
+ init: function ( dt, button, config ) {
893
+ var that = this;
894
+
895
+ // .DT namespace listeners are removed by DataTables automatically
896
+ // on table destroy
897
+ dt.on( 'draw.dt.DT select.dt.DT deselect.dt.DT', function () {
898
+ var enable = that.rows( { selected: true } ).any() ||
899
+ that.columns( { selected: true } ).any() ||
900
+ that.cells( { selected: true } ).any();
901
+
902
+ that.enable( enable );
903
+ } );
904
+
905
+ this.disable();
906
+ }
907
+ },
908
+ selectedSingle: {
909
+ text: i18n( 'selectedSingle', 'Selected single' ),
910
+ className: 'buttons-selected-single',
911
+ init: function ( dt, button, config ) {
912
+ var that = this;
913
+
914
+ dt.on( 'draw.dt.DT select.dt.DT deselect.dt.DT', function () {
915
+ var count = dt.rows( { selected: true } ).flatten().length +
916
+ dt.columns( { selected: true } ).flatten().length +
917
+ dt.cells( { selected: true } ).flatten().length;
918
+
919
+ that.enable( count === 1 );
920
+ } );
921
+
922
+ this.disable();
923
+ }
924
+ },
925
+ selectAll: {
926
+ text: i18n( 'selectAll', 'Select all' ),
927
+ className: 'buttons-select-all',
928
+ action: function () {
929
+ var items = this.select.items();
930
+ this[ items+'s' ]().select();
931
+ }
932
+ },
933
+ selectNone: {
934
+ text: i18n( 'selectNone', 'Deselect all' ),
935
+ className: 'buttons-select-none',
936
+ action: function () {
937
+ clear( this.settings()[0], true );
938
+ }
939
+ }
940
+ } );
941
+
942
+ $.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) {
943
+ var lc = item.toLowerCase();
944
+
945
+ DataTable.ext.buttons[ 'select'+item+'s' ] = {
946
+ text: i18n( 'select'+item+'s', 'Select '+lc+'s' ),
947
+ className: 'buttons-select-'+lc+'s',
948
+ action: function () {
949
+ this.select.items( lc );
950
+ },
951
+ init: function ( dt, button, config ) {
952
+ var that = this;
953
+
954
+ dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) {
955
+ that.active( items === lc );
956
+ } );
957
+ }
958
+ };
959
+ } );
960
+
961
+
962
+
963
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
964
+ * Initialisation
965
+ */
966
+
967
+ // DataTables creation - check if the buttons have been defined for this table,
968
+ // they will have been if the `B` option was used in `dom`, otherwise we should
969
+ // create the buttons instance here so they can be inserted into the document
970
+ // using the API
971
+ $(document).on( 'preInit.dt.dtSelect', function (e, ctx, json) {
972
+ if ( e.namespace !== 'dt' ) {
973
+ return;
974
+ }
975
+
976
+ var opts = ctx.oInit.select || DataTable.defaults.select;
977
+ var dt = new DataTable.Api( ctx );
978
+
979
+ // Set defaults
980
+ var items = 'row';
981
+ var style = 'api';
982
+ var blurable = false;
983
+ var info = true;
984
+ var selector = 'td, th';
985
+ var className = 'selected';
986
+
987
+ ctx._select = {};
988
+
989
+ // Initialisation customisations
990
+ if ( opts === true ) {
991
+ style = 'os';
992
+ }
993
+ else if ( typeof opts === 'string' ) {
994
+ style = opts;
995
+ }
996
+ else if ( $.isPlainObject( opts ) ) {
997
+ if ( opts.blurable !== undefined ) {
998
+ blurable = opts.blurable;
999
+ }
1000
+
1001
+ if ( opts.info !== undefined ) {
1002
+ info = opts.info;
1003
+ }
1004
+
1005
+ if ( opts.items !== undefined ) {
1006
+ items = opts.items;
1007
+ }
1008
+
1009
+ if ( opts.style !== undefined ) {
1010
+ style = opts.style;
1011
+ }
1012
+
1013
+ if ( opts.selector !== undefined ) {
1014
+ selector = opts.selector;
1015
+ }
1016
+
1017
+ if ( opts.className !== undefined ) {
1018
+ className = opts.className;
1019
+ }
1020
+ }
1021
+
1022
+ dt.select.selector( selector );
1023
+ dt.select.items( items );
1024
+ dt.select.style( style );
1025
+ dt.select.blurable( blurable );
1026
+ dt.select.info( info );
1027
+ ctx._select.className = className;
1028
+
1029
+ // If the init options haven't enabled select, but there is a selectable
1030
+ // class name, then enable
1031
+ if ( $( dt.table().node() ).hasClass( 'selectable' ) ) {
1032
+ dt.select.style( 'os' );
1033
+ }
1034
+ } );
1035
+
1036
+
1037
+ return DataTable.select;
1038
+ }));