datatables.net-columncontrol 1.0.6 → 1.1.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
- /*! ColumnControl 1.0.6
1
+ /*! ColumnControl 1.1.0
2
2
  * Copyright (c) SpryMedia Ltd - datatables.net/license
3
3
  *
4
4
  * SVG icons: ISC License
@@ -102,6 +102,14 @@ var icons = {
102
102
  greater: wrap('<path d="m9 18 6-6-6-6"/>'),
103
103
  // Custom
104
104
  greaterOrEqual: wrap('<path d="m9 16 6-6-6-6"/><path d="m9 21 6-6"/>'),
105
+ // Custom
106
+ groupAdd: wrap('<path d="M6 21v-7.5m-3.549 3.75H9.75"/><rect width="13.5" height="7.5" x="3" y="3" rx="1.5"/><rect width="7.5" height="7.5" x="13.5" y="13.5" fill="currentColor" rx="1.5"/>'),
107
+ // Custom
108
+ groupClear: wrap('<rect width="13.5" height="7.5" x="3" y="3" rx="1.5"/><rect width="7.5" height="7.5" x="13.5" y="13.5" rx="1.5"/>'),
109
+ // Custom
110
+ groupTop: wrap('<rect width="13.5" height="7.5" x="3" y="3" fill="currentColor" rx="1.5"/><rect width="7.5" height="7.5" x="13.5" y="13.5" rx="1.5"/>'),
111
+ // Custom
112
+ groupRemove: wrap('<path d="M2.451 17.25H9.75"/><rect width="13.5" height="7.5" x="3" y="3" rx="1.5"/><rect width="7.5" height="7.5" x="13.5" y="13.5" rx="1.5"/>'),
105
113
  less: wrap('<path d="m15 18-6-6 6-6"/>'),
106
114
  // Custom
107
115
  lessOrEqual: wrap('<path d="m15 16-6-6 6-6"/><path d="m15 21-6-6"/>'),
@@ -208,6 +216,7 @@ function attachDropdown(dropdown, dt, btn) {
208
216
  dropdown._shown = true;
209
217
  dtContainer.append(dropdown);
210
218
  positionDropdown(dropdown, dt, btn.element());
219
+ btn.element().setAttribute('aria-expanded', 'true');
211
220
  // Note that this could be called when the dropdown has already been removed from the document
212
221
  // via another dropdown being shown. This will clean up the event on the next body click.
213
222
  var removeDropdown = function (e) {
@@ -220,6 +229,12 @@ function attachDropdown(dropdown, dt, btn) {
220
229
  if (e.target === dropdown || dropdown.contains(e.target)) {
221
230
  return;
222
231
  }
232
+ // If there is currently a datetime picker visible on the page, assume that it belongs to
233
+ // this dropdown. Don't want to close while operating on the picker.
234
+ var datetime = document.querySelector('div.dt-datetime');
235
+ if (datetime && (e.target === datetime || datetime.contains(e.target))) {
236
+ return;
237
+ }
223
238
  dropdown._close();
224
239
  document.body.removeEventListener('click', removeDropdown);
225
240
  };
@@ -317,6 +332,7 @@ var dropdownContent = {
317
332
  dropdown._close = function () {
318
333
  dropdown.remove();
319
334
  dropdown._shown = false;
335
+ btn.element().setAttribute('aria-expanded', 'false');
320
336
  };
321
337
  dropdown.setAttribute('role', 'dialog');
322
338
  dropdown.setAttribute('aria-label', dt.i18n('columnControl.dropdown', config.text));
@@ -329,7 +345,7 @@ var dropdownContent = {
329
345
  });
330
346
  // A liner element allows more styling options, so the contents go inside this
331
347
  var liner = dropdown.childNodes[0];
332
- var btn = new Button(dt)
348
+ var btn = new Button(dt, this)
333
349
  .text(dt.i18n('columnControl.dropdown', config.text))
334
350
  .icon(config.icon)
335
351
  .className(config.className)
@@ -348,6 +364,7 @@ var dropdownContent = {
348
364
  }
349
365
  });
350
366
  btn.element().setAttribute('aria-haspopup', 'dialog');
367
+ btn.element().setAttribute('aria-expanded', 'false');
351
368
  // Add the content for the dropdown
352
369
  for (var i = 0; i < config.content.length; i++) {
353
370
  var content = this.resolve(config.content[i]);
@@ -384,18 +401,20 @@ var Button = /** @class */ (function () {
384
401
  * Create a new button for use in ColumnControl contents. Buttons created by this class can be
385
402
  * used at the top level in the header or in a dropdown.
386
403
  */
387
- function Button(dt) {
404
+ function Button(dt, host) {
388
405
  this._s = {
389
406
  active: false,
390
407
  activeList: [],
391
408
  buttonClick: null,
392
409
  dt: null,
393
410
  enabled: true,
411
+ host: null,
394
412
  label: '',
395
413
  namespace: '',
396
414
  value: null
397
415
  };
398
416
  this._s.dt = dt;
417
+ this._s.host = host;
399
418
  this._dom = {
400
419
  button: createElement('button', Button.classes.container),
401
420
  dropdownDisplay: null,
@@ -473,9 +492,7 @@ var Button = /** @class */ (function () {
473
492
  this._dom.button.removeEventListener('click', this._s.buttonClick);
474
493
  this._dom.button.removeEventListener('keypress', this._s.buttonClick);
475
494
  }
476
- if (this._s.namespace) {
477
- this._s.dt.off('destroy.' + this._s.namespace);
478
- }
495
+ this._s.host.destroyRemove(this);
479
496
  };
480
497
  /**
481
498
  * Relevant for drop downs only. When a button in a dropdown is hidden, we might want to
@@ -497,13 +514,10 @@ var Button = /** @class */ (function () {
497
514
  Button.prototype.element = function () {
498
515
  return this._dom.button;
499
516
  };
500
- /**
501
- * Set if the button should be enabled or not.
502
- *
503
- * @param enable Toggle the enable state
504
- * @returns Button instance
505
- */
506
517
  Button.prototype.enable = function (enable) {
518
+ if (enable === undefined) {
519
+ return this._s.enabled;
520
+ }
507
521
  this._dom.button.classList.toggle('dtcc-button_disabled', !enable);
508
522
  this._s.enabled = enable;
509
523
  return this;
@@ -540,10 +554,7 @@ var Button = /** @class */ (function () {
540
554
  this._s.namespace = 'dtcc-' + _namespace++;
541
555
  this._dom.button.addEventListener('click', buttonClick);
542
556
  this._dom.button.addEventListener('keypress', buttonClick);
543
- // Use a unique namespace to be able to easily remove per button
544
- this._s.dt.on('destroy.' + this._s.namespace, function () {
545
- _this.destroy();
546
- });
557
+ this._s.host.destroyAdd(this);
547
558
  return this;
548
559
  };
549
560
  /**
@@ -598,19 +609,22 @@ var CheckList = /** @class */ (function () {
598
609
  /**
599
610
  * Container for a list of buttons
600
611
  */
601
- function CheckList(dt, opts) {
612
+ function CheckList(dt, host, opts) {
602
613
  var _this = this;
603
614
  this._s = {
604
615
  buttons: [],
605
616
  dt: null,
606
617
  handler: function () { },
618
+ host: null,
607
619
  search: ''
608
620
  };
609
621
  this._s.dt = dt;
622
+ this._s.host = host;
610
623
  this._dom = {
611
624
  buttons: createElement('div', 'dtcc-list-buttons'),
612
625
  container: createElement('div', CheckList.classes.container),
613
626
  controls: createElement('div', 'dtcc-list-controls'),
627
+ empty: createElement('div', 'dtcc-list-empty', dt.i18n('columnControl.list.empty', 'No options')),
614
628
  title: createElement('div', 'dtcc-list-title'),
615
629
  selectAll: createElement('button', 'dtcc-list-selectAll', dt.i18n('columnControl.list.all', 'Select all')),
616
630
  selectAllCount: createElement('span'),
@@ -622,6 +636,7 @@ var CheckList = /** @class */ (function () {
622
636
  dom.search.setAttribute('type', 'text');
623
637
  dom.container.append(dom.title);
624
638
  dom.container.append(dom.controls);
639
+ dom.container.append(dom.empty);
625
640
  dom.container.append(dom.buttons);
626
641
  if (opts.select) {
627
642
  dom.controls.append(dom.selectAll);
@@ -672,7 +687,7 @@ var CheckList = /** @class */ (function () {
672
687
  }
673
688
  var _loop_1 = function (i) {
674
689
  var option = options[i];
675
- var btn = new Button(this_1._s.dt)
690
+ var btn = new Button(this_1._s.dt, this_1._s.host)
676
691
  .active(option.active || false)
677
692
  .handler(function (e) {
678
693
  _this._s.handler(e, btn, _this._s.buttons, true);
@@ -681,7 +696,7 @@ var CheckList = /** @class */ (function () {
681
696
  .icon(option.icon || '')
682
697
  .text(option.label !== ''
683
698
  ? option.label
684
- : this_1._s.dt.i18n('columnControl.list.emptyOption', 'Empty'))
699
+ : this_1._s.dt.i18n('columnControl.list.empty', 'Empty'))
685
700
  .value(option.value);
686
701
  if (option.label === '') {
687
702
  btn.className('empty');
@@ -752,11 +767,11 @@ var CheckList = /** @class */ (function () {
752
767
  * @param dt DataTable instance
753
768
  * @param idx Column index
754
769
  */
755
- CheckList.prototype.searchListener = function (dt, parent) {
770
+ CheckList.prototype.searchListener = function (dt) {
756
771
  var _this = this;
757
- // Column control search clearing (column().ccSearchClear() method)
772
+ // Column control search clearing (column().columnControl.searchClear() method)
758
773
  dt.on('cc-search-clear', function (e, colIdx) {
759
- if (colIdx === parent.idx()) {
774
+ if (colIdx === _this._s.host.idx()) {
760
775
  _this.selectNone();
761
776
  _this._s.handler(e, null, _this._s.buttons, false);
762
777
  _this._s.search = '';
@@ -844,6 +859,8 @@ var CheckList = /** @class */ (function () {
844
859
  el.appendChild(btn.element());
845
860
  }
846
861
  }
862
+ this._dom.empty.style.display = buttons.length === 0 ? 'block' : 'none';
863
+ el.style.display = buttons.length > 0 ? 'block' : 'none';
847
864
  };
848
865
  CheckList.classes = {
849
866
  container: 'dtcc-list',
@@ -862,7 +879,7 @@ var colVis = {
862
879
  },
863
880
  init: function (config) {
864
881
  var dt = this.dt();
865
- var checkList = new CheckList(dt, {
882
+ var checkList = new CheckList(dt, this, {
866
883
  search: config.search,
867
884
  select: config.select
868
885
  })
@@ -945,7 +962,7 @@ var reorder = {
945
962
  init: function (config) {
946
963
  var _this = this;
947
964
  var dt = this.dt();
948
- var btn = new Button(dt)
965
+ var btn = new Button(dt, this)
949
966
  .text(dt.i18n('columnControl.reorder', config.text))
950
967
  .icon(config.icon)
951
968
  .className(config.className)
@@ -978,7 +995,7 @@ var reorderLeft = {
978
995
  init: function (config) {
979
996
  var _this = this;
980
997
  var dt = this.dt();
981
- var btn = new Button(dt)
998
+ var btn = new Button(dt, this)
982
999
  .text(dt.i18n('columnControl.reorderLeft', config.text))
983
1000
  .icon(config.icon)
984
1001
  .className(config.className)
@@ -1008,7 +1025,7 @@ var reorderRight = {
1008
1025
  init: function (config) {
1009
1026
  var _this = this;
1010
1027
  var dt = this.dt();
1011
- var btn = new Button(dt)
1028
+ var btn = new Button(dt, this)
1012
1029
  .text(dt.i18n('columnControl.reorderRight', config.text))
1013
1030
  .icon(config.icon)
1014
1031
  .className(config.className)
@@ -1040,7 +1057,7 @@ var order = {
1040
1057
  init: function (config) {
1041
1058
  var _this = this;
1042
1059
  var dt = this.dt();
1043
- var btn = new Button(dt)
1060
+ var btn = new Button(dt, this)
1044
1061
  .text(dt.i18n('columnControl.order', config.text))
1045
1062
  .icon('orderAsc')
1046
1063
  .className(config.className);
@@ -1072,7 +1089,7 @@ var orderAddAsc = {
1072
1089
  init: function (config) {
1073
1090
  var _this = this;
1074
1091
  var dt = this.dt();
1075
- var btn = new Button(dt)
1092
+ var btn = new Button(dt, this)
1076
1093
  .text(dt.i18n('columnControl.orderAddAsc', config.text))
1077
1094
  .icon(config.icon)
1078
1095
  .className(config.className)
@@ -1098,7 +1115,7 @@ var orderAddDesc = {
1098
1115
  init: function (config) {
1099
1116
  var _this = this;
1100
1117
  var dt = this.dt();
1101
- var btn = new Button(dt)
1118
+ var btn = new Button(dt, this)
1102
1119
  .text(dt.i18n('columnControl.orderAddDesc', config.text))
1103
1120
  .icon(config.icon)
1104
1121
  .className(config.className)
@@ -1124,7 +1141,7 @@ var orderAsc = {
1124
1141
  init: function (config) {
1125
1142
  var _this = this;
1126
1143
  var dt = this.dt();
1127
- var btn = new Button(dt)
1144
+ var btn = new Button(dt, this)
1128
1145
  .text(dt.i18n('columnControl.orderAsc', config.text))
1129
1146
  .icon(config.icon)
1130
1147
  .className(config.className)
@@ -1154,7 +1171,7 @@ var orderClear = {
1154
1171
  },
1155
1172
  init: function (config) {
1156
1173
  var dt = this.dt();
1157
- var btn = new Button(dt)
1174
+ var btn = new Button(dt, this)
1158
1175
  .text(dt.i18n('columnControl.orderClear', config.text))
1159
1176
  .icon(config.icon)
1160
1177
  .className(config.className)
@@ -1180,7 +1197,7 @@ var orderDesc = {
1180
1197
  init: function (config) {
1181
1198
  var _this = this;
1182
1199
  var dt = this.dt();
1183
- var btn = new Button(dt)
1200
+ var btn = new Button(dt, this)
1184
1201
  .text(dt.i18n('columnControl.orderDesc', config.text))
1185
1202
  .icon(config.icon)
1186
1203
  .className(config.className)
@@ -1211,7 +1228,7 @@ var orderRemove = {
1211
1228
  init: function (config) {
1212
1229
  var _this = this;
1213
1230
  var dt = this.dt();
1214
- var btn = new Button(dt)
1231
+ var btn = new Button(dt, this)
1215
1232
  .text(dt.i18n('columnControl.orderRemove', config.text))
1216
1233
  .icon(config.icon)
1217
1234
  .className(config.className)
@@ -1245,6 +1262,190 @@ var orderStatus = {
1245
1262
  }
1246
1263
  };
1247
1264
 
1265
+ /**
1266
+ * Add an item to the grouping structure
1267
+ *
1268
+ * @param dt DataTable API instance
1269
+ * @param dataSrc Grouping data point to add
1270
+ * @returns Grouping array
1271
+ */
1272
+ function rowGroupAdd$1(dt, dataSrc) {
1273
+ var applied = rowGroupApplied(dt);
1274
+ var idx = applied.indexOf(dataSrc);
1275
+ if (idx === -1) {
1276
+ applied.push(dataSrc);
1277
+ dt.rowGroup().dataSrc(applied);
1278
+ }
1279
+ return applied;
1280
+ }
1281
+ /**
1282
+ * Always want an array return
1283
+ *
1284
+ * @param dt DataTable API instance
1285
+ * @returns
1286
+ */
1287
+ function rowGroupApplied(dt) {
1288
+ var applied = dt.rowGroup().dataSrc();
1289
+ return Array.isArray(applied)
1290
+ ? applied
1291
+ : [applied];
1292
+ }
1293
+ /**
1294
+ * Remove all grouping
1295
+ *
1296
+ * @param dt DataTable API instance
1297
+ */
1298
+ function rowGroupClear$1(dt) {
1299
+ dt.rowGroup().dataSrc([]);
1300
+ }
1301
+ /**
1302
+ * Remove an item from the grouping structure
1303
+ *
1304
+ * @param dt DataTable API instance
1305
+ * @param dataSrc Grouping data point to remove
1306
+ * @returns Grouping array
1307
+ */
1308
+ function rowGroupRemove$1(dt, dataSrc) {
1309
+ var applied = rowGroupApplied(dt);
1310
+ var idx = applied.indexOf(dataSrc);
1311
+ if (idx !== -1) {
1312
+ applied.splice(idx, 1);
1313
+ dt.rowGroup().dataSrc(applied);
1314
+ }
1315
+ return applied;
1316
+ }
1317
+ var rowGroup = {
1318
+ defaults: {
1319
+ className: 'rowGroup',
1320
+ icon: 'groupTop',
1321
+ order: true,
1322
+ text: 'Group rows'
1323
+ },
1324
+ init: function (config) {
1325
+ var _this = this;
1326
+ var dt = this.dt();
1327
+ var btn = new Button(dt, this)
1328
+ .text(dt.i18n('columnControl.rowGroup', config.text))
1329
+ .icon(config.icon)
1330
+ .className(config.className)
1331
+ .handler(function () {
1332
+ var dataSrc = dt.column(_this.idx()).dataSrc();
1333
+ if (btn.active()) {
1334
+ // Grouping is active - remove
1335
+ rowGroupRemove$1(dt, dataSrc);
1336
+ }
1337
+ else {
1338
+ // No grouping by this column yet, set it
1339
+ rowGroupClear$1(dt);
1340
+ rowGroupAdd$1(dt, dataSrc);
1341
+ if (config.order !== false) {
1342
+ dt.order([_this.idx(), 'asc']);
1343
+ }
1344
+ }
1345
+ dt.draw();
1346
+ });
1347
+ // Show as active when grouping is applied
1348
+ dt.on('rowgroup-datasrc', function () {
1349
+ var applied = rowGroupApplied(dt);
1350
+ var ours = dt.column(_this.idx()).dataSrc();
1351
+ btn.active(applied.includes(ours));
1352
+ });
1353
+ return btn.element();
1354
+ }
1355
+ };
1356
+
1357
+ var rowGroupAdd = {
1358
+ defaults: {
1359
+ className: 'rowGroupAdd',
1360
+ icon: 'groupAdd',
1361
+ order: true,
1362
+ text: 'Add to grouping'
1363
+ },
1364
+ init: function (config) {
1365
+ var _this = this;
1366
+ var dt = this.dt();
1367
+ var btn = new Button(dt, this)
1368
+ .text(dt.i18n('columnControl.rowGroup', config.text))
1369
+ .icon(config.icon)
1370
+ .className(config.className)
1371
+ .handler(function () {
1372
+ var dataSrc = dt.column(_this.idx()).dataSrc();
1373
+ if (btn.enable()) {
1374
+ // No grouping by this column yet, add it
1375
+ rowGroupAdd$1(dt, dataSrc);
1376
+ }
1377
+ dt.draw();
1378
+ });
1379
+ // Show as active when grouping is applied
1380
+ dt.on('rowgroup-datasrc', function () {
1381
+ var applied = rowGroupApplied(dt);
1382
+ var ours = dt.column(_this.idx()).dataSrc();
1383
+ btn.enable(!applied.includes(ours));
1384
+ });
1385
+ return btn.element();
1386
+ }
1387
+ };
1388
+
1389
+ var rowGroupClear = {
1390
+ defaults: {
1391
+ className: 'rowGroupClear',
1392
+ icon: 'groupClear',
1393
+ text: 'Clear all grouping'
1394
+ },
1395
+ init: function (config) {
1396
+ var dt = this.dt();
1397
+ var btn = new Button(dt, this)
1398
+ .text(dt.i18n('columnControl.rowGroup', config.text))
1399
+ .icon(config.icon)
1400
+ .className(config.className)
1401
+ .handler(function () {
1402
+ rowGroupClear$1(dt);
1403
+ dt.draw();
1404
+ });
1405
+ // Show as active when any grouping is applied
1406
+ dt.on('rowgroup-datasrc', function () {
1407
+ btn.enable(rowGroupApplied(dt).length > 0);
1408
+ });
1409
+ // Default status
1410
+ btn.enable(rowGroupApplied(dt).length > 0);
1411
+ return btn.element();
1412
+ }
1413
+ };
1414
+
1415
+ var rowGroupRemove = {
1416
+ defaults: {
1417
+ className: 'rowGroupRemove',
1418
+ icon: 'groupRemove',
1419
+ order: true,
1420
+ text: 'Remove from grouping'
1421
+ },
1422
+ init: function (config) {
1423
+ var _this = this;
1424
+ var dt = this.dt();
1425
+ var btn = new Button(dt, this)
1426
+ .text(dt.i18n('columnControl.rowGroup', config.text))
1427
+ .icon(config.icon)
1428
+ .className(config.className)
1429
+ .handler(function () {
1430
+ var dataSrc = dt.column(_this.idx()).dataSrc();
1431
+ if (btn.enable()) {
1432
+ // Grouping is active - remove
1433
+ rowGroupRemove$1(dt, dataSrc);
1434
+ dt.draw();
1435
+ }
1436
+ });
1437
+ // Show as active when grouping is applied
1438
+ dt.on('rowgroup-datasrc', function () {
1439
+ var applied = rowGroupApplied(dt);
1440
+ var ours = dt.column(_this.idx()).dataSrc();
1441
+ btn.enable(applied.includes(ours));
1442
+ });
1443
+ // Default disabled
1444
+ btn.enable(false);
1445
+ return btn.element();
1446
+ }
1447
+ };
1448
+
1248
1449
  var SearchInput = /** @class */ (function () {
1249
1450
  /**
1250
1451
  * Create a container element, for consistent DOM structure and styling
@@ -1252,6 +1453,8 @@ var SearchInput = /** @class */ (function () {
1252
1453
  function SearchInput(dt, idx) {
1253
1454
  var _this = this;
1254
1455
  this._type = 'text';
1456
+ this._sspTransform = null;
1457
+ this._sspData = {};
1255
1458
  this._dt = dt;
1256
1459
  this._idx = idx;
1257
1460
  this._dom = {
@@ -1290,28 +1493,28 @@ var SearchInput = /** @class */ (function () {
1290
1493
  });
1291
1494
  // State handling - all components that use this class have the same state saving structure
1292
1495
  // so shared handling can be performed here.
1293
- dt.on('stateSaveParams', function (e, s, data) {
1496
+ dt.on('stateSaveParams.DT', function (e, s, data) {
1294
1497
  if (!data.columnControl) {
1295
1498
  data.columnControl = {};
1296
1499
  }
1297
- if (!data.columnControl[idx]) {
1298
- data.columnControl[idx] = {};
1500
+ if (!data.columnControl[_this._idx]) {
1501
+ data.columnControl[_this._idx] = {};
1299
1502
  }
1300
- data.columnControl[idx].searchInput = {
1503
+ data.columnControl[_this._idx].searchInput = {
1301
1504
  logic: dom.select.value,
1302
1505
  type: _this._type,
1303
1506
  value: dom.input.value
1304
1507
  };
1305
1508
  });
1306
- dt.on('stateLoaded', function (e, s, state) {
1509
+ dt.on('stateLoaded.DT', function (e, s, state) {
1307
1510
  _this._stateLoad(state);
1308
1511
  });
1309
1512
  // Same as for ColumnControl - reassign a column index if needed.
1310
- dt.on('columns-reordered', function (e, details) {
1513
+ dt.on('columns-reordered.DT', function (e, details) {
1311
1514
  _this._idx = dt.colReorder.transpose(originalIdx, 'fromOriginal');
1312
1515
  });
1313
- // Column control search clearing (column().ccSearchClear() method)
1314
- dt.on('cc-search-clear', function (e, colIdx) {
1516
+ // Column control search clearing (column().columnControl.searchClear() method)
1517
+ dt.on('cc-search-clear.DT', function (e, colIdx) {
1315
1518
  if (colIdx === _this._idx) {
1316
1519
  // Don't want an automatic redraw on this event
1317
1520
  _this._loadingState = true;
@@ -1319,6 +1522,23 @@ var SearchInput = /** @class */ (function () {
1319
1522
  _this._loadingState = false;
1320
1523
  }
1321
1524
  });
1525
+ // Data for server-side processing
1526
+ if (dt.page.info().serverSide) {
1527
+ dt.on('preXhr.DT', function (e, s, d) {
1528
+ if (!d.columns[_this._idx].columnControl) {
1529
+ d.columns[_this._idx].columnControl = {};
1530
+ }
1531
+ var val = _this._dom.input.value;
1532
+ if (_this._sspTransform) {
1533
+ val = _this._sspTransform(val);
1534
+ }
1535
+ d.columns[_this._idx].columnControl.search = Object.assign({
1536
+ value: val,
1537
+ logic: _this._dom.select.value,
1538
+ type: _this._type
1539
+ }, _this._sspData);
1540
+ });
1541
+ }
1322
1542
  }
1323
1543
  /**
1324
1544
  * Add a class to the container
@@ -1441,6 +1661,26 @@ var SearchInput = /** @class */ (function () {
1441
1661
  this.runSearch();
1442
1662
  return this;
1443
1663
  };
1664
+ /**
1665
+ * Set a function to transform the input value before SSP data submission
1666
+ *
1667
+ * @param fn Transform function
1668
+ * @returns Self for chaining
1669
+ */
1670
+ SearchInput.prototype.sspTransform = function (fn) {
1671
+ this._sspTransform = fn;
1672
+ return this;
1673
+ };
1674
+ /**
1675
+ * Set extra information to be send to the server for server-side processing
1676
+ *
1677
+ * @param data Data object
1678
+ * @returns Self for chaining
1679
+ */
1680
+ SearchInput.prototype.sspData = function (data) {
1681
+ this._sspData = data;
1682
+ return this;
1683
+ };
1444
1684
  /**
1445
1685
  * Set the text that will be shown as the title for the control
1446
1686
  *
@@ -1503,6 +1743,8 @@ var SearchInput = /** @class */ (function () {
1503
1743
  var searchDateTime = {
1504
1744
  defaults: {
1505
1745
  clear: true,
1746
+ format: '',
1747
+ mask: '',
1506
1748
  placeholder: '',
1507
1749
  title: '',
1508
1750
  titleAttr: ''
@@ -1514,11 +1756,14 @@ var searchDateTime = {
1514
1756
  var luxon = DataTable.use('luxon');
1515
1757
  var dt = this.dt();
1516
1758
  var i18nBase = 'columnControl.search.datetime.';
1517
- var displayFormat = '';
1759
+ var pickerFormat = '';
1760
+ var dataSrcFormat = '';
1518
1761
  var dateTime;
1519
1762
  var searchInput = new SearchInput(dt, this.idx())
1520
1763
  .type('date')
1521
1764
  .addClass('dtcc-searchDateTime')
1765
+ .sspTransform(function (val) { return toISO(val, pickerFormat, moment, luxon); })
1766
+ .sspData({ mask: config.mask })
1522
1767
  .clearable(config.clear)
1523
1768
  .placeholder(config.placeholder)
1524
1769
  .title(config.title)
@@ -1532,10 +1777,18 @@ var searchDateTime = {
1532
1777
  { label: dt.i18n(i18nBase + 'notEmpty', 'Not empty'), value: 'notEmpty' }
1533
1778
  ])
1534
1779
  .search(function (searchType, searchTerm, loadingState) {
1780
+ // When SSP, don't apply a filter here, SearchInput will add to the submit data
1781
+ if (dt.page.info().serverSide) {
1782
+ if (!loadingState) {
1783
+ dt.draw();
1784
+ }
1785
+ return;
1786
+ }
1787
+ var mask = config.mask;
1535
1788
  var column = dt.column(_this.idx());
1536
1789
  var search = searchTerm === ''
1537
1790
  ? ''
1538
- : dateToNum(dateTime && fromPicker ? dateTime.val() : searchTerm.trim(), displayFormat, moment, luxon);
1791
+ : dateToNum(dateTime && fromPicker ? dateTime.val() : searchTerm.trim(), pickerFormat, moment, luxon, mask);
1539
1792
  if (searchType === 'empty') {
1540
1793
  column.search.fixed('dtcc', function (haystack) { return !haystack; });
1541
1794
  }
@@ -1554,16 +1807,24 @@ var searchDateTime = {
1554
1807
  // Use a function for matching - weak typing
1555
1808
  // Note that the haystack in the search function is the rendered date - it
1556
1809
  // might need to be converted back to a date
1557
- column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) == search; });
1810
+ column.search.fixed('dtcc', function (haystack) {
1811
+ return dateToNum(haystack, dataSrcFormat, moment, luxon, mask) == search;
1812
+ });
1558
1813
  }
1559
1814
  else if (searchType === 'notEqual') {
1560
- column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) != search; });
1815
+ column.search.fixed('dtcc', function (haystack) {
1816
+ return dateToNum(haystack, dataSrcFormat, moment, luxon, mask) != search;
1817
+ });
1561
1818
  }
1562
1819
  else if (searchType === 'greater') {
1563
- column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) > search; });
1820
+ column.search.fixed('dtcc', function (haystack) {
1821
+ return dateToNum(haystack, dataSrcFormat, moment, luxon, mask) > search;
1822
+ });
1564
1823
  }
1565
1824
  else if (searchType === 'less') {
1566
- column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) < search; });
1825
+ column.search.fixed('dtcc', function (haystack) {
1826
+ return dateToNum(haystack, dataSrcFormat, moment, luxon, mask) < search;
1827
+ });
1567
1828
  }
1568
1829
  // If in a dropdown, set the parent levels as active
1569
1830
  if (config._parents) {
@@ -1578,10 +1839,13 @@ var searchDateTime = {
1578
1839
  // Once data has been loaded we can run DateTime with the specified format
1579
1840
  dt.ready(function () {
1580
1841
  var DateTime = DataTable.use('datetime');
1581
- displayFormat = getFormat(dt, _this.idx());
1842
+ dataSrcFormat = getFormat(dt, _this.idx());
1843
+ pickerFormat = config.format
1844
+ ? config.format
1845
+ : dataSrcFormat;
1582
1846
  if (DateTime) {
1583
1847
  dateTime = new DateTime(searchInput.input(), {
1584
- format: displayFormat,
1848
+ format: pickerFormat,
1585
1849
  i18n: dt.settings()[0].oLanguage.datetime, // could be undefined
1586
1850
  onChange: function () {
1587
1851
  fromPicker = true;
@@ -1608,18 +1872,36 @@ function getFormat(dt, column) {
1608
1872
  return 'YYYY-MM-DD';
1609
1873
  }
1610
1874
  else if (type === 'datetime') {
1611
- // If no format was specified in the DT type, then we need to use Moment / Luxon's default
1612
- // locale formatting.
1613
- var moment = DataTable.use('moment');
1614
- var luxon = DataTable.use('luxon');
1615
- if (moment) {
1616
- return moment().creationData().locale._longDateFormat.L;
1617
- }
1618
- if (luxon) {
1619
- // Luxon doesn't appear to provide a way to let us get the default locale formatting
1620
- // string, so we need to attempt to decode it.
1621
- return luxon.DateTime.fromISO('1999-08-07')
1622
- .toLocaleString()
1875
+ // If no format was specified in the DT type, a Javascript native toLocaleDateString
1876
+ // was used. Need to work out what that format is in Moment or Luxon. We need to pass
1877
+ // a known value though the renderer and work out the format
1878
+ var renderer = dt.settings()[0].aoColumns[column].mRender;
1879
+ var resultPm = renderer('1999-08-07T23:05:04Z', 'display');
1880
+ var resultAm = renderer('1999-08-07T03:05:04Z', 'display');
1881
+ var leadingZero = resultAm.includes('03');
1882
+ // What formatter are we using?
1883
+ if (DataTable.use('moment')) {
1884
+ return resultPm
1885
+ .replace('23', leadingZero ? 'HH' : 'H')
1886
+ .replace('11', leadingZero ? 'hh' : 'h')
1887
+ .replace('05', 'mm')
1888
+ .replace('04', 'ss')
1889
+ .replace('PM', 'A')
1890
+ .replace('pm', 'a')
1891
+ .replace('07', 'DD')
1892
+ .replace('7', 'D')
1893
+ .replace('08', 'MM')
1894
+ .replace('8', 'M')
1895
+ .replace('1999', 'YYYY')
1896
+ .replace('99', 'YY');
1897
+ }
1898
+ else if (DataTable.use('luxon')) {
1899
+ return resultPm
1900
+ .replace('23', leadingZero ? 'HH' : 'H')
1901
+ .replace('11', leadingZero ? 'hh' : 'h')
1902
+ .replace('05', 'mm')
1903
+ .replace('04', 'ss')
1904
+ .replace('PM', 'a')
1623
1905
  .replace('07', 'dd')
1624
1906
  .replace('7', 'd')
1625
1907
  .replace('08', 'MM')
@@ -1627,6 +1909,13 @@ function getFormat(dt, column) {
1627
1909
  .replace('1999', 'yyyy')
1628
1910
  .replace('99', 'yy');
1629
1911
  }
1912
+ else if (resultPm.includes('23') && resultPm.includes('1999')) {
1913
+ return 'YYYY-MM-DD hh:mm:ss';
1914
+ }
1915
+ else if (resultPm.includes('23')) {
1916
+ return 'hh:mm:ss';
1917
+ }
1918
+ // fall through to final return
1630
1919
  }
1631
1920
  else if (type.includes('datetime-')) {
1632
1921
  // Column was specified with a particular display format - we can extract that format from
@@ -1650,19 +1939,75 @@ function getFormat(dt, column) {
1650
1939
  * @param luxon Luxon object, if it is available
1651
1940
  * @returns Time stamp - milliseconds
1652
1941
  */
1653
- function dateToNum(input, srcFormat, moment, luxon) {
1942
+ function dateToNum(input, srcFormat, moment, luxon, mask) {
1943
+ var d;
1654
1944
  if (input === '') {
1655
1945
  return '';
1656
1946
  }
1657
- else if (input instanceof Date) {
1658
- return input.getTime();
1947
+ if (input instanceof Date) {
1948
+ d = input;
1659
1949
  }
1660
1950
  else if (srcFormat !== 'YYYY-MM-DD' && (moment || luxon)) {
1661
- return moment
1951
+ d = new Date(moment
1662
1952
  ? moment(input, srcFormat).unix() * 1000
1663
- : luxon.DateTime.fromFormat(input, srcFormat).toMillis();
1953
+ : luxon.DateTime.fromFormat(input, srcFormat).toMillis());
1954
+ }
1955
+ else {
1956
+ // new Date() with `/` separators will treat the input as local time, but with `-` it will
1957
+ // treat it as UTC. We want UTC so do a replacement
1958
+ d = new Date(input.replace(/\//g, '-'));
1664
1959
  }
1665
- return new Date(input).getTime();
1960
+ if (mask) {
1961
+ if (!mask.includes('YYYY')) {
1962
+ d.setFullYear(1970);
1963
+ }
1964
+ if (!mask.includes('MM')) {
1965
+ d.setUTCMonth(0);
1966
+ }
1967
+ if (!mask.includes('DD')) {
1968
+ d.setUTCDate(1);
1969
+ }
1970
+ if (!mask.includes('hh')) {
1971
+ d.setUTCHours(0);
1972
+ }
1973
+ if (!mask.includes('mm')) {
1974
+ d.setUTCMinutes(0);
1975
+ }
1976
+ if (!mask.includes('ss')) {
1977
+ // This will match milliseconds as well, but that's fine, you won't match mS but not S
1978
+ d.setUTCSeconds(0);
1979
+ }
1980
+ if (!mask.includes('sss')) {
1981
+ d.setUTCMilliseconds(0);
1982
+ }
1983
+ }
1984
+ return d.getTime();
1985
+ }
1986
+ /**
1987
+ * Convert an input string to an ISO formatted date
1988
+ *
1989
+ * @param input Input value
1990
+ * @param srcFormat String format of the input
1991
+ * @param moment Moment instance, if it is available
1992
+ * @param luxon Luxon object, if it is available
1993
+ * @returns Value in ISO
1994
+ */
1995
+ function toISO(input, srcFormat, moment, luxon) {
1996
+ if (input === '') {
1997
+ return '';
1998
+ }
1999
+ else if (srcFormat !== 'YYYY-MM-DD' && moment) {
2000
+ // TODO Does it have a time component?
2001
+ return moment.utc(input, srcFormat).toISOString();
2002
+ }
2003
+ else if (srcFormat !== 'YYYY-MM-DD' && luxon) {
2004
+ // TODO Does it have a time component?
2005
+ return luxon.DateTime.fromFormat(input, srcFormat).toISO();
2006
+ }
2007
+ // new Date() with `/` separators will treat the input as local time, but with `-` it will
2008
+ // treat it as UTC. We want UTC so do a replacement
2009
+ input = input.replace(/\//g, '-');
2010
+ return input;
1666
2011
  }
1667
2012
 
1668
2013
  /** Set the options to show in the list */
@@ -1728,17 +2073,19 @@ function reloadOptions(dt, config, idx, checkList, loadedValues) {
1728
2073
  options = jsonOptions;
1729
2074
  }
1730
2075
  else if (json && config.ajaxOnly) {
1731
- // Ajax only options - need to hide the search list
1732
- checkList.element().style.display = 'none';
1733
- // Check if the parent buttons should be hidden as well (they will be if there
1734
- // is no visible content in them)
1735
- if (config._parents) {
1736
- config._parents.forEach(function (btn) { return btn.checkDisplay(); });
2076
+ if (config.hidable) {
2077
+ // Ajax only options - need to hide the search list
2078
+ checkList.element().style.display = 'none';
2079
+ // Check if the parent buttons should be hidden as well (they will be if there
2080
+ // is no visible content in them)
2081
+ if (config._parents) {
2082
+ config._parents.forEach(function (btn) { return btn.checkDisplay(); });
2083
+ }
1737
2084
  }
1738
2085
  // No point in doing any further processing here
1739
2086
  return;
1740
2087
  }
1741
- else {
2088
+ else if (!dt.page.info().serverSide) {
1742
2089
  // Either no ajax object (i.e. not an Ajax table), or no matching ajax options
1743
2090
  // for this column - get the values for the column, taking into account
1744
2091
  // orthogonal rendering
@@ -1746,11 +2093,14 @@ function reloadOptions(dt, config, idx, checkList, loadedValues) {
1746
2093
  var rows = dt.rows({ order: idx }).indexes().toArray();
1747
2094
  var settings = dt.settings()[0];
1748
2095
  for (var i = 0; i < rows.length; i++) {
1749
- var filter = settings.fastData(rows[i], idx, 'filter').toString();
2096
+ var raw = settings.fastData(rows[i], idx, 'filter');
2097
+ var filter = raw !== null && raw !== undefined
2098
+ ? raw.toString()
2099
+ : '';
1750
2100
  if (!found[filter]) {
1751
2101
  found[filter] = true;
1752
2102
  options.push({
1753
- label: settings.fastData(rows[i], idx, 'display'),
2103
+ label: settings.fastData(rows[i], idx, config.orthogonal),
1754
2104
  value: filter
1755
2105
  });
1756
2106
  }
@@ -1767,7 +2117,9 @@ var searchList = {
1767
2117
  defaults: {
1768
2118
  ajaxOnly: true,
1769
2119
  className: 'searchList',
2120
+ hidable: true,
1770
2121
  options: null,
2122
+ orthogonal: 'display',
1771
2123
  search: true,
1772
2124
  select: true,
1773
2125
  title: ''
@@ -1779,6 +2131,10 @@ var searchList = {
1779
2131
  // The search can be applied from a stored start at start up before the options are
1780
2132
  // available. It can also be applied by user input, so it is generalised into this function.
1781
2133
  var applySearch = function (values) {
2134
+ // When SSP, don't do any client-side filtering
2135
+ if (dt.page.info().serverSide) {
2136
+ return;
2137
+ }
1782
2138
  var col = dt.column(_this.idx());
1783
2139
  if (!values) {
1784
2140
  return;
@@ -1798,11 +2154,11 @@ var searchList = {
1798
2154
  config._parents.forEach(function (btn) { return btn.activeList(_this.unique(), !!values.length); });
1799
2155
  }
1800
2156
  };
1801
- var checkList = new CheckList(dt, {
2157
+ var checkList = new CheckList(dt, this, {
1802
2158
  search: config.search,
1803
2159
  select: config.select
1804
2160
  })
1805
- .searchListener(dt, this)
2161
+ .searchListener(dt)
1806
2162
  .title(dt
1807
2163
  .i18n('columnControl.searchList', config.title)
1808
2164
  .replace('[title]', dt.column(this.idx()).title()))
@@ -1822,14 +2178,24 @@ var searchList = {
1822
2178
  dt.ready(function () {
1823
2179
  reloadOptions(dt, config, _this.idx(), checkList, loadedValues);
1824
2180
  });
2181
+ // Xhr event listener for updates of options
2182
+ dt.on('xhr', function (e, s, json) {
2183
+ // Need to wait for the draw to complete so the table has the latest data
2184
+ dt.one('draw', function () {
2185
+ reloadOptions(dt, config, _this.idx(), checkList, loadedValues);
2186
+ });
2187
+ });
1825
2188
  }
1826
- // Xhr event listener for updates of options
1827
- dt.on('xhr', function (e, s, json) {
1828
- // Need to wait for the draw to complete so the table has the latest data
1829
- dt.one('draw', function () {
1830
- reloadOptions(dt, config, _this.idx(), checkList, loadedValues);
2189
+ // Data for server-side processing
2190
+ if (dt.page.info().serverSide) {
2191
+ dt.on('preXhr.DT', function (e, s, d) {
2192
+ if (!d.columns[_this.idx()].columnControl) {
2193
+ d.columns[_this.idx()].columnControl = {};
2194
+ }
2195
+ // We need the indexes in the HTTP parameter names (for .NET), so use an object.
2196
+ d.columns[_this.idx()].columnControl.list = Object.assign({}, checkList.values());
1831
2197
  });
1832
- });
2198
+ }
1833
2199
  // Unlike the SearchInput based search contents, CheckList does not handle state saving
1834
2200
  // (since the mechanism for column visibility is different), so state saving is handled
1835
2201
  // here.
@@ -1837,6 +2203,7 @@ var searchList = {
1837
2203
  var values = getState(_this.idx(), state);
1838
2204
  if (values) {
1839
2205
  checkList.values(values);
2206
+ applySearch(values);
1840
2207
  }
1841
2208
  });
1842
2209
  dt.on('stateSaveParams', function (e, s, data) {
@@ -1854,6 +2221,14 @@ var searchList = {
1854
2221
  ? checkList.values()
1855
2222
  : loadedValues;
1856
2223
  });
2224
+ dt.settings()[0].aoColumns[this.idx()].columnControlSearchList = function (options) {
2225
+ if (options === 'refresh') {
2226
+ reloadOptions(dt, config, _this.idx(), checkList, null);
2227
+ }
2228
+ else {
2229
+ setOptions(checkList, options);
2230
+ }
2231
+ };
1857
2232
  loadedValues = getState(this.idx(), dt.state.loaded());
1858
2233
  applySearch(loadedValues);
1859
2234
  return checkList.element();
@@ -1892,6 +2267,13 @@ var searchNumber = {
1892
2267
  { label: dt.i18n(i18nBase + 'notEmpty', 'Not empty'), value: 'notEmpty' }
1893
2268
  ])
1894
2269
  .search(function (searchType, searchTerm, loadingState) {
2270
+ // When SSP, don't apply a filter here, SearchInput will add to the submit data
2271
+ if (dt.page.info().serverSide) {
2272
+ if (!loadingState) {
2273
+ dt.draw();
2274
+ }
2275
+ return;
2276
+ }
1895
2277
  var column = dt.column(_this.idx());
1896
2278
  if (searchType === 'empty') {
1897
2279
  column.search.fixed('dtcc', function (haystack) { return !haystack; });
@@ -1989,6 +2371,13 @@ var searchText = {
1989
2371
  { label: dt.i18n(i18nBase + 'notEmpty', 'Not empty'), value: 'notEmpty' }
1990
2372
  ])
1991
2373
  .search(function (searchType, searchTerm, loadingState) {
2374
+ // When SSP, don't apply a filter here, SearchInput will add to the submit data
2375
+ if (dt.page.info().serverSide) {
2376
+ if (!loadingState) {
2377
+ dt.draw();
2378
+ }
2379
+ return;
2380
+ }
1992
2381
  var column = dt.column(_this.idx());
1993
2382
  searchTerm = searchTerm.toLowerCase();
1994
2383
  if (searchType === 'empty') {
@@ -2095,7 +2484,7 @@ var search = {
2095
2484
  }
2096
2485
  };
2097
2486
 
2098
- var searchClear = {
2487
+ var searchClear$1 = {
2099
2488
  defaults: {
2100
2489
  className: 'searchClear',
2101
2490
  icon: 'searchClear',
@@ -2104,12 +2493,12 @@ var searchClear = {
2104
2493
  init: function (config) {
2105
2494
  var _this = this;
2106
2495
  var dt = this.dt();
2107
- var btn = new Button(dt)
2496
+ var btn = new Button(dt, this)
2108
2497
  .text(dt.i18n('columnControl.searchClear', config.text))
2109
2498
  .icon(config.icon)
2110
2499
  .className(config.className)
2111
2500
  .handler(function () {
2112
- dt.column(_this.idx()).ccSearchClear().draw();
2501
+ dt.column(_this.idx()).columnControl.searchClear().draw();
2113
2502
  })
2114
2503
  .enable(false);
2115
2504
  dt.on('draw', function () {
@@ -2129,7 +2518,9 @@ var searchDropdown = {
2129
2518
  className: 'searchDropdown',
2130
2519
  clear: true,
2131
2520
  columns: '',
2521
+ hidable: true,
2132
2522
  options: null,
2523
+ orthogonal: 'display',
2133
2524
  placeholder: '',
2134
2525
  search: true,
2135
2526
  select: true,
@@ -2186,6 +2577,10 @@ var contentTypes = {
2186
2577
  reorder: reorder,
2187
2578
  reorderLeft: reorderLeft,
2188
2579
  reorderRight: reorderRight,
2580
+ rowGroup: rowGroup,
2581
+ rowGroupAdd: rowGroupAdd,
2582
+ rowGroupClear: rowGroupClear,
2583
+ rowGroupRemove: rowGroupRemove,
2189
2584
  order: order,
2190
2585
  orderAddAsc: orderAddAsc,
2191
2586
  orderAddDesc: orderAddDesc,
@@ -2195,7 +2590,7 @@ var contentTypes = {
2195
2590
  orderRemove: orderRemove,
2196
2591
  orderStatus: orderStatus,
2197
2592
  search: search,
2198
- searchClear: searchClear,
2593
+ searchClear: searchClear$1,
2199
2594
  searchDropdown: searchDropdown,
2200
2595
  searchDateTime: searchDateTime,
2201
2596
  searchList: searchList,
@@ -2225,7 +2620,8 @@ var ColumnControl = /** @class */ (function () {
2225
2620
  this._c = {};
2226
2621
  this._s = {
2227
2622
  columnIdx: null,
2228
- unique: null
2623
+ unique: null,
2624
+ toDestroy: []
2229
2625
  };
2230
2626
  this._dt = dt;
2231
2627
  this._s.columnIdx = columnIdx;
@@ -2251,10 +2647,33 @@ var ColumnControl = /** @class */ (function () {
2251
2647
  _this._dom.wrapper.appendChild(el);
2252
2648
  });
2253
2649
  dt.on('destroy', function () {
2650
+ _this._s.toDestroy.slice().forEach(function (el) {
2651
+ el.destroy();
2652
+ });
2254
2653
  _this._dom.wrapper.remove();
2255
2654
  });
2256
2655
  }
2257
2656
  }
2657
+ /**
2658
+ * Add a component to the destroy list. This is so there is a single destroy event handler,
2659
+ * which is much better for performance.
2660
+ *
2661
+ * @param component Any instance with a `destroy` method
2662
+ */
2663
+ ColumnControl.prototype.destroyAdd = function (component) {
2664
+ this._s.toDestroy.push(component);
2665
+ };
2666
+ /**
2667
+ * Remove an instance from the destroy list (it has been destroyed itself)
2668
+ *
2669
+ * @param component Any instance with a `destroy` method
2670
+ */
2671
+ ColumnControl.prototype.destroyRemove = function (component) {
2672
+ var idx = this._s.toDestroy.indexOf(component);
2673
+ if (idx !== -1) {
2674
+ this._s.toDestroy.splice(idx, 1);
2675
+ }
2676
+ };
2258
2677
  /**
2259
2678
  * Get the DataTables API instance that hosts this instance of ColumnControl
2260
2679
  *
@@ -2365,7 +2784,7 @@ var ColumnControl = /** @class */ (function () {
2365
2784
  /** SVG icons that can be used by the content plugins */
2366
2785
  ColumnControl.icons = icons;
2367
2786
  /** Version */
2368
- ColumnControl.version = '1.0.6';
2787
+ ColumnControl.version = '1.1.0';
2369
2788
  return ColumnControl;
2370
2789
  }());
2371
2790
 
@@ -2394,7 +2813,9 @@ $(document).on('i18n.dt', function (e, settings) {
2394
2813
  settings.titleRow = 0;
2395
2814
  }
2396
2815
  identifyTargets(baseTargets, tableInit);
2397
- identifyTargets(baseTargets, defaultInit);
2816
+ if (ColumnControl.defaults.content) {
2817
+ identifyTargets(baseTargets, defaultInit);
2818
+ }
2398
2819
  api.columns().every(function (i) {
2399
2820
  var columnInit = this.init().columnControl;
2400
2821
  identifyTargets(baseTargets, columnInit);
@@ -2414,7 +2835,10 @@ $(document).on('preInit.dt', function (e, settings) {
2414
2835
  var defaultInit = ColumnControl.defaults;
2415
2836
  var baseTargets = [];
2416
2837
  identifyTargets(baseTargets, tableInit);
2417
- identifyTargets(baseTargets, defaultInit);
2838
+ // Only add the default target if there is actually content for it
2839
+ if (ColumnControl.defaults.content) {
2840
+ identifyTargets(baseTargets, defaultInit);
2841
+ }
2418
2842
  api.columns().every(function (i) {
2419
2843
  var columnInit = this.init().columnControl;
2420
2844
  var targets = identifyTargets(baseTargets.slice(), columnInit);
@@ -2432,12 +2856,20 @@ $(document).on('preInit.dt', function (e, settings) {
2432
2856
  }
2433
2857
  });
2434
2858
  });
2435
- DataTable.Api.registerPlural('columns().ccSearchClear()', 'column().ccSearchClear()', function () {
2859
+ function searchClear() {
2436
2860
  var ctx = this;
2437
2861
  return this.iterator('column', function (settings, idx) {
2438
2862
  // Note that the listeners for this will not redraw the table.
2439
2863
  ctx.trigger('cc-search-clear', [idx]);
2440
2864
  });
2865
+ }
2866
+ DataTable.Api.registerPlural('columns().columnControl.searchClear()', 'column().columnControl.searchClear()', searchClear);
2867
+ // Legacy (1.0.x)) - was never documented, but was mentioned in the forum
2868
+ DataTable.Api.registerPlural('columns().ccSearchClear()', 'column().ccSearchClear()', searchClear);
2869
+ DataTable.Api.registerPlural('columns().columnControl.searchList()', 'column().columnControl.searchList()', function (options) {
2870
+ return this.iterator('column', function (settings, idx) {
2871
+ settings.aoColumns[idx].columnControlSearchList(options);
2872
+ });
2441
2873
  });
2442
2874
  DataTable.ext.buttons.ccSearchClear = {
2443
2875
  text: function (dt) {
@@ -2445,7 +2877,7 @@ DataTable.ext.buttons.ccSearchClear = {
2445
2877
  },
2446
2878
  init: function (dt, node, config) {
2447
2879
  var _this = this;
2448
- dt.on('draw', function () {
2880
+ dt.on('draw.DT', function () {
2449
2881
  var enabled = false;
2450
2882
  var glob = !!dt.search();
2451
2883
  // No point in wasting clock cycles if we already know it will be enabled
@@ -2462,7 +2894,7 @@ DataTable.ext.buttons.ccSearchClear = {
2462
2894
  },
2463
2895
  action: function (e, dt, node, config) {
2464
2896
  dt.search('');
2465
- dt.columns().ccSearchClear();
2897
+ dt.columns().columnControl.searchClear();
2466
2898
  dt.draw();
2467
2899
  }
2468
2900
  };
@@ -2598,12 +3030,18 @@ function identifyTargets(targets, input) {
2598
3030
  }
2599
3031
  }
2600
3032
  if (Array.isArray(input)) {
2601
- // Array of options, or an array of content
2602
- input.forEach(function (item) {
2603
- add(typeof item === 'object' && item.target !== undefined
2604
- ? item.target
2605
- : ColumnControl.defaults.target);
2606
- });
3033
+ if (input.length === 0) {
3034
+ // Empty array - assume it is empty content
3035
+ add(ColumnControl.defaults.target);
3036
+ }
3037
+ else {
3038
+ // Array of options, or an array of content
3039
+ input.forEach(function (item) {
3040
+ add(typeof item === 'object' && item.target !== undefined
3041
+ ? item.target
3042
+ : ColumnControl.defaults.target);
3043
+ });
3044
+ }
2607
3045
  }
2608
3046
  else if (typeof input === 'object') {
2609
3047
  // Full options defined: { target: x, content: [] }
@@ -2618,9 +3056,7 @@ function identifyTargets(targets, input) {
2618
3056
  * @returns true if it is a config object
2619
3057
  */
2620
3058
  function isIConfig(item) {
2621
- return typeof item === 'object' && item.target !== undefined
2622
- ? true
2623
- : false;
3059
+ return typeof item === 'object' && item.target !== undefined ? true : false;
2624
3060
  }
2625
3061
  /**
2626
3062
  * Determine if an array contains only content items or not