jquery-datatables-rails 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }));