datatables.net-select 2.0.5 → 3.0.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.
@@ -1,4 +1,4 @@
1
- /*! Select for DataTables 2.0.5
1
+ /*! Select for DataTables 3.0.0
2
2
  * © SpryMedia Ltd - datatables.net/license/mit
3
3
  */
4
4
 
@@ -56,7 +56,7 @@ DataTable.select.classes = {
56
56
  checkbox: 'dt-select-checkbox'
57
57
  };
58
58
 
59
- DataTable.select.version = '2.0.5';
59
+ DataTable.select.version = '3.0.0';
60
60
 
61
61
  DataTable.select.init = function (dt) {
62
62
  var ctx = dt.settings()[0];
@@ -126,11 +126,13 @@ DataTable.select.init = function (dt) {
126
126
  var style = 'api';
127
127
  var blurable = false;
128
128
  var toggleable = true;
129
+ var selectable = null;
129
130
  var info = true;
130
131
  var selector = 'td, th';
131
132
  var className = 'selected';
132
133
  var headerCheckbox = true;
133
134
  var setStyle = false;
135
+ var keys = false;
134
136
 
135
137
  ctx._select = {
136
138
  infoEls: []
@@ -182,6 +184,14 @@ DataTable.select.init = function (dt) {
182
184
  if (opts.headerCheckbox !== undefined) {
183
185
  headerCheckbox = opts.headerCheckbox;
184
186
  }
187
+
188
+ if (opts.selectable !== undefined) {
189
+ selectable = opts.selectable;
190
+ }
191
+
192
+ if (opts.keys !== undefined) {
193
+ keys = opts.keys;
194
+ }
185
195
  }
186
196
 
187
197
  dt.select.selector(selector);
@@ -190,6 +200,8 @@ DataTable.select.init = function (dt) {
190
200
  dt.select.blurable(blurable);
191
201
  dt.select.toggleable(toggleable);
192
202
  dt.select.info(info);
203
+ dt.select.keys(keys);
204
+ dt.select.selectable(selectable);
193
205
  ctx._select.className = className;
194
206
 
195
207
  // If the init options haven't enabled select, but there is a selectable
@@ -199,11 +211,9 @@ DataTable.select.init = function (dt) {
199
211
  }
200
212
 
201
213
  // Insert a checkbox into the header if needed - might need to wait
202
- // for init complete, or it might already be done
214
+ // for init complete
203
215
  if (headerCheckbox || headerCheckbox === 'select-page' || headerCheckbox === 'select-all') {
204
- initCheckboxHeader(dt, headerCheckbox);
205
-
206
- dt.on('init', function () {
216
+ dt.ready(function () {
207
217
  initCheckboxHeader(dt, headerCheckbox);
208
218
  });
209
219
  }
@@ -570,9 +580,17 @@ function info(api, node) {
570
580
  return;
571
581
  }
572
582
 
573
- var rows = api.rows({ selected: true }).flatten().length;
574
- var columns = api.columns({ selected: true }).flatten().length;
575
- var cells = api.cells({ selected: true }).flatten().length;
583
+ var ctx = api.settings()[0];
584
+ var rowSetLength = ctx._select_set.length;
585
+ var rows = rowSetLength ? rowSetLength : api.rows({ selected: true }).count();
586
+ var columns = api.columns({ selected: true }).count();
587
+ var cells = api.cells({ selected: true }).count();
588
+
589
+ // If subtractive selection, then we need to take the number of rows and
590
+ // subtract those that have been deselected
591
+ if (ctx._select_mode === 'subtractive') {
592
+ rows = api.page.info().recordsDisplay - rowSetLength;
593
+ }
576
594
 
577
595
  var add = function (el, name, num) {
578
596
  el.append(
@@ -612,7 +630,8 @@ function info(api, node) {
612
630
  * @param {*} headerCheckbox the header checkbox option
613
631
  */
614
632
  function initCheckboxHeader( dt, headerCheckbox ) {
615
- var dtInternalColumns = dt.settings()[0].aoColumns;
633
+ var dtSettings = dt.settings()[0];
634
+ var dtInternalColumns = dtSettings.aoColumns;
616
635
 
617
636
  // Find any checkbox column(s)
618
637
  dt.columns().iterator('column', function (s, idx) {
@@ -636,33 +655,37 @@ function initCheckboxHeader( dt, headerCheckbox ) {
636
655
  .on('change', function () {
637
656
  if (this.checked) {
638
657
  if (headerCheckbox == 'select-page') {
639
- dt.rows({page: 'current'}).select()
640
- } else {
658
+ dt.rows({page: 'current'}).select();
659
+ }
660
+ else {
641
661
  dt.rows({search: 'applied'}).select();
642
662
  }
643
663
  }
644
664
  else {
645
- dt.rows({selected: true}).deselect();
665
+ if (headerCheckbox == 'select-page') {
666
+ dt.rows({page: 'current', selected: true}).deselect();
667
+ }
668
+ else {
669
+ dt.rows({selected: true}).deselect();
670
+ }
646
671
  }
647
672
  })
648
673
  .on('click', function (e) {
649
674
  e.stopPropagation();
650
675
  });
651
-
676
+
652
677
  // Update the header checkbox's state when the selection in the
653
678
  // table changes
654
679
  dt.on('draw select deselect', function (e, pass, type) {
655
680
  if (type === 'row' || ! type) {
656
- var count = dt.rows({selected: true}).count();
657
- var search = dt.rows({search: 'applied', selected: true}).count();
658
- var available = headerCheckbox == 'select-page' ? dt.rows({page: 'current'}).count() : dt.rows({search: 'applied'}).count();
681
+ var nums = headerCheckboxState(dt, headerCheckbox);
659
682
 
660
- if (search && search <= count && search === available) {
683
+ if (nums.search && nums.search <= nums.count && nums.search === nums.available) {
661
684
  input
662
685
  .prop('checked', true)
663
686
  .prop('indeterminate', false);
664
687
  }
665
- else if (search === 0 && count === 0) {
688
+ else if (nums.search === 0 && nums.count === 0) {
666
689
  input
667
690
  .prop('checked', false)
668
691
  .prop('indeterminate', false);
@@ -678,6 +701,188 @@ function initCheckboxHeader( dt, headerCheckbox ) {
678
701
  });
679
702
  }
680
703
 
704
+ function keysSet(dt) {
705
+ var ctx = dt.settings()[0];
706
+ var flag = ctx._select.keys;
707
+ var namespace = 'dts-keys-' + ctx.sTableId;
708
+
709
+ if (flag) {
710
+ // Need a tabindex of the `tr` elements to make them focusable by the browser
711
+ $(dt.rows({page: 'current'}).nodes()).attr('tabindex', 0);
712
+
713
+ dt.on('draw.' + namespace, function () {
714
+ $(dt.rows({page: 'current'}).nodes()).attr('tabindex', 0);
715
+ });
716
+
717
+ // Listen on document for tab, up and down
718
+ $(document).on('keydown.' + namespace, function (e) {
719
+ var key = e.keyCode;
720
+ var active = document.activeElement;
721
+
722
+ // Can't use e.key as it wasn't widely supported until 2017
723
+ // 9 Tab
724
+ // 13 Return
725
+ // 32 Space
726
+ // 38 ArrowUp
727
+ // 40 ArrowDown
728
+ if (! [9, 13, 32, 38, 40].includes(key)) {
729
+ return;
730
+ }
731
+
732
+ var nodes = dt.rows({page: 'current'}).nodes().toArray();
733
+ var idx = nodes.indexOf(active);
734
+ var preventDefault = true;
735
+
736
+ // Only take an action if a row has focus
737
+ if (idx === -1) {
738
+ return;
739
+ }
740
+
741
+ if (key === 9) {
742
+ // Tab focus change
743
+ if (e.shift === false && idx === nodes.length - 1) {
744
+ keysPageDown(dt);
745
+ }
746
+ else if (e.shift === true && idx === 0) {
747
+ keysPageUp(dt);
748
+ }
749
+ else {
750
+ // Browser will do it for us
751
+ preventDefault = false;
752
+ }
753
+ }
754
+ else if (key === 13 || key === 32) {
755
+ // Row selection / deselection
756
+ var row = dt.row(active);
757
+
758
+ if (row.selected()) {
759
+ row.deselect();
760
+ }
761
+ else {
762
+ row.select();
763
+ }
764
+ }
765
+ else if (key === 38) {
766
+ // Move up
767
+ if (idx > 0) {
768
+ nodes[idx-1].focus();
769
+ }
770
+ else {
771
+ keysPageUp(dt);
772
+ }
773
+ }
774
+ else {
775
+ // Move down
776
+ if (idx < nodes.length -1) {
777
+ nodes[idx+1].focus();
778
+ }
779
+ else {
780
+ keysPageDown(dt);
781
+ }
782
+ }
783
+
784
+ if (preventDefault) {
785
+ e.stopPropagation();
786
+ e.preventDefault();
787
+ }
788
+ });
789
+ }
790
+ else {
791
+ // Stop the rows from being able to gain focus
792
+ $(dt.rows().nodes()).removeAttr('tabindex');
793
+
794
+ // Nuke events
795
+ dt.off('draw.' + namespace);
796
+ $(document).off('keydown.' + namespace);
797
+ }
798
+ }
799
+
800
+ /**
801
+ * Change to the next page and focus on the first row
802
+ *
803
+ * @param {DataTable.Api} dt DataTable instance
804
+ */
805
+ function keysPageDown(dt) {
806
+ // Is there another page to turn to?
807
+ var info = dt.page.info();
808
+
809
+ if (info.page < info.pages - 1) {
810
+ dt
811
+ .one('draw', function () {
812
+ dt.row(':first-child').node().focus();
813
+ })
814
+ .page('next')
815
+ .draw(false);
816
+ }
817
+ }
818
+
819
+ /**
820
+ * Change to the previous page and focus on the last row
821
+ *
822
+ * @param {DataTable.Api} dt DataTable instance
823
+ */
824
+ function keysPageUp(dt) {
825
+ // Is there another page to turn to?
826
+ var info = dt.page.info();
827
+
828
+ if (info.page > 0) {
829
+ dt
830
+ .one('draw', function () {
831
+ dt.row(':last-child').node().focus();
832
+ })
833
+ .page('previous')
834
+ .draw(false);
835
+ }
836
+ }
837
+
838
+ /**
839
+ * Determine the counts used to define the header checkbox's state
840
+ *
841
+ * @param {*} dt DT API
842
+ * @param {*} headerCheckbox Configuration for what the header checkbox does
843
+ * @returns Counts object
844
+ */
845
+ function headerCheckboxState(dt, headerCheckbox) {
846
+ var ctx = dt.settings()[0];
847
+ var selectable = ctx._select.selectable;
848
+ var available = 0;
849
+ var count = headerCheckbox == 'select-page'
850
+ ? dt.rows({page: 'current', selected: true}).count()
851
+ : dt.rows({selected: true}).count();
852
+ var search = headerCheckbox == 'select-page'
853
+ ? dt.rows({page: 'current', selected: true}).count()
854
+ : dt.rows({search: 'applied', selected: true}).count();
855
+
856
+ if (! selectable) {
857
+ available = headerCheckbox == 'select-page'
858
+ ? dt.rows({page: 'current'}).count()
859
+ : dt.rows({search: 'applied'}).count();
860
+ }
861
+ else {
862
+ // Need to count how many rows are actually selectable to know if all selectable
863
+ // rows are selected or not
864
+ var indexes = headerCheckbox == 'select-page'
865
+ ? dt.rows({page: 'current'}).indexes()
866
+ : dt.rows({search: 'applied'}).indexes();
867
+
868
+ for (var i=0 ; i<indexes.length ; i++) {
869
+ // For speed I use the internal DataTables object.
870
+ var rowInternal = ctx.aoData[indexes[i]];
871
+ var result = selectable(rowInternal._aData, rowInternal.nTr, indexes[i]);
872
+
873
+ if (result) {
874
+ available++;
875
+ }
876
+ }
877
+ }
878
+
879
+ return {
880
+ available: available,
881
+ count: count,
882
+ search: search
883
+ }
884
+ }
885
+
681
886
  /**
682
887
  * Initialisation of a new table. Attach event handlers and callbacks to allow
683
888
  * Select to operate correctly.
@@ -692,6 +897,12 @@ function init(ctx) {
692
897
  var api = new DataTable.Api(ctx);
693
898
  ctx._select_init = true;
694
899
 
900
+ // When `additive` then `_select_set` contains a list of the row ids that
901
+ // are selected. If `subtractive` then all rows are selected, except those
902
+ // in `_select_set`, which is a list of ids.
903
+ ctx._select_mode = 'additive';
904
+ ctx._select_set = [];
905
+
695
906
  // Row callback so that classes can be added to rows and cells if the item
696
907
  // was selected before the element was created. This will happen with the
697
908
  // `deferRender` option enabled.
@@ -702,9 +913,16 @@ function init(ctx) {
702
913
  ctx.aoRowCreatedCallback.push(function (row, data, index) {
703
914
  var i, ien;
704
915
  var d = ctx.aoData[index];
916
+ var id = api.row(index).id();
705
917
 
706
918
  // Row
707
- if (d._select_selected) {
919
+ if (
920
+ d._select_selected ||
921
+ (ctx._select_mode === 'additive' && ctx._select_set.includes(id)) ||
922
+ (ctx._select_mode === 'subtractive' && ! ctx._select_set.includes(id))
923
+ ) {
924
+ d._select_selected = true;
925
+
708
926
  $(row)
709
927
  .addClass(ctx._select.className)
710
928
  .find('input.' + checkboxClass(true)).prop('checked', true);
@@ -723,46 +941,7 @@ function init(ctx) {
723
941
  }
724
942
  );
725
943
 
726
- // On Ajax reload we want to reselect all rows which are currently selected,
727
- // if there is an rowId (i.e. a unique value to identify each row with)
728
- api.on('preXhr.dt.dtSelect', function (e, settings) {
729
- if (settings !== api.settings()[0]) {
730
- // Not triggered by our DataTable!
731
- return;
732
- }
733
-
734
- // note that column selection doesn't need to be cached and then
735
- // reselected, as they are already selected
736
- var rows = api
737
- .rows({ selected: true })
738
- .ids(true)
739
- .filter(function (d) {
740
- return d !== undefined;
741
- });
742
-
743
- var cells = api
744
- .cells({ selected: true })
745
- .eq(0)
746
- .map(function (cellIdx) {
747
- var id = api.row(cellIdx.row).id(true);
748
- return id ? { row: id, column: cellIdx.column } : undefined;
749
- })
750
- .filter(function (d) {
751
- return d !== undefined;
752
- });
753
-
754
- // On the next draw, reselect the currently selected items
755
- api.one('draw.dt.dtSelect', function () {
756
- api.rows(rows).select();
757
-
758
- // `cells` is not a cell index selector, so it needs a loop
759
- if (cells.any()) {
760
- cells.each(function (id) {
761
- api.cells(id.row, id.column).select();
762
- });
763
- }
764
- });
765
- });
944
+ _cumulativeEvents(api);
766
945
 
767
946
  // Update the table information element with selected item summary
768
947
  api.on('info.dt', function (e, ctx, node) {
@@ -935,6 +1114,73 @@ function _safeId(node) {
935
1114
  return node.id.replace(/[^a-zA-Z0-9\-\_]/g, '-');
936
1115
  }
937
1116
 
1117
+ /**
1118
+ * Set up event handlers for cumulative selection
1119
+ *
1120
+ * @param {*} api DT API instance
1121
+ */
1122
+ function _cumulativeEvents(api) {
1123
+ // Add event listeners to add / remove from the _select_set
1124
+ api.on('select', function (e, dt, type, indexes) {
1125
+ // Only support for rows at the moment
1126
+ if (type !== 'row') {
1127
+ return;
1128
+ }
1129
+
1130
+ var ctx = api.settings()[0];
1131
+
1132
+ if (ctx._select_mode === 'additive') {
1133
+ // Add row to the selection list if it isn't already there
1134
+ _add(api, ctx._select_set, indexes);
1135
+ }
1136
+ else {
1137
+ // Subtractive - if a row is selected it should not in the list
1138
+ // as in subtractive mode the list gives the rows which are not
1139
+ // selected
1140
+ _remove(api, ctx._select_set, indexes);
1141
+ }
1142
+ });
1143
+
1144
+ api.on('deselect', function (e, dt, type, indexes) {
1145
+ // Only support for rows at the moment
1146
+ if (type !== 'row') {
1147
+ return;
1148
+ }
1149
+
1150
+ var ctx = api.settings()[0];
1151
+
1152
+ if (ctx._select_mode === 'additive') {
1153
+ // List is of those rows selected, so remove it
1154
+ _remove(api, ctx._select_set, indexes);
1155
+ }
1156
+ else {
1157
+ // List is of rows which are deselected, so add it!
1158
+ _add(api, ctx._select_set, indexes);
1159
+ }
1160
+ });
1161
+ }
1162
+
1163
+ function _add(api, arr, indexes) {
1164
+ for (var i=0 ; i<indexes.length ; i++) {
1165
+ var id = api.row(indexes[i]).id();
1166
+
1167
+ if (id && id !== 'undefined' && ! arr.includes(id)) {
1168
+ arr.push(id);
1169
+ }
1170
+ }
1171
+ }
1172
+
1173
+ function _remove(api, arr, indexes) {
1174
+ for (var i=0 ; i<indexes.length ; i++) {
1175
+ var id = api.row(indexes[i]).id();
1176
+ var idx = arr.indexOf(id);
1177
+
1178
+ if (idx !== -1) {
1179
+ arr.splice(idx, 1);
1180
+ }
1181
+ }
1182
+ }
1183
+
938
1184
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
939
1185
  * DataTables selectors
940
1186
  */
@@ -1062,6 +1308,22 @@ apiRegister('select.items()', function (items) {
1062
1308
  });
1063
1309
  });
1064
1310
 
1311
+ apiRegister('select.keys()', function (flag) {
1312
+ if (flag === undefined) {
1313
+ return this.context[0]._select.keys;
1314
+ }
1315
+
1316
+ return this.iterator('table', function (ctx) {
1317
+ if (!ctx._select) {
1318
+ DataTable.select.init(new DataTable.Api(ctx));
1319
+ }
1320
+
1321
+ ctx._select.keys = flag;
1322
+
1323
+ keysSet(new DataTable.Api(ctx));
1324
+ });
1325
+ });
1326
+
1065
1327
  // Takes effect from the _next_ selection. None disables future selection, but
1066
1328
  // does not clear the current selection. Use the `deselect` methods for that
1067
1329
  apiRegister('select.style()', function (style) {
@@ -1083,10 +1345,15 @@ apiRegister('select.style()', function (style) {
1083
1345
  // Add / remove mouse event handlers. They aren't required when only
1084
1346
  // API selection is available
1085
1347
  var dt = new DataTable.Api(ctx);
1086
- disableMouseSelection(dt);
1087
1348
 
1088
1349
  if (style !== 'api') {
1089
- enableMouseSelection(dt);
1350
+ dt.ready(function () {
1351
+ disableMouseSelection(dt);
1352
+ enableMouseSelection(dt);
1353
+ });
1354
+ }
1355
+ else {
1356
+ disableMouseSelection(dt);
1090
1357
  }
1091
1358
 
1092
1359
  eventTrigger(new DataTable.Api(ctx), 'selectStyle', [style]);
@@ -1099,16 +1366,36 @@ apiRegister('select.selector()', function (selector) {
1099
1366
  }
1100
1367
 
1101
1368
  return this.iterator('table', function (ctx) {
1102
- disableMouseSelection(new DataTable.Api(ctx));
1369
+ var dt = new DataTable.Api(ctx);
1370
+ var style = ctx._select.style;
1371
+
1372
+ disableMouseSelection(dt);
1103
1373
 
1104
1374
  ctx._select.selector = selector;
1105
1375
 
1106
- if (ctx._select.style !== 'api') {
1107
- enableMouseSelection(new DataTable.Api(ctx));
1376
+ if (style && style !== 'api') {
1377
+ dt.ready(function () {
1378
+ disableMouseSelection(dt);
1379
+ enableMouseSelection(dt);
1380
+ });
1381
+ }
1382
+ else {
1383
+ disableMouseSelection(dt);
1108
1384
  }
1109
1385
  });
1110
1386
  });
1111
1387
 
1388
+ apiRegister('select.selectable()', function (set) {
1389
+ let ctx = this.context[0];
1390
+
1391
+ if (set) {
1392
+ ctx._select.selectable = set;
1393
+ return this;
1394
+ }
1395
+
1396
+ return ctx._select.selectable;
1397
+ });
1398
+
1112
1399
  apiRegister('select.last()', function (set) {
1113
1400
  let ctx = this.context[0];
1114
1401
 
@@ -1120,8 +1407,50 @@ apiRegister('select.last()', function (set) {
1120
1407
  return ctx._select_lastCell;
1121
1408
  });
1122
1409
 
1410
+ apiRegister('select.cumulative()', function (mode) {
1411
+ if (mode) {
1412
+ return this.iterator('table', function (ctx) {
1413
+ if (ctx._select_mode === mode) {
1414
+ return;
1415
+ }
1416
+
1417
+ var dt = new DataTable.Api(ctx);
1418
+
1419
+ // Convert from the current mode, to the new
1420
+ if (mode === 'subtractive') {
1421
+ // For subtractive mode we track the row ids which are not selected
1422
+ var unselected = dt.rows({selected: false}).ids().toArray();
1423
+
1424
+ ctx._select_mode = mode;
1425
+ ctx._select_set.length = 0;
1426
+ ctx._select_set.push.apply(ctx._select_set, unselected);
1427
+ }
1428
+ else {
1429
+ // Switching to additive, so selected rows are to be used
1430
+ var selected = dt.rows({selected: true}).ids().toArray();
1431
+
1432
+ ctx._select_mode = mode;
1433
+ ctx._select_set.length = 0;
1434
+ ctx._select_set.push.apply(ctx._select_set, selected);
1435
+ }
1436
+ }).draw(false);
1437
+ }
1438
+
1439
+ let ctx = this.context[0];
1440
+
1441
+ if (ctx && ctx._select_set) {
1442
+ return {
1443
+ mode: ctx._select_mode,
1444
+ rows: ctx._select_set
1445
+ };
1446
+ }
1447
+
1448
+ return null;
1449
+ });
1450
+
1123
1451
  apiRegisterPlural('rows().select()', 'row().select()', function (select) {
1124
1452
  var api = this;
1453
+ var selectedIndexes = [];
1125
1454
 
1126
1455
  if (select === false) {
1127
1456
  return this.deselect();
@@ -1136,9 +1465,20 @@ apiRegisterPlural('rows().select()', 'row().select()', function (select) {
1136
1465
  var dtData = ctx.aoData[idx];
1137
1466
  var dtColumns = ctx.aoColumns;
1138
1467
 
1468
+ if (ctx._select.selectable) {
1469
+ var result = ctx._select.selectable(dtData._aData, dtData.nTr, idx);
1470
+
1471
+ if (result === false) {
1472
+ // Not selectable - do nothing
1473
+ return;
1474
+ }
1475
+ }
1476
+
1139
1477
  $(dtData.nTr).addClass(ctx._select.className);
1140
1478
  dtData._select_selected = true;
1141
1479
 
1480
+ selectedIndexes.push(idx);
1481
+
1142
1482
  for (var i=0 ; i<dtColumns.length ; i++) {
1143
1483
  var col = dtColumns[i];
1144
1484
 
@@ -1163,8 +1503,8 @@ apiRegisterPlural('rows().select()', 'row().select()', function (select) {
1163
1503
  }
1164
1504
  });
1165
1505
 
1166
- this.iterator('table', function (ctx, i) {
1167
- eventTrigger(api, 'select', ['row', api[i]], true);
1506
+ this.iterator('table', function (ct) {
1507
+ eventTrigger(api, 'select', ['row', selectedIndexes], true);
1168
1508
  });
1169
1509
 
1170
1510
  return this;
@@ -1180,6 +1520,22 @@ apiRegister('row().selected()', function () {
1180
1520
  return false;
1181
1521
  });
1182
1522
 
1523
+ apiRegister('row().focus()', function () {
1524
+ var ctx = this.context[0];
1525
+
1526
+ if (ctx && this.length && ctx.aoData[this[0]] && ctx.aoData[this[0]].nTr) {
1527
+ ctx.aoData[this[0]].nTr.focus();
1528
+ }
1529
+ });
1530
+
1531
+ apiRegister('row().blur()', function () {
1532
+ var ctx = this.context[0];
1533
+
1534
+ if (ctx && this.length && ctx.aoData[this[0]] && ctx.aoData[this[0]].nTr) {
1535
+ ctx.aoData[this[0]].nTr.blur();
1536
+ }
1537
+ });
1538
+
1183
1539
  apiRegisterPlural('columns().select()', 'column().select()', function (select) {
1184
1540
  var api = this;
1185
1541
 
@@ -1526,6 +1882,8 @@ $.each(['Row', 'Column', 'Cell'], function (i, item) {
1526
1882
  init: function (dt) {
1527
1883
  var that = this;
1528
1884
 
1885
+ this.active(dt.select.items() === lc);
1886
+
1529
1887
  dt.on('selectItems.dt.DT', function (e, ctx, items) {
1530
1888
  that.active(items === lc);
1531
1889
  });
@@ -1576,8 +1934,18 @@ DataTable.render.select = function (valueProp, nameProp) {
1576
1934
  var dtRow = meta.settings.aoData[meta.row];
1577
1935
  var selected = dtRow._select_selected;
1578
1936
  var ariaLabel = meta.settings.oLanguage.select.aria.rowCheckbox;
1937
+ var selectable = meta.settings._select.selectable;
1579
1938
 
1580
1939
  if (type === 'display') {
1940
+ // Check if the row is selectable before showing the checkbox
1941
+ if (selectable) {
1942
+ var result = selectable(row, dtRow.nTr, meta.row);
1943
+
1944
+ if (result === false) {
1945
+ return '';
1946
+ }
1947
+ }
1948
+
1581
1949
  return $('<input>')
1582
1950
  .attr({
1583
1951
  'aria-label': ariaLabel,
@@ -1636,11 +2004,10 @@ $.fn.DataTable.select = DataTable.select;
1636
2004
  * Initialisation
1637
2005
  */
1638
2006
 
1639
- // DataTables creation - check if select has been defined in the options. Note
1640
- // this required that the table be in the document! If it isn't then something
1641
- // needs to trigger this method unfortunately. The next major release of
1642
- // DataTables will rework the events and address this.
1643
- $(document).on('preInit.dt.dtSelect', function (e, ctx) {
2007
+ // DataTables creation - we need this to run _before_ data is read in, but
2008
+ // for backwards compat. we also run again on preInit. If it happens twice
2009
+ // it will simply do nothing the second time around.
2010
+ $(document).on('i18n.dt.dtSelect preInit.dt.dtSelect', function (e, ctx) {
1644
2011
  if (e.namespace !== 'dt') {
1645
2012
  return;
1646
2013
  }