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
@@ -62,6 +62,14 @@ var icons = {
62
62
  greater: wrap('<path d="m9 18 6-6-6-6"/>'),
63
63
  // Custom
64
64
  greaterOrEqual: wrap('<path d="m9 16 6-6-6-6"/><path d="m9 21 6-6"/>'),
65
+ // Custom
66
+ 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"/>'),
67
+ // Custom
68
+ 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"/>'),
69
+ // Custom
70
+ 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"/>'),
71
+ // Custom
72
+ 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"/>'),
65
73
  less: wrap('<path d="m15 18-6-6 6-6"/>'),
66
74
  // Custom
67
75
  lessOrEqual: wrap('<path d="m15 16-6-6 6-6"/><path d="m15 21-6-6"/>'),
@@ -168,6 +176,7 @@ function attachDropdown(dropdown, dt, btn) {
168
176
  dropdown._shown = true;
169
177
  dtContainer.append(dropdown);
170
178
  positionDropdown(dropdown, dt, btn.element());
179
+ btn.element().setAttribute('aria-expanded', 'true');
171
180
  // Note that this could be called when the dropdown has already been removed from the document
172
181
  // via another dropdown being shown. This will clean up the event on the next body click.
173
182
  var removeDropdown = function (e) {
@@ -180,6 +189,12 @@ function attachDropdown(dropdown, dt, btn) {
180
189
  if (e.target === dropdown || dropdown.contains(e.target)) {
181
190
  return;
182
191
  }
192
+ // If there is currently a datetime picker visible on the page, assume that it belongs to
193
+ // this dropdown. Don't want to close while operating on the picker.
194
+ var datetime = document.querySelector('div.dt-datetime');
195
+ if (datetime && (e.target === datetime || datetime.contains(e.target))) {
196
+ return;
197
+ }
183
198
  dropdown._close();
184
199
  document.body.removeEventListener('click', removeDropdown);
185
200
  };
@@ -277,6 +292,7 @@ var dropdownContent = {
277
292
  dropdown._close = function () {
278
293
  dropdown.remove();
279
294
  dropdown._shown = false;
295
+ btn.element().setAttribute('aria-expanded', 'false');
280
296
  };
281
297
  dropdown.setAttribute('role', 'dialog');
282
298
  dropdown.setAttribute('aria-label', dt.i18n('columnControl.dropdown', config.text));
@@ -289,7 +305,7 @@ var dropdownContent = {
289
305
  });
290
306
  // A liner element allows more styling options, so the contents go inside this
291
307
  var liner = dropdown.childNodes[0];
292
- var btn = new Button(dt)
308
+ var btn = new Button(dt, this)
293
309
  .text(dt.i18n('columnControl.dropdown', config.text))
294
310
  .icon(config.icon)
295
311
  .className(config.className)
@@ -308,6 +324,7 @@ var dropdownContent = {
308
324
  }
309
325
  });
310
326
  btn.element().setAttribute('aria-haspopup', 'dialog');
327
+ btn.element().setAttribute('aria-expanded', 'false');
311
328
  // Add the content for the dropdown
312
329
  for (var i = 0; i < config.content.length; i++) {
313
330
  var content = this.resolve(config.content[i]);
@@ -344,18 +361,20 @@ var Button = /** @class */ (function () {
344
361
  * Create a new button for use in ColumnControl contents. Buttons created by this class can be
345
362
  * used at the top level in the header or in a dropdown.
346
363
  */
347
- function Button(dt) {
364
+ function Button(dt, host) {
348
365
  this._s = {
349
366
  active: false,
350
367
  activeList: [],
351
368
  buttonClick: null,
352
369
  dt: null,
353
370
  enabled: true,
371
+ host: null,
354
372
  label: '',
355
373
  namespace: '',
356
374
  value: null
357
375
  };
358
376
  this._s.dt = dt;
377
+ this._s.host = host;
359
378
  this._dom = {
360
379
  button: createElement('button', Button.classes.container),
361
380
  dropdownDisplay: null,
@@ -433,9 +452,7 @@ var Button = /** @class */ (function () {
433
452
  this._dom.button.removeEventListener('click', this._s.buttonClick);
434
453
  this._dom.button.removeEventListener('keypress', this._s.buttonClick);
435
454
  }
436
- if (this._s.namespace) {
437
- this._s.dt.off('destroy.' + this._s.namespace);
438
- }
455
+ this._s.host.destroyRemove(this);
439
456
  };
440
457
  /**
441
458
  * Relevant for drop downs only. When a button in a dropdown is hidden, we might want to
@@ -457,13 +474,10 @@ var Button = /** @class */ (function () {
457
474
  Button.prototype.element = function () {
458
475
  return this._dom.button;
459
476
  };
460
- /**
461
- * Set if the button should be enabled or not.
462
- *
463
- * @param enable Toggle the enable state
464
- * @returns Button instance
465
- */
466
477
  Button.prototype.enable = function (enable) {
478
+ if (enable === undefined) {
479
+ return this._s.enabled;
480
+ }
467
481
  this._dom.button.classList.toggle('dtcc-button_disabled', !enable);
468
482
  this._s.enabled = enable;
469
483
  return this;
@@ -500,10 +514,7 @@ var Button = /** @class */ (function () {
500
514
  this._s.namespace = 'dtcc-' + _namespace++;
501
515
  this._dom.button.addEventListener('click', buttonClick);
502
516
  this._dom.button.addEventListener('keypress', buttonClick);
503
- // Use a unique namespace to be able to easily remove per button
504
- this._s.dt.on('destroy.' + this._s.namespace, function () {
505
- _this.destroy();
506
- });
517
+ this._s.host.destroyAdd(this);
507
518
  return this;
508
519
  };
509
520
  /**
@@ -558,19 +569,22 @@ var CheckList = /** @class */ (function () {
558
569
  /**
559
570
  * Container for a list of buttons
560
571
  */
561
- function CheckList(dt, opts) {
572
+ function CheckList(dt, host, opts) {
562
573
  var _this = this;
563
574
  this._s = {
564
575
  buttons: [],
565
576
  dt: null,
566
577
  handler: function () { },
578
+ host: null,
567
579
  search: ''
568
580
  };
569
581
  this._s.dt = dt;
582
+ this._s.host = host;
570
583
  this._dom = {
571
584
  buttons: createElement('div', 'dtcc-list-buttons'),
572
585
  container: createElement('div', CheckList.classes.container),
573
586
  controls: createElement('div', 'dtcc-list-controls'),
587
+ empty: createElement('div', 'dtcc-list-empty', dt.i18n('columnControl.list.empty', 'No options')),
574
588
  title: createElement('div', 'dtcc-list-title'),
575
589
  selectAll: createElement('button', 'dtcc-list-selectAll', dt.i18n('columnControl.list.all', 'Select all')),
576
590
  selectAllCount: createElement('span'),
@@ -582,6 +596,7 @@ var CheckList = /** @class */ (function () {
582
596
  dom.search.setAttribute('type', 'text');
583
597
  dom.container.append(dom.title);
584
598
  dom.container.append(dom.controls);
599
+ dom.container.append(dom.empty);
585
600
  dom.container.append(dom.buttons);
586
601
  if (opts.select) {
587
602
  dom.controls.append(dom.selectAll);
@@ -632,7 +647,7 @@ var CheckList = /** @class */ (function () {
632
647
  }
633
648
  var _loop_1 = function (i) {
634
649
  var option = options[i];
635
- var btn = new Button(this_1._s.dt)
650
+ var btn = new Button(this_1._s.dt, this_1._s.host)
636
651
  .active(option.active || false)
637
652
  .handler(function (e) {
638
653
  _this._s.handler(e, btn, _this._s.buttons, true);
@@ -641,7 +656,7 @@ var CheckList = /** @class */ (function () {
641
656
  .icon(option.icon || '')
642
657
  .text(option.label !== ''
643
658
  ? option.label
644
- : this_1._s.dt.i18n('columnControl.list.emptyOption', 'Empty'))
659
+ : this_1._s.dt.i18n('columnControl.list.empty', 'Empty'))
645
660
  .value(option.value);
646
661
  if (option.label === '') {
647
662
  btn.className('empty');
@@ -712,11 +727,11 @@ var CheckList = /** @class */ (function () {
712
727
  * @param dt DataTable instance
713
728
  * @param idx Column index
714
729
  */
715
- CheckList.prototype.searchListener = function (dt, parent) {
730
+ CheckList.prototype.searchListener = function (dt) {
716
731
  var _this = this;
717
- // Column control search clearing (column().ccSearchClear() method)
732
+ // Column control search clearing (column().columnControl.searchClear() method)
718
733
  dt.on('cc-search-clear', function (e, colIdx) {
719
- if (colIdx === parent.idx()) {
734
+ if (colIdx === _this._s.host.idx()) {
720
735
  _this.selectNone();
721
736
  _this._s.handler(e, null, _this._s.buttons, false);
722
737
  _this._s.search = '';
@@ -804,6 +819,8 @@ var CheckList = /** @class */ (function () {
804
819
  el.appendChild(btn.element());
805
820
  }
806
821
  }
822
+ this._dom.empty.style.display = buttons.length === 0 ? 'block' : 'none';
823
+ el.style.display = buttons.length > 0 ? 'block' : 'none';
807
824
  };
808
825
  CheckList.classes = {
809
826
  container: 'dtcc-list',
@@ -822,7 +839,7 @@ var colVis = {
822
839
  },
823
840
  init: function (config) {
824
841
  var dt = this.dt();
825
- var checkList = new CheckList(dt, {
842
+ var checkList = new CheckList(dt, this, {
826
843
  search: config.search,
827
844
  select: config.select
828
845
  })
@@ -905,7 +922,7 @@ var reorder = {
905
922
  init: function (config) {
906
923
  var _this = this;
907
924
  var dt = this.dt();
908
- var btn = new Button(dt)
925
+ var btn = new Button(dt, this)
909
926
  .text(dt.i18n('columnControl.reorder', config.text))
910
927
  .icon(config.icon)
911
928
  .className(config.className)
@@ -938,7 +955,7 @@ var reorderLeft = {
938
955
  init: function (config) {
939
956
  var _this = this;
940
957
  var dt = this.dt();
941
- var btn = new Button(dt)
958
+ var btn = new Button(dt, this)
942
959
  .text(dt.i18n('columnControl.reorderLeft', config.text))
943
960
  .icon(config.icon)
944
961
  .className(config.className)
@@ -968,7 +985,7 @@ var reorderRight = {
968
985
  init: function (config) {
969
986
  var _this = this;
970
987
  var dt = this.dt();
971
- var btn = new Button(dt)
988
+ var btn = new Button(dt, this)
972
989
  .text(dt.i18n('columnControl.reorderRight', config.text))
973
990
  .icon(config.icon)
974
991
  .className(config.className)
@@ -1000,7 +1017,7 @@ var order = {
1000
1017
  init: function (config) {
1001
1018
  var _this = this;
1002
1019
  var dt = this.dt();
1003
- var btn = new Button(dt)
1020
+ var btn = new Button(dt, this)
1004
1021
  .text(dt.i18n('columnControl.order', config.text))
1005
1022
  .icon('orderAsc')
1006
1023
  .className(config.className);
@@ -1032,7 +1049,7 @@ var orderAddAsc = {
1032
1049
  init: function (config) {
1033
1050
  var _this = this;
1034
1051
  var dt = this.dt();
1035
- var btn = new Button(dt)
1052
+ var btn = new Button(dt, this)
1036
1053
  .text(dt.i18n('columnControl.orderAddAsc', config.text))
1037
1054
  .icon(config.icon)
1038
1055
  .className(config.className)
@@ -1058,7 +1075,7 @@ var orderAddDesc = {
1058
1075
  init: function (config) {
1059
1076
  var _this = this;
1060
1077
  var dt = this.dt();
1061
- var btn = new Button(dt)
1078
+ var btn = new Button(dt, this)
1062
1079
  .text(dt.i18n('columnControl.orderAddDesc', config.text))
1063
1080
  .icon(config.icon)
1064
1081
  .className(config.className)
@@ -1084,7 +1101,7 @@ var orderAsc = {
1084
1101
  init: function (config) {
1085
1102
  var _this = this;
1086
1103
  var dt = this.dt();
1087
- var btn = new Button(dt)
1104
+ var btn = new Button(dt, this)
1088
1105
  .text(dt.i18n('columnControl.orderAsc', config.text))
1089
1106
  .icon(config.icon)
1090
1107
  .className(config.className)
@@ -1114,7 +1131,7 @@ var orderClear = {
1114
1131
  },
1115
1132
  init: function (config) {
1116
1133
  var dt = this.dt();
1117
- var btn = new Button(dt)
1134
+ var btn = new Button(dt, this)
1118
1135
  .text(dt.i18n('columnControl.orderClear', config.text))
1119
1136
  .icon(config.icon)
1120
1137
  .className(config.className)
@@ -1140,7 +1157,7 @@ var orderDesc = {
1140
1157
  init: function (config) {
1141
1158
  var _this = this;
1142
1159
  var dt = this.dt();
1143
- var btn = new Button(dt)
1160
+ var btn = new Button(dt, this)
1144
1161
  .text(dt.i18n('columnControl.orderDesc', config.text))
1145
1162
  .icon(config.icon)
1146
1163
  .className(config.className)
@@ -1171,7 +1188,7 @@ var orderRemove = {
1171
1188
  init: function (config) {
1172
1189
  var _this = this;
1173
1190
  var dt = this.dt();
1174
- var btn = new Button(dt)
1191
+ var btn = new Button(dt, this)
1175
1192
  .text(dt.i18n('columnControl.orderRemove', config.text))
1176
1193
  .icon(config.icon)
1177
1194
  .className(config.className)
@@ -1205,6 +1222,190 @@ var orderStatus = {
1205
1222
  }
1206
1223
  };
1207
1224
 
1225
+ /**
1226
+ * Add an item to the grouping structure
1227
+ *
1228
+ * @param dt DataTable API instance
1229
+ * @param dataSrc Grouping data point to add
1230
+ * @returns Grouping array
1231
+ */
1232
+ function rowGroupAdd$1(dt, dataSrc) {
1233
+ var applied = rowGroupApplied(dt);
1234
+ var idx = applied.indexOf(dataSrc);
1235
+ if (idx === -1) {
1236
+ applied.push(dataSrc);
1237
+ dt.rowGroup().dataSrc(applied);
1238
+ }
1239
+ return applied;
1240
+ }
1241
+ /**
1242
+ * Always want an array return
1243
+ *
1244
+ * @param dt DataTable API instance
1245
+ * @returns
1246
+ */
1247
+ function rowGroupApplied(dt) {
1248
+ var applied = dt.rowGroup().dataSrc();
1249
+ return Array.isArray(applied)
1250
+ ? applied
1251
+ : [applied];
1252
+ }
1253
+ /**
1254
+ * Remove all grouping
1255
+ *
1256
+ * @param dt DataTable API instance
1257
+ */
1258
+ function rowGroupClear$1(dt) {
1259
+ dt.rowGroup().dataSrc([]);
1260
+ }
1261
+ /**
1262
+ * Remove an item from the grouping structure
1263
+ *
1264
+ * @param dt DataTable API instance
1265
+ * @param dataSrc Grouping data point to remove
1266
+ * @returns Grouping array
1267
+ */
1268
+ function rowGroupRemove$1(dt, dataSrc) {
1269
+ var applied = rowGroupApplied(dt);
1270
+ var idx = applied.indexOf(dataSrc);
1271
+ if (idx !== -1) {
1272
+ applied.splice(idx, 1);
1273
+ dt.rowGroup().dataSrc(applied);
1274
+ }
1275
+ return applied;
1276
+ }
1277
+ var rowGroup = {
1278
+ defaults: {
1279
+ className: 'rowGroup',
1280
+ icon: 'groupTop',
1281
+ order: true,
1282
+ text: 'Group rows'
1283
+ },
1284
+ init: function (config) {
1285
+ var _this = this;
1286
+ var dt = this.dt();
1287
+ var btn = new Button(dt, this)
1288
+ .text(dt.i18n('columnControl.rowGroup', config.text))
1289
+ .icon(config.icon)
1290
+ .className(config.className)
1291
+ .handler(function () {
1292
+ var dataSrc = dt.column(_this.idx()).dataSrc();
1293
+ if (btn.active()) {
1294
+ // Grouping is active - remove
1295
+ rowGroupRemove$1(dt, dataSrc);
1296
+ }
1297
+ else {
1298
+ // No grouping by this column yet, set it
1299
+ rowGroupClear$1(dt);
1300
+ rowGroupAdd$1(dt, dataSrc);
1301
+ if (config.order !== false) {
1302
+ dt.order([_this.idx(), 'asc']);
1303
+ }
1304
+ }
1305
+ dt.draw();
1306
+ });
1307
+ // Show as active when grouping is applied
1308
+ dt.on('rowgroup-datasrc', function () {
1309
+ var applied = rowGroupApplied(dt);
1310
+ var ours = dt.column(_this.idx()).dataSrc();
1311
+ btn.active(applied.includes(ours));
1312
+ });
1313
+ return btn.element();
1314
+ }
1315
+ };
1316
+
1317
+ var rowGroupAdd = {
1318
+ defaults: {
1319
+ className: 'rowGroupAdd',
1320
+ icon: 'groupAdd',
1321
+ order: true,
1322
+ text: 'Add to grouping'
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.enable()) {
1334
+ // No grouping by this column yet, add it
1335
+ rowGroupAdd$1(dt, dataSrc);
1336
+ }
1337
+ dt.draw();
1338
+ });
1339
+ // Show as active when grouping is applied
1340
+ dt.on('rowgroup-datasrc', function () {
1341
+ var applied = rowGroupApplied(dt);
1342
+ var ours = dt.column(_this.idx()).dataSrc();
1343
+ btn.enable(!applied.includes(ours));
1344
+ });
1345
+ return btn.element();
1346
+ }
1347
+ };
1348
+
1349
+ var rowGroupClear = {
1350
+ defaults: {
1351
+ className: 'rowGroupClear',
1352
+ icon: 'groupClear',
1353
+ text: 'Clear all grouping'
1354
+ },
1355
+ init: function (config) {
1356
+ var dt = this.dt();
1357
+ var btn = new Button(dt, this)
1358
+ .text(dt.i18n('columnControl.rowGroup', config.text))
1359
+ .icon(config.icon)
1360
+ .className(config.className)
1361
+ .handler(function () {
1362
+ rowGroupClear$1(dt);
1363
+ dt.draw();
1364
+ });
1365
+ // Show as active when any grouping is applied
1366
+ dt.on('rowgroup-datasrc', function () {
1367
+ btn.enable(rowGroupApplied(dt).length > 0);
1368
+ });
1369
+ // Default status
1370
+ btn.enable(rowGroupApplied(dt).length > 0);
1371
+ return btn.element();
1372
+ }
1373
+ };
1374
+
1375
+ var rowGroupRemove = {
1376
+ defaults: {
1377
+ className: 'rowGroupRemove',
1378
+ icon: 'groupRemove',
1379
+ order: true,
1380
+ text: 'Remove from grouping'
1381
+ },
1382
+ init: function (config) {
1383
+ var _this = this;
1384
+ var dt = this.dt();
1385
+ var btn = new Button(dt, this)
1386
+ .text(dt.i18n('columnControl.rowGroup', config.text))
1387
+ .icon(config.icon)
1388
+ .className(config.className)
1389
+ .handler(function () {
1390
+ var dataSrc = dt.column(_this.idx()).dataSrc();
1391
+ if (btn.enable()) {
1392
+ // Grouping is active - remove
1393
+ rowGroupRemove$1(dt, dataSrc);
1394
+ dt.draw();
1395
+ }
1396
+ });
1397
+ // Show as active when grouping is applied
1398
+ dt.on('rowgroup-datasrc', function () {
1399
+ var applied = rowGroupApplied(dt);
1400
+ var ours = dt.column(_this.idx()).dataSrc();
1401
+ btn.enable(applied.includes(ours));
1402
+ });
1403
+ // Default disabled
1404
+ btn.enable(false);
1405
+ return btn.element();
1406
+ }
1407
+ };
1408
+
1208
1409
  var SearchInput = /** @class */ (function () {
1209
1410
  /**
1210
1411
  * Create a container element, for consistent DOM structure and styling
@@ -1212,6 +1413,8 @@ var SearchInput = /** @class */ (function () {
1212
1413
  function SearchInput(dt, idx) {
1213
1414
  var _this = this;
1214
1415
  this._type = 'text';
1416
+ this._sspTransform = null;
1417
+ this._sspData = {};
1215
1418
  this._dt = dt;
1216
1419
  this._idx = idx;
1217
1420
  this._dom = {
@@ -1250,28 +1453,28 @@ var SearchInput = /** @class */ (function () {
1250
1453
  });
1251
1454
  // State handling - all components that use this class have the same state saving structure
1252
1455
  // so shared handling can be performed here.
1253
- dt.on('stateSaveParams', function (e, s, data) {
1456
+ dt.on('stateSaveParams.DT', function (e, s, data) {
1254
1457
  if (!data.columnControl) {
1255
1458
  data.columnControl = {};
1256
1459
  }
1257
- if (!data.columnControl[idx]) {
1258
- data.columnControl[idx] = {};
1460
+ if (!data.columnControl[_this._idx]) {
1461
+ data.columnControl[_this._idx] = {};
1259
1462
  }
1260
- data.columnControl[idx].searchInput = {
1463
+ data.columnControl[_this._idx].searchInput = {
1261
1464
  logic: dom.select.value,
1262
1465
  type: _this._type,
1263
1466
  value: dom.input.value
1264
1467
  };
1265
1468
  });
1266
- dt.on('stateLoaded', function (e, s, state) {
1469
+ dt.on('stateLoaded.DT', function (e, s, state) {
1267
1470
  _this._stateLoad(state);
1268
1471
  });
1269
1472
  // Same as for ColumnControl - reassign a column index if needed.
1270
- dt.on('columns-reordered', function (e, details) {
1473
+ dt.on('columns-reordered.DT', function (e, details) {
1271
1474
  _this._idx = dt.colReorder.transpose(originalIdx, 'fromOriginal');
1272
1475
  });
1273
- // Column control search clearing (column().ccSearchClear() method)
1274
- dt.on('cc-search-clear', function (e, colIdx) {
1476
+ // Column control search clearing (column().columnControl.searchClear() method)
1477
+ dt.on('cc-search-clear.DT', function (e, colIdx) {
1275
1478
  if (colIdx === _this._idx) {
1276
1479
  // Don't want an automatic redraw on this event
1277
1480
  _this._loadingState = true;
@@ -1279,6 +1482,23 @@ var SearchInput = /** @class */ (function () {
1279
1482
  _this._loadingState = false;
1280
1483
  }
1281
1484
  });
1485
+ // Data for server-side processing
1486
+ if (dt.page.info().serverSide) {
1487
+ dt.on('preXhr.DT', function (e, s, d) {
1488
+ if (!d.columns[_this._idx].columnControl) {
1489
+ d.columns[_this._idx].columnControl = {};
1490
+ }
1491
+ var val = _this._dom.input.value;
1492
+ if (_this._sspTransform) {
1493
+ val = _this._sspTransform(val);
1494
+ }
1495
+ d.columns[_this._idx].columnControl.search = Object.assign({
1496
+ value: val,
1497
+ logic: _this._dom.select.value,
1498
+ type: _this._type
1499
+ }, _this._sspData);
1500
+ });
1501
+ }
1282
1502
  }
1283
1503
  /**
1284
1504
  * Add a class to the container
@@ -1401,6 +1621,26 @@ var SearchInput = /** @class */ (function () {
1401
1621
  this.runSearch();
1402
1622
  return this;
1403
1623
  };
1624
+ /**
1625
+ * Set a function to transform the input value before SSP data submission
1626
+ *
1627
+ * @param fn Transform function
1628
+ * @returns Self for chaining
1629
+ */
1630
+ SearchInput.prototype.sspTransform = function (fn) {
1631
+ this._sspTransform = fn;
1632
+ return this;
1633
+ };
1634
+ /**
1635
+ * Set extra information to be send to the server for server-side processing
1636
+ *
1637
+ * @param data Data object
1638
+ * @returns Self for chaining
1639
+ */
1640
+ SearchInput.prototype.sspData = function (data) {
1641
+ this._sspData = data;
1642
+ return this;
1643
+ };
1404
1644
  /**
1405
1645
  * Set the text that will be shown as the title for the control
1406
1646
  *
@@ -1463,6 +1703,8 @@ var SearchInput = /** @class */ (function () {
1463
1703
  var searchDateTime = {
1464
1704
  defaults: {
1465
1705
  clear: true,
1706
+ format: '',
1707
+ mask: '',
1466
1708
  placeholder: '',
1467
1709
  title: '',
1468
1710
  titleAttr: ''
@@ -1474,11 +1716,14 @@ var searchDateTime = {
1474
1716
  var luxon = DataTable.use('luxon');
1475
1717
  var dt = this.dt();
1476
1718
  var i18nBase = 'columnControl.search.datetime.';
1477
- var displayFormat = '';
1719
+ var pickerFormat = '';
1720
+ var dataSrcFormat = '';
1478
1721
  var dateTime;
1479
1722
  var searchInput = new SearchInput(dt, this.idx())
1480
1723
  .type('date')
1481
1724
  .addClass('dtcc-searchDateTime')
1725
+ .sspTransform(function (val) { return toISO(val, pickerFormat, moment, luxon); })
1726
+ .sspData({ mask: config.mask })
1482
1727
  .clearable(config.clear)
1483
1728
  .placeholder(config.placeholder)
1484
1729
  .title(config.title)
@@ -1492,10 +1737,18 @@ var searchDateTime = {
1492
1737
  { label: dt.i18n(i18nBase + 'notEmpty', 'Not empty'), value: 'notEmpty' }
1493
1738
  ])
1494
1739
  .search(function (searchType, searchTerm, loadingState) {
1740
+ // When SSP, don't apply a filter here, SearchInput will add to the submit data
1741
+ if (dt.page.info().serverSide) {
1742
+ if (!loadingState) {
1743
+ dt.draw();
1744
+ }
1745
+ return;
1746
+ }
1747
+ var mask = config.mask;
1495
1748
  var column = dt.column(_this.idx());
1496
1749
  var search = searchTerm === ''
1497
1750
  ? ''
1498
- : dateToNum(dateTime && fromPicker ? dateTime.val() : searchTerm.trim(), displayFormat, moment, luxon);
1751
+ : dateToNum(dateTime && fromPicker ? dateTime.val() : searchTerm.trim(), pickerFormat, moment, luxon, mask);
1499
1752
  if (searchType === 'empty') {
1500
1753
  column.search.fixed('dtcc', function (haystack) { return !haystack; });
1501
1754
  }
@@ -1514,16 +1767,24 @@ var searchDateTime = {
1514
1767
  // Use a function for matching - weak typing
1515
1768
  // Note that the haystack in the search function is the rendered date - it
1516
1769
  // might need to be converted back to a date
1517
- column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) == search; });
1770
+ column.search.fixed('dtcc', function (haystack) {
1771
+ return dateToNum(haystack, dataSrcFormat, moment, luxon, mask) == search;
1772
+ });
1518
1773
  }
1519
1774
  else if (searchType === 'notEqual') {
1520
- column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) != search; });
1775
+ column.search.fixed('dtcc', function (haystack) {
1776
+ return dateToNum(haystack, dataSrcFormat, moment, luxon, mask) != search;
1777
+ });
1521
1778
  }
1522
1779
  else if (searchType === 'greater') {
1523
- column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) > search; });
1780
+ column.search.fixed('dtcc', function (haystack) {
1781
+ return dateToNum(haystack, dataSrcFormat, moment, luxon, mask) > search;
1782
+ });
1524
1783
  }
1525
1784
  else if (searchType === 'less') {
1526
- column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) < search; });
1785
+ column.search.fixed('dtcc', function (haystack) {
1786
+ return dateToNum(haystack, dataSrcFormat, moment, luxon, mask) < search;
1787
+ });
1527
1788
  }
1528
1789
  // If in a dropdown, set the parent levels as active
1529
1790
  if (config._parents) {
@@ -1538,10 +1799,13 @@ var searchDateTime = {
1538
1799
  // Once data has been loaded we can run DateTime with the specified format
1539
1800
  dt.ready(function () {
1540
1801
  var DateTime = DataTable.use('datetime');
1541
- displayFormat = getFormat(dt, _this.idx());
1802
+ dataSrcFormat = getFormat(dt, _this.idx());
1803
+ pickerFormat = config.format
1804
+ ? config.format
1805
+ : dataSrcFormat;
1542
1806
  if (DateTime) {
1543
1807
  dateTime = new DateTime(searchInput.input(), {
1544
- format: displayFormat,
1808
+ format: pickerFormat,
1545
1809
  i18n: dt.settings()[0].oLanguage.datetime, // could be undefined
1546
1810
  onChange: function () {
1547
1811
  fromPicker = true;
@@ -1568,18 +1832,36 @@ function getFormat(dt, column) {
1568
1832
  return 'YYYY-MM-DD';
1569
1833
  }
1570
1834
  else if (type === 'datetime') {
1571
- // If no format was specified in the DT type, then we need to use Moment / Luxon's default
1572
- // locale formatting.
1573
- var moment = DataTable.use('moment');
1574
- var luxon = DataTable.use('luxon');
1575
- if (moment) {
1576
- return moment().creationData().locale._longDateFormat.L;
1577
- }
1578
- if (luxon) {
1579
- // Luxon doesn't appear to provide a way to let us get the default locale formatting
1580
- // string, so we need to attempt to decode it.
1581
- return luxon.DateTime.fromISO('1999-08-07')
1582
- .toLocaleString()
1835
+ // If no format was specified in the DT type, a Javascript native toLocaleDateString
1836
+ // was used. Need to work out what that format is in Moment or Luxon. We need to pass
1837
+ // a known value though the renderer and work out the format
1838
+ var renderer = dt.settings()[0].aoColumns[column].mRender;
1839
+ var resultPm = renderer('1999-08-07T23:05:04Z', 'display');
1840
+ var resultAm = renderer('1999-08-07T03:05:04Z', 'display');
1841
+ var leadingZero = resultAm.includes('03');
1842
+ // What formatter are we using?
1843
+ if (DataTable.use('moment')) {
1844
+ return resultPm
1845
+ .replace('23', leadingZero ? 'HH' : 'H')
1846
+ .replace('11', leadingZero ? 'hh' : 'h')
1847
+ .replace('05', 'mm')
1848
+ .replace('04', 'ss')
1849
+ .replace('PM', 'A')
1850
+ .replace('pm', 'a')
1851
+ .replace('07', 'DD')
1852
+ .replace('7', 'D')
1853
+ .replace('08', 'MM')
1854
+ .replace('8', 'M')
1855
+ .replace('1999', 'YYYY')
1856
+ .replace('99', 'YY');
1857
+ }
1858
+ else if (DataTable.use('luxon')) {
1859
+ return resultPm
1860
+ .replace('23', leadingZero ? 'HH' : 'H')
1861
+ .replace('11', leadingZero ? 'hh' : 'h')
1862
+ .replace('05', 'mm')
1863
+ .replace('04', 'ss')
1864
+ .replace('PM', 'a')
1583
1865
  .replace('07', 'dd')
1584
1866
  .replace('7', 'd')
1585
1867
  .replace('08', 'MM')
@@ -1587,6 +1869,13 @@ function getFormat(dt, column) {
1587
1869
  .replace('1999', 'yyyy')
1588
1870
  .replace('99', 'yy');
1589
1871
  }
1872
+ else if (resultPm.includes('23') && resultPm.includes('1999')) {
1873
+ return 'YYYY-MM-DD hh:mm:ss';
1874
+ }
1875
+ else if (resultPm.includes('23')) {
1876
+ return 'hh:mm:ss';
1877
+ }
1878
+ // fall through to final return
1590
1879
  }
1591
1880
  else if (type.includes('datetime-')) {
1592
1881
  // Column was specified with a particular display format - we can extract that format from
@@ -1610,19 +1899,75 @@ function getFormat(dt, column) {
1610
1899
  * @param luxon Luxon object, if it is available
1611
1900
  * @returns Time stamp - milliseconds
1612
1901
  */
1613
- function dateToNum(input, srcFormat, moment, luxon) {
1902
+ function dateToNum(input, srcFormat, moment, luxon, mask) {
1903
+ var d;
1614
1904
  if (input === '') {
1615
1905
  return '';
1616
1906
  }
1617
- else if (input instanceof Date) {
1618
- return input.getTime();
1907
+ if (input instanceof Date) {
1908
+ d = input;
1619
1909
  }
1620
1910
  else if (srcFormat !== 'YYYY-MM-DD' && (moment || luxon)) {
1621
- return moment
1911
+ d = new Date(moment
1622
1912
  ? moment(input, srcFormat).unix() * 1000
1623
- : luxon.DateTime.fromFormat(input, srcFormat).toMillis();
1913
+ : luxon.DateTime.fromFormat(input, srcFormat).toMillis());
1914
+ }
1915
+ else {
1916
+ // new Date() with `/` separators will treat the input as local time, but with `-` it will
1917
+ // treat it as UTC. We want UTC so do a replacement
1918
+ d = new Date(input.replace(/\//g, '-'));
1624
1919
  }
1625
- return new Date(input).getTime();
1920
+ if (mask) {
1921
+ if (!mask.includes('YYYY')) {
1922
+ d.setFullYear(1970);
1923
+ }
1924
+ if (!mask.includes('MM')) {
1925
+ d.setUTCMonth(0);
1926
+ }
1927
+ if (!mask.includes('DD')) {
1928
+ d.setUTCDate(1);
1929
+ }
1930
+ if (!mask.includes('hh')) {
1931
+ d.setUTCHours(0);
1932
+ }
1933
+ if (!mask.includes('mm')) {
1934
+ d.setUTCMinutes(0);
1935
+ }
1936
+ if (!mask.includes('ss')) {
1937
+ // This will match milliseconds as well, but that's fine, you won't match mS but not S
1938
+ d.setUTCSeconds(0);
1939
+ }
1940
+ if (!mask.includes('sss')) {
1941
+ d.setUTCMilliseconds(0);
1942
+ }
1943
+ }
1944
+ return d.getTime();
1945
+ }
1946
+ /**
1947
+ * Convert an input string to an ISO formatted date
1948
+ *
1949
+ * @param input Input value
1950
+ * @param srcFormat String format of the input
1951
+ * @param moment Moment instance, if it is available
1952
+ * @param luxon Luxon object, if it is available
1953
+ * @returns Value in ISO
1954
+ */
1955
+ function toISO(input, srcFormat, moment, luxon) {
1956
+ if (input === '') {
1957
+ return '';
1958
+ }
1959
+ else if (srcFormat !== 'YYYY-MM-DD' && moment) {
1960
+ // TODO Does it have a time component?
1961
+ return moment.utc(input, srcFormat).toISOString();
1962
+ }
1963
+ else if (srcFormat !== 'YYYY-MM-DD' && luxon) {
1964
+ // TODO Does it have a time component?
1965
+ return luxon.DateTime.fromFormat(input, srcFormat).toISO();
1966
+ }
1967
+ // new Date() with `/` separators will treat the input as local time, but with `-` it will
1968
+ // treat it as UTC. We want UTC so do a replacement
1969
+ input = input.replace(/\//g, '-');
1970
+ return input;
1626
1971
  }
1627
1972
 
1628
1973
  /** Set the options to show in the list */
@@ -1688,17 +2033,19 @@ function reloadOptions(dt, config, idx, checkList, loadedValues) {
1688
2033
  options = jsonOptions;
1689
2034
  }
1690
2035
  else if (json && config.ajaxOnly) {
1691
- // Ajax only options - need to hide the search list
1692
- checkList.element().style.display = 'none';
1693
- // Check if the parent buttons should be hidden as well (they will be if there
1694
- // is no visible content in them)
1695
- if (config._parents) {
1696
- config._parents.forEach(function (btn) { return btn.checkDisplay(); });
2036
+ if (config.hidable) {
2037
+ // Ajax only options - need to hide the search list
2038
+ checkList.element().style.display = 'none';
2039
+ // Check if the parent buttons should be hidden as well (they will be if there
2040
+ // is no visible content in them)
2041
+ if (config._parents) {
2042
+ config._parents.forEach(function (btn) { return btn.checkDisplay(); });
2043
+ }
1697
2044
  }
1698
2045
  // No point in doing any further processing here
1699
2046
  return;
1700
2047
  }
1701
- else {
2048
+ else if (!dt.page.info().serverSide) {
1702
2049
  // Either no ajax object (i.e. not an Ajax table), or no matching ajax options
1703
2050
  // for this column - get the values for the column, taking into account
1704
2051
  // orthogonal rendering
@@ -1706,11 +2053,14 @@ function reloadOptions(dt, config, idx, checkList, loadedValues) {
1706
2053
  var rows = dt.rows({ order: idx }).indexes().toArray();
1707
2054
  var settings = dt.settings()[0];
1708
2055
  for (var i = 0; i < rows.length; i++) {
1709
- var filter = settings.fastData(rows[i], idx, 'filter').toString();
2056
+ var raw = settings.fastData(rows[i], idx, 'filter');
2057
+ var filter = raw !== null && raw !== undefined
2058
+ ? raw.toString()
2059
+ : '';
1710
2060
  if (!found[filter]) {
1711
2061
  found[filter] = true;
1712
2062
  options.push({
1713
- label: settings.fastData(rows[i], idx, 'display'),
2063
+ label: settings.fastData(rows[i], idx, config.orthogonal),
1714
2064
  value: filter
1715
2065
  });
1716
2066
  }
@@ -1727,7 +2077,9 @@ var searchList = {
1727
2077
  defaults: {
1728
2078
  ajaxOnly: true,
1729
2079
  className: 'searchList',
2080
+ hidable: true,
1730
2081
  options: null,
2082
+ orthogonal: 'display',
1731
2083
  search: true,
1732
2084
  select: true,
1733
2085
  title: ''
@@ -1739,6 +2091,10 @@ var searchList = {
1739
2091
  // The search can be applied from a stored start at start up before the options are
1740
2092
  // available. It can also be applied by user input, so it is generalised into this function.
1741
2093
  var applySearch = function (values) {
2094
+ // When SSP, don't do any client-side filtering
2095
+ if (dt.page.info().serverSide) {
2096
+ return;
2097
+ }
1742
2098
  var col = dt.column(_this.idx());
1743
2099
  if (!values) {
1744
2100
  return;
@@ -1758,11 +2114,11 @@ var searchList = {
1758
2114
  config._parents.forEach(function (btn) { return btn.activeList(_this.unique(), !!values.length); });
1759
2115
  }
1760
2116
  };
1761
- var checkList = new CheckList(dt, {
2117
+ var checkList = new CheckList(dt, this, {
1762
2118
  search: config.search,
1763
2119
  select: config.select
1764
2120
  })
1765
- .searchListener(dt, this)
2121
+ .searchListener(dt)
1766
2122
  .title(dt
1767
2123
  .i18n('columnControl.searchList', config.title)
1768
2124
  .replace('[title]', dt.column(this.idx()).title()))
@@ -1782,14 +2138,24 @@ var searchList = {
1782
2138
  dt.ready(function () {
1783
2139
  reloadOptions(dt, config, _this.idx(), checkList, loadedValues);
1784
2140
  });
2141
+ // Xhr event listener for updates of options
2142
+ dt.on('xhr', function (e, s, json) {
2143
+ // Need to wait for the draw to complete so the table has the latest data
2144
+ dt.one('draw', function () {
2145
+ reloadOptions(dt, config, _this.idx(), checkList, loadedValues);
2146
+ });
2147
+ });
1785
2148
  }
1786
- // Xhr event listener for updates of options
1787
- dt.on('xhr', function (e, s, json) {
1788
- // Need to wait for the draw to complete so the table has the latest data
1789
- dt.one('draw', function () {
1790
- reloadOptions(dt, config, _this.idx(), checkList, loadedValues);
2149
+ // Data for server-side processing
2150
+ if (dt.page.info().serverSide) {
2151
+ dt.on('preXhr.DT', function (e, s, d) {
2152
+ if (!d.columns[_this.idx()].columnControl) {
2153
+ d.columns[_this.idx()].columnControl = {};
2154
+ }
2155
+ // We need the indexes in the HTTP parameter names (for .NET), so use an object.
2156
+ d.columns[_this.idx()].columnControl.list = Object.assign({}, checkList.values());
1791
2157
  });
1792
- });
2158
+ }
1793
2159
  // Unlike the SearchInput based search contents, CheckList does not handle state saving
1794
2160
  // (since the mechanism for column visibility is different), so state saving is handled
1795
2161
  // here.
@@ -1797,6 +2163,7 @@ var searchList = {
1797
2163
  var values = getState(_this.idx(), state);
1798
2164
  if (values) {
1799
2165
  checkList.values(values);
2166
+ applySearch(values);
1800
2167
  }
1801
2168
  });
1802
2169
  dt.on('stateSaveParams', function (e, s, data) {
@@ -1814,6 +2181,14 @@ var searchList = {
1814
2181
  ? checkList.values()
1815
2182
  : loadedValues;
1816
2183
  });
2184
+ dt.settings()[0].aoColumns[this.idx()].columnControlSearchList = function (options) {
2185
+ if (options === 'refresh') {
2186
+ reloadOptions(dt, config, _this.idx(), checkList, null);
2187
+ }
2188
+ else {
2189
+ setOptions(checkList, options);
2190
+ }
2191
+ };
1817
2192
  loadedValues = getState(this.idx(), dt.state.loaded());
1818
2193
  applySearch(loadedValues);
1819
2194
  return checkList.element();
@@ -1852,6 +2227,13 @@ var searchNumber = {
1852
2227
  { label: dt.i18n(i18nBase + 'notEmpty', 'Not empty'), value: 'notEmpty' }
1853
2228
  ])
1854
2229
  .search(function (searchType, searchTerm, loadingState) {
2230
+ // When SSP, don't apply a filter here, SearchInput will add to the submit data
2231
+ if (dt.page.info().serverSide) {
2232
+ if (!loadingState) {
2233
+ dt.draw();
2234
+ }
2235
+ return;
2236
+ }
1855
2237
  var column = dt.column(_this.idx());
1856
2238
  if (searchType === 'empty') {
1857
2239
  column.search.fixed('dtcc', function (haystack) { return !haystack; });
@@ -1949,6 +2331,13 @@ var searchText = {
1949
2331
  { label: dt.i18n(i18nBase + 'notEmpty', 'Not empty'), value: 'notEmpty' }
1950
2332
  ])
1951
2333
  .search(function (searchType, searchTerm, loadingState) {
2334
+ // When SSP, don't apply a filter here, SearchInput will add to the submit data
2335
+ if (dt.page.info().serverSide) {
2336
+ if (!loadingState) {
2337
+ dt.draw();
2338
+ }
2339
+ return;
2340
+ }
1952
2341
  var column = dt.column(_this.idx());
1953
2342
  searchTerm = searchTerm.toLowerCase();
1954
2343
  if (searchType === 'empty') {
@@ -2055,7 +2444,7 @@ var search = {
2055
2444
  }
2056
2445
  };
2057
2446
 
2058
- var searchClear = {
2447
+ var searchClear$1 = {
2059
2448
  defaults: {
2060
2449
  className: 'searchClear',
2061
2450
  icon: 'searchClear',
@@ -2064,12 +2453,12 @@ var searchClear = {
2064
2453
  init: function (config) {
2065
2454
  var _this = this;
2066
2455
  var dt = this.dt();
2067
- var btn = new Button(dt)
2456
+ var btn = new Button(dt, this)
2068
2457
  .text(dt.i18n('columnControl.searchClear', config.text))
2069
2458
  .icon(config.icon)
2070
2459
  .className(config.className)
2071
2460
  .handler(function () {
2072
- dt.column(_this.idx()).ccSearchClear().draw();
2461
+ dt.column(_this.idx()).columnControl.searchClear().draw();
2073
2462
  })
2074
2463
  .enable(false);
2075
2464
  dt.on('draw', function () {
@@ -2089,7 +2478,9 @@ var searchDropdown = {
2089
2478
  className: 'searchDropdown',
2090
2479
  clear: true,
2091
2480
  columns: '',
2481
+ hidable: true,
2092
2482
  options: null,
2483
+ orthogonal: 'display',
2093
2484
  placeholder: '',
2094
2485
  search: true,
2095
2486
  select: true,
@@ -2146,6 +2537,10 @@ var contentTypes = {
2146
2537
  reorder: reorder,
2147
2538
  reorderLeft: reorderLeft,
2148
2539
  reorderRight: reorderRight,
2540
+ rowGroup: rowGroup,
2541
+ rowGroupAdd: rowGroupAdd,
2542
+ rowGroupClear: rowGroupClear,
2543
+ rowGroupRemove: rowGroupRemove,
2149
2544
  order: order,
2150
2545
  orderAddAsc: orderAddAsc,
2151
2546
  orderAddDesc: orderAddDesc,
@@ -2155,7 +2550,7 @@ var contentTypes = {
2155
2550
  orderRemove: orderRemove,
2156
2551
  orderStatus: orderStatus,
2157
2552
  search: search,
2158
- searchClear: searchClear,
2553
+ searchClear: searchClear$1,
2159
2554
  searchDropdown: searchDropdown,
2160
2555
  searchDateTime: searchDateTime,
2161
2556
  searchList: searchList,
@@ -2185,7 +2580,8 @@ var ColumnControl = /** @class */ (function () {
2185
2580
  this._c = {};
2186
2581
  this._s = {
2187
2582
  columnIdx: null,
2188
- unique: null
2583
+ unique: null,
2584
+ toDestroy: []
2189
2585
  };
2190
2586
  this._dt = dt;
2191
2587
  this._s.columnIdx = columnIdx;
@@ -2211,10 +2607,33 @@ var ColumnControl = /** @class */ (function () {
2211
2607
  _this._dom.wrapper.appendChild(el);
2212
2608
  });
2213
2609
  dt.on('destroy', function () {
2610
+ _this._s.toDestroy.slice().forEach(function (el) {
2611
+ el.destroy();
2612
+ });
2214
2613
  _this._dom.wrapper.remove();
2215
2614
  });
2216
2615
  }
2217
2616
  }
2617
+ /**
2618
+ * Add a component to the destroy list. This is so there is a single destroy event handler,
2619
+ * which is much better for performance.
2620
+ *
2621
+ * @param component Any instance with a `destroy` method
2622
+ */
2623
+ ColumnControl.prototype.destroyAdd = function (component) {
2624
+ this._s.toDestroy.push(component);
2625
+ };
2626
+ /**
2627
+ * Remove an instance from the destroy list (it has been destroyed itself)
2628
+ *
2629
+ * @param component Any instance with a `destroy` method
2630
+ */
2631
+ ColumnControl.prototype.destroyRemove = function (component) {
2632
+ var idx = this._s.toDestroy.indexOf(component);
2633
+ if (idx !== -1) {
2634
+ this._s.toDestroy.splice(idx, 1);
2635
+ }
2636
+ };
2218
2637
  /**
2219
2638
  * Get the DataTables API instance that hosts this instance of ColumnControl
2220
2639
  *
@@ -2325,7 +2744,7 @@ var ColumnControl = /** @class */ (function () {
2325
2744
  /** SVG icons that can be used by the content plugins */
2326
2745
  ColumnControl.icons = icons;
2327
2746
  /** Version */
2328
- ColumnControl.version = '1.0.6';
2747
+ ColumnControl.version = '1.1.0';
2329
2748
  return ColumnControl;
2330
2749
  }());
2331
2750
 
@@ -2354,7 +2773,9 @@ $(document).on('i18n.dt', function (e, settings) {
2354
2773
  settings.titleRow = 0;
2355
2774
  }
2356
2775
  identifyTargets(baseTargets, tableInit);
2357
- identifyTargets(baseTargets, defaultInit);
2776
+ if (ColumnControl.defaults.content) {
2777
+ identifyTargets(baseTargets, defaultInit);
2778
+ }
2358
2779
  api.columns().every(function (i) {
2359
2780
  var columnInit = this.init().columnControl;
2360
2781
  identifyTargets(baseTargets, columnInit);
@@ -2374,7 +2795,10 @@ $(document).on('preInit.dt', function (e, settings) {
2374
2795
  var defaultInit = ColumnControl.defaults;
2375
2796
  var baseTargets = [];
2376
2797
  identifyTargets(baseTargets, tableInit);
2377
- identifyTargets(baseTargets, defaultInit);
2798
+ // Only add the default target if there is actually content for it
2799
+ if (ColumnControl.defaults.content) {
2800
+ identifyTargets(baseTargets, defaultInit);
2801
+ }
2378
2802
  api.columns().every(function (i) {
2379
2803
  var columnInit = this.init().columnControl;
2380
2804
  var targets = identifyTargets(baseTargets.slice(), columnInit);
@@ -2392,12 +2816,20 @@ $(document).on('preInit.dt', function (e, settings) {
2392
2816
  }
2393
2817
  });
2394
2818
  });
2395
- DataTable.Api.registerPlural('columns().ccSearchClear()', 'column().ccSearchClear()', function () {
2819
+ function searchClear() {
2396
2820
  var ctx = this;
2397
2821
  return this.iterator('column', function (settings, idx) {
2398
2822
  // Note that the listeners for this will not redraw the table.
2399
2823
  ctx.trigger('cc-search-clear', [idx]);
2400
2824
  });
2825
+ }
2826
+ DataTable.Api.registerPlural('columns().columnControl.searchClear()', 'column().columnControl.searchClear()', searchClear);
2827
+ // Legacy (1.0.x)) - was never documented, but was mentioned in the forum
2828
+ DataTable.Api.registerPlural('columns().ccSearchClear()', 'column().ccSearchClear()', searchClear);
2829
+ DataTable.Api.registerPlural('columns().columnControl.searchList()', 'column().columnControl.searchList()', function (options) {
2830
+ return this.iterator('column', function (settings, idx) {
2831
+ settings.aoColumns[idx].columnControlSearchList(options);
2832
+ });
2401
2833
  });
2402
2834
  DataTable.ext.buttons.ccSearchClear = {
2403
2835
  text: function (dt) {
@@ -2405,7 +2837,7 @@ DataTable.ext.buttons.ccSearchClear = {
2405
2837
  },
2406
2838
  init: function (dt, node, config) {
2407
2839
  var _this = this;
2408
- dt.on('draw', function () {
2840
+ dt.on('draw.DT', function () {
2409
2841
  var enabled = false;
2410
2842
  var glob = !!dt.search();
2411
2843
  // No point in wasting clock cycles if we already know it will be enabled
@@ -2422,7 +2854,7 @@ DataTable.ext.buttons.ccSearchClear = {
2422
2854
  },
2423
2855
  action: function (e, dt, node, config) {
2424
2856
  dt.search('');
2425
- dt.columns().ccSearchClear();
2857
+ dt.columns().columnControl.searchClear();
2426
2858
  dt.draw();
2427
2859
  }
2428
2860
  };
@@ -2558,12 +2990,18 @@ function identifyTargets(targets, input) {
2558
2990
  }
2559
2991
  }
2560
2992
  if (Array.isArray(input)) {
2561
- // Array of options, or an array of content
2562
- input.forEach(function (item) {
2563
- add(typeof item === 'object' && item.target !== undefined
2564
- ? item.target
2565
- : ColumnControl.defaults.target);
2566
- });
2993
+ if (input.length === 0) {
2994
+ // Empty array - assume it is empty content
2995
+ add(ColumnControl.defaults.target);
2996
+ }
2997
+ else {
2998
+ // Array of options, or an array of content
2999
+ input.forEach(function (item) {
3000
+ add(typeof item === 'object' && item.target !== undefined
3001
+ ? item.target
3002
+ : ColumnControl.defaults.target);
3003
+ });
3004
+ }
2567
3005
  }
2568
3006
  else if (typeof input === 'object') {
2569
3007
  // Full options defined: { target: x, content: [] }
@@ -2578,9 +3016,7 @@ function identifyTargets(targets, input) {
2578
3016
  * @returns true if it is a config object
2579
3017
  */
2580
3018
  function isIConfig(item) {
2581
- return typeof item === 'object' && item.target !== undefined
2582
- ? true
2583
- : false;
3019
+ return typeof item === 'object' && item.target !== undefined ? true : false;
2584
3020
  }
2585
3021
  /**
2586
3022
  * Determine if an array contains only content items or not