datatables.net-columncontrol 0.9.2

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.
@@ -0,0 +1,2537 @@
1
+ /*! ColumnControl 0.9.2
2
+ * Copyright (c) SpryMedia Ltd - datatables.net/license
3
+ *
4
+ * SVG icons: ISC License
5
+ * Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT).
6
+ * All other copyright (c) for Lucide are held by Lucide Contributors 2022.
7
+ */
8
+
9
+ (function( factory ){
10
+ if ( typeof define === 'function' && define.amd ) {
11
+ // AMD
12
+ define( ['jquery', 'datatables.net'], function ( $ ) {
13
+ return factory( $, window, document );
14
+ } );
15
+ }
16
+ else if ( typeof exports === 'object' ) {
17
+ // CommonJS
18
+ var jq = require('jquery');
19
+ var cjsRequires = function (root, $) {
20
+ if ( ! $.fn.dataTable ) {
21
+ require('datatables.net')(root, $);
22
+ }
23
+ };
24
+
25
+ if (typeof window === 'undefined') {
26
+ module.exports = function (root, $) {
27
+ if ( ! root ) {
28
+ // CommonJS environments without a window global must pass a
29
+ // root. This will give an error otherwise
30
+ root = window;
31
+ }
32
+
33
+ if ( ! $ ) {
34
+ $ = jq( root );
35
+ }
36
+
37
+ cjsRequires( root, $ );
38
+ return factory( $, root, root.document );
39
+ };
40
+ }
41
+ else {
42
+ cjsRequires( window, jq );
43
+ module.exports = factory( jq, window, window.document );
44
+ }
45
+ }
46
+ else {
47
+ // Browser
48
+ factory( jQuery, window, document );
49
+ }
50
+ }(function( $, window, document ) {
51
+ 'use strict';
52
+ var DataTable = $.fn.dataTable;
53
+
54
+
55
+
56
+ function createElement(type, classes, text, children) {
57
+ if (classes === void 0) { classes = []; }
58
+ if (text === void 0) { text = null; }
59
+ if (children === void 0) { children = []; }
60
+ var el = document.createElement(type);
61
+ addClass(el, classes);
62
+ if (text) {
63
+ el.innerHTML = text;
64
+ }
65
+ children.forEach(function (child) {
66
+ el.appendChild(child);
67
+ });
68
+ return el;
69
+ }
70
+ function addClass(el, classes) {
71
+ if (!classes) {
72
+ return;
73
+ }
74
+ if (!Array.isArray(classes)) {
75
+ classes = [classes];
76
+ }
77
+ classes.forEach(function (className) {
78
+ if (className) {
79
+ el.classList.add(className);
80
+ }
81
+ });
82
+ }
83
+
84
+ // The SVG for many of these icons are from Lucide ( https://lucide.dev ), which are available
85
+ // under the ISC License. There are a number of custom icons as well. These are optimised through
86
+ // https://optimize.svgomg.net/
87
+ function wrap(paths) {
88
+ return ('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' +
89
+ paths +
90
+ '</svg>');
91
+ }
92
+ var icons = {
93
+ chevronRight: wrap('<path d="m9 18 6-6-6-6"/>'),
94
+ // columns-3
95
+ columns: wrap('<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/><path d="M15 3v18"/>'),
96
+ // Custom
97
+ contains: wrap('<path d="M10 3h4v18h-4z"/><path d="M18 8h3v9h-3"/><path d="M6 17H3V8h3"/>'),
98
+ empty: wrap('<circle cx="12" cy="12" r="10"/>'),
99
+ ends: wrap('<path d="M21 3h-4v18h4z"/><path d="M13 8H3v9h10"/>'),
100
+ // Customised
101
+ equal: wrap('<line x1="5" x2="19" y1="9" y2="9"/><line x1="5" x2="19" y1="15" y2="15"/>'),
102
+ greater: wrap('<path d="m9 18 6-6-6-6"/>'),
103
+ // Custom
104
+ greaterOrEqual: wrap('<path d="m9 16 6-6-6-6"/><path d="m9 21 6-6"/>'),
105
+ less: wrap('<path d="m15 18-6-6 6-6"/>'),
106
+ // Custom
107
+ lessOrEqual: wrap('<path d="m15 16-6-6 6-6"/><path d="m15 21-6-6"/>'),
108
+ menu: wrap('<line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/>'),
109
+ // move-horizontal
110
+ move: wrap('<line x1="12" x2="12" y1="3" y2="21"/><polyline points="8 8 4 12 8 16"/><polyline points="16 16 20 12 16 8"/>'),
111
+ // arrow-left-from-line
112
+ moveLeft: wrap('<path d="m9 6-6 6 6 6"/><path d="M3 12h14"/><path d="M21 19V5"/>'),
113
+ // arrow-right-from-line
114
+ moveRight: wrap('<path d="M3 5v14"/><path d="M21 12H7"/><path d="m15 18 6-6-6-6"/>'),
115
+ // Custom
116
+ notContains: wrap('<path d="M15 4 9 20"/><path d="M3 8h18v9H3z"/>'),
117
+ notEmpty: wrap('<circle cx="12" cy="12" r="10"/><line x1="9" x2="15" y1="15" y2="9"/>'),
118
+ notEqual: wrap('<path d="M5 9h14"/><path d="M5 15h14"/><path d="M15 5 9 19"/>'),
119
+ // Custom
120
+ orderAddAsc: wrap('<path d="M17 21v-8"/><path d="M3 4h6"/><path d="M3 8h9"/><path d="M3 12h10"/><path d="M13 17h8"/>'),
121
+ // Custom
122
+ orderAddDesc: wrap('<path d="M17 21v-8"/><path d="M3 4h12"/><path d="M3 8h9"/><path d="M3 12h6"/><path d="M13 17h8"/>'),
123
+ orderAsc: wrap('<path d="m3 8 4-4 4 4"/><path d="M7 4v16"/><path d="M11 12h4"/><path d="M11 16h7"/><path d="M11 20h10"/>'),
124
+ // Custom
125
+ orderClear: wrap('<path d="m21 21-8-8"/><path d="M3 4h12"/><path d="M3 8h9"/><path d="M3 12h6"/><path d="m13 21 8-8"/>'),
126
+ orderDesc: wrap('<path d="m3 16 4 4 4-4"/><path d="M7 20V4"/><path d="M11 4h10"/><path d="M11 8h7"/><path d="M11 12h4"/>'),
127
+ // Custom
128
+ orderRemove: wrap('<path d="M3 4h12"/><path d="M3 8h9"/><path d="M3 12h6"/><path d="M13 17h8"/>'),
129
+ // Custom
130
+ orderNone: wrap('<path d="m3 8 4-4 4 4"/><path d="m11 16-4 4-4-4"/><path d="M7 4v16"/><path d="M15 8h6"/><path d="M15 16h6"/><path d="M13 12h8"/>'),
131
+ // search
132
+ search: wrap('<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>'),
133
+ // search-x
134
+ searchClear: wrap('<path d="m13.5 8.5-5 5"/><path d="m8.5 8.5 5 5"/><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>'),
135
+ // Custom
136
+ starts: wrap('<path d="M3 3h4v18H3z"/><path d="M11 8h10v9H11"/>'),
137
+ // tick
138
+ tick: wrap('<path d="M20 6 9 17l-5-5"/>'),
139
+ // x
140
+ x: wrap('<path d="M18 6 6 18"/><path d="m6 6 12 12"/>')
141
+ };
142
+
143
+ /**
144
+ * Close all or only other dropdowns
145
+ *
146
+ * @param e Event or null to close all others
147
+ */
148
+ function close(e) {
149
+ if (e === void 0) { e = null; }
150
+ document.querySelectorAll('div.dtcc-dropdown').forEach(function (el) {
151
+ if (e === null || !el.contains(e.target)) {
152
+ el._close();
153
+ if (!e._closed) {
154
+ e._closed = [];
155
+ }
156
+ e._closed.push(el);
157
+ }
158
+ });
159
+ }
160
+ /**
161
+ * Position the dropdown relative to the button that activated it, with possible corrections
162
+ * to make sure it is visible on the page.
163
+ *
164
+ * @param dropdown Dropdown element
165
+ * @param dt Container DataTable
166
+ * @param btn Button the dropdown emanates from
167
+ */
168
+ function positionDropdown(dropdown, dt, btn) {
169
+ var dtContainer = dt.table().container();
170
+ var header = btn.closest('div.dt-column-header');
171
+ var headerStyle = getComputedStyle(header);
172
+ var dropdownWidth = dropdown.offsetWidth;
173
+ var position = relativePosition(dtContainer, btn);
174
+ var left, top;
175
+ top = position.top + btn.offsetHeight;
176
+ if (headerStyle.flexDirection === 'row-reverse') {
177
+ // Icon is on the left of the header - align the left hand sides
178
+ left = position.left;
179
+ }
180
+ else {
181
+ // Icon is on the right of the header - align the right hand sides
182
+ left = position.left - dropdownWidth + btn.offsetWidth;
183
+ }
184
+ // Corrections - don't extend past the DataTable to the left and right
185
+ var containerOffsetLeft = dtContainer.offsetLeft;
186
+ var containerWidth = dtContainer.offsetWidth;
187
+ if (left + dropdownWidth > containerWidth) {
188
+ left -= left + dropdownWidth - containerWidth;
189
+ }
190
+ if (left < containerOffsetLeft) {
191
+ left = containerOffsetLeft;
192
+ }
193
+ dropdown.style.top = top + 'px';
194
+ dropdown.style.left = left + 'px';
195
+ }
196
+ /**
197
+ * Display the dropdown in the document
198
+ *
199
+ * @param dropdown Dropdown element
200
+ * @param dt Container DataTable
201
+ * @param btn Button the dropdown emanates from
202
+ * @returns Function to call when the dropdown should be removed from the document
203
+ */
204
+ function attachDropdown(dropdown, dt, btn) {
205
+ var dtContainer = dt.table().container();
206
+ dropdown._shown = true;
207
+ dtContainer.append(dropdown);
208
+ positionDropdown(dropdown, dt, btn.element());
209
+ // Note that this could be called when the dropdown has already been removed from the document
210
+ // via another dropdown being shown. This will clean up the event on the next body click.
211
+ var removeDropdown = function (e) {
212
+ // Not in document, so just clean up the event handler
213
+ if (!dropdown._shown) {
214
+ document.body.removeEventListener('click', removeDropdown);
215
+ return;
216
+ }
217
+ // If the click is inside the dropdown, ignore it - we don't want to immediately close
218
+ if (e.target === dropdown || dropdown.contains(e.target)) {
219
+ return;
220
+ }
221
+ dropdown._close();
222
+ document.body.removeEventListener('click', removeDropdown);
223
+ };
224
+ document.body.addEventListener('click', removeDropdown);
225
+ return removeDropdown;
226
+ }
227
+ /**
228
+ * Get the position of an element, relative to a given parent. The origin MUST be under the
229
+ * parent's tree.
230
+ *
231
+ * @param parent Parent element to get position relative to
232
+ * @param origin Target element
233
+ */
234
+ function relativePosition(parent, origin) {
235
+ var top = 0;
236
+ var left = 0;
237
+ while (origin && origin !== parent && origin !== document.body) {
238
+ top += origin.offsetTop;
239
+ left += origin.offsetLeft;
240
+ origin = origin.offsetParent;
241
+ }
242
+ return {
243
+ top: top,
244
+ left: left
245
+ };
246
+ }
247
+ var dropdownContent = {
248
+ classes: {
249
+ container: 'dtcc-dropdown',
250
+ liner: 'dtcc-dropdown-liner'
251
+ },
252
+ defaults: {
253
+ className: 'dropdown',
254
+ content: [],
255
+ icon: 'menu',
256
+ text: 'More...'
257
+ },
258
+ init: function (config) {
259
+ var dt = this.dt();
260
+ var dropdown = createElement('div', dropdownContent.classes.container, '', [
261
+ createElement('div', dropdownContent.classes.liner)
262
+ ]);
263
+ dropdown._shown = false;
264
+ dropdown._close = function () {
265
+ dropdown.remove();
266
+ dropdown._shown = false;
267
+ };
268
+ // A liner element allows more styling options, so the contents go inside this
269
+ var liner = dropdown.childNodes[0];
270
+ var btn = new Button(dt)
271
+ .text(dt.i18n('columnControl.dropdown', config.text))
272
+ .icon(config.icon)
273
+ .className(config.className)
274
+ .dropdownDisplay(liner)
275
+ .handler(function (e) {
276
+ // Do nothing if our dropdown was just closed as part of the event (i.e. allow
277
+ // the button to toggle it closed)
278
+ if (e._closed && e._closed.includes(dropdown)) {
279
+ return;
280
+ }
281
+ attachDropdown(dropdown, dt, config._parents ? config._parents[0] : btn);
282
+ });
283
+ // Add the content for the dropdown
284
+ for (var i = 0; i < config.content.length; i++) {
285
+ var content = this.resolve(config.content[i]);
286
+ // For nested items we need to keep a reference to the top level so the sub-levels
287
+ // can communicate back - e.g. active or positioned relative to that top level.
288
+ if (!content.config._parents) {
289
+ content.config._parents = [];
290
+ }
291
+ content.config._parents.push(btn);
292
+ var el = content.plugin.init.call(this, content.config);
293
+ liner.appendChild(el);
294
+ }
295
+ // For nested dropdowns, add an extra icon element to show that it will dropdown further
296
+ if (config._parents && config._parents.length) {
297
+ btn.extra('chevronRight');
298
+ }
299
+ // Reposition if needed
300
+ dt.on('columns-reordered', function () {
301
+ positionDropdown(dropdown, dt, btn.element());
302
+ });
303
+ return btn.element();
304
+ }
305
+ };
306
+
307
+ var _namespace = 0;
308
+ var Button = /** @class */ (function () {
309
+ /**
310
+ * Create a new button for use in ColumnControl contents. Buttons created by this class can be
311
+ * used at the top level in the header or in a dropdown.
312
+ */
313
+ function Button(dt) {
314
+ this._s = {
315
+ active: false,
316
+ activeList: [],
317
+ buttonClick: null,
318
+ dt: null,
319
+ enabled: true,
320
+ label: '',
321
+ namespace: '',
322
+ value: null
323
+ };
324
+ this._s.dt = dt;
325
+ this._dom = {
326
+ button: createElement('button', Button.classes.container),
327
+ dropdownDisplay: null,
328
+ extra: createElement('span', 'dtcc-button-extra'),
329
+ icon: createElement('span', 'dtcc-button-icon'),
330
+ state: createElement('span', 'dtcc-button-state'),
331
+ text: createElement('span', 'dtcc-button-text')
332
+ };
333
+ this._dom.button.append(this._dom.icon);
334
+ this._dom.button.append(this._dom.text);
335
+ this._dom.button.append(this._dom.state);
336
+ this._dom.button.append(this._dom.extra);
337
+ // Default state is enabled
338
+ this.enable(true);
339
+ }
340
+ Button.prototype.active = function (active) {
341
+ if (active === undefined) {
342
+ return this._s.active;
343
+ }
344
+ this._s.active = active;
345
+ this._checkActive();
346
+ return this;
347
+ };
348
+ /**
349
+ * A button can be marked as active by any of its sub-buttons (i.e. if it is a dropdown)
350
+ * and each one needs to be able to enable this button without effecting the active state
351
+ * trigged by any other sub-buttons. This method provides a way to do that.
352
+ *
353
+ * @param unique Unique id for the activate state
354
+ * @param active If it is active
355
+ * @returns Button instance
356
+ */
357
+ Button.prototype.activeList = function (unique, active) {
358
+ this._s.activeList[unique] = active;
359
+ this._checkActive();
360
+ return this;
361
+ };
362
+ /**
363
+ * Scan over the dropdown element looking for any visible content. If there isn't any then
364
+ * we hide this button.
365
+ *
366
+ * @returns Button instance
367
+ */
368
+ Button.prototype.checkDisplay = function () {
369
+ var visible = 0;
370
+ var children = this._dom.dropdownDisplay.childNodes;
371
+ for (var i = 0; i < children.length; i++) {
372
+ // No need to getComputedStyle since if a button is hidden, it was done with JS writing
373
+ // to style.display, so we can check against that.
374
+ if (children[i].style.display !== 'none') {
375
+ visible++;
376
+ }
377
+ }
378
+ if (visible === 0) {
379
+ this._dom.button.style.display = 'none';
380
+ }
381
+ return this;
382
+ };
383
+ /**
384
+ * Set the class name for the button
385
+ *
386
+ * @param className Class name
387
+ * @returns Button instance
388
+ */
389
+ Button.prototype.className = function (className) {
390
+ this._dom.button.classList.add('dtcc-button_' + className);
391
+ return this;
392
+ };
393
+ /**
394
+ * Destroy the button, cleaning up event listeners
395
+ */
396
+ Button.prototype.destroy = function () {
397
+ if (this._s.buttonClick) {
398
+ this._dom.button.removeEventListener('click', this._s.buttonClick);
399
+ }
400
+ if (this._s.namespace) {
401
+ this._s.dt.off('destroy.' + this._s.namespace);
402
+ }
403
+ };
404
+ /**
405
+ * Relevant for drop downs only. When a button in a dropdown is hidden, we might want to
406
+ * hide the host button as well (if it has nothing else to show). For that we need to know
407
+ * what the dropdown element is.
408
+ *
409
+ * @param el Element that can be used for telling us about drop down elements.
410
+ * @returns Button instance
411
+ */
412
+ Button.prototype.dropdownDisplay = function (el) {
413
+ this._dom.dropdownDisplay = el;
414
+ return this;
415
+ };
416
+ /**
417
+ * Get the DOM Button element to attach into the document
418
+ *
419
+ * @returns The Button element
420
+ */
421
+ Button.prototype.element = function () {
422
+ return this._dom.button;
423
+ };
424
+ /**
425
+ * Set if the button should be enabled or not.
426
+ *
427
+ * @param enable Toggle the enable state
428
+ * @returns Button instance
429
+ */
430
+ Button.prototype.enable = function (enable) {
431
+ this._dom.button.classList.toggle('dtcc-button_disabled', !enable);
432
+ this._s.enabled = enable;
433
+ return this;
434
+ };
435
+ /**
436
+ * Set the extra information icon
437
+ *
438
+ * @param icon Icon name
439
+ * @returns Button instance
440
+ */
441
+ Button.prototype.extra = function (icon) {
442
+ this._dom.extra.innerHTML = icon ? icons[icon] : '';
443
+ return this;
444
+ };
445
+ /**
446
+ * Set the event handler for when the button is activated
447
+ *
448
+ * @param fn Event handler
449
+ * @returns Button instance
450
+ */
451
+ Button.prototype.handler = function (fn) {
452
+ var _this = this;
453
+ var buttonClick = function (e) {
454
+ // Close any dropdowns which are already open
455
+ close(e);
456
+ // Stop bubbling to the DataTables default header, which might still be enabled
457
+ e.stopPropagation();
458
+ e.preventDefault();
459
+ if (_this._s.enabled) {
460
+ fn(e);
461
+ }
462
+ };
463
+ this._s.buttonClick = buttonClick;
464
+ this._s.namespace = 'dtcc-' + _namespace++;
465
+ this._dom.button.addEventListener('click', buttonClick);
466
+ // Use a unique namespace to be able to easily remove per button
467
+ this._s.dt.on('destroy.' + this._s.namespace, function () {
468
+ _this.destroy();
469
+ });
470
+ return this;
471
+ };
472
+ /**
473
+ * Set the icon to display in the button
474
+ *
475
+ * @param icon Icon name
476
+ * @returns Button instance
477
+ */
478
+ Button.prototype.icon = function (icon) {
479
+ this._dom.icon.innerHTML = icon ? icons[icon] : '';
480
+ return this;
481
+ };
482
+ Button.prototype.text = function (text) {
483
+ if (text === undefined) {
484
+ return this._s.label;
485
+ }
486
+ this._dom.text.innerHTML = text;
487
+ this._s.label = text; // for fast retrieval
488
+ return this;
489
+ };
490
+ Button.prototype.value = function (val) {
491
+ if (val === undefined) {
492
+ return this._s.value;
493
+ }
494
+ this._s.value = val;
495
+ return this;
496
+ };
497
+ /**
498
+ * Check if anything is making this button active
499
+ *
500
+ * @returns Self for chaining
501
+ */
502
+ Button.prototype._checkActive = function () {
503
+ if (this._s.active === true || Object.values(this._s.activeList).includes(true)) {
504
+ this._dom.state.innerHTML = icons.tick;
505
+ this._dom.button.classList.add('dtcc-button_active');
506
+ }
507
+ else {
508
+ this._dom.state.innerHTML = '';
509
+ this._dom.button.classList.remove('dtcc-button_active');
510
+ }
511
+ return this;
512
+ };
513
+ Button.classes = {
514
+ container: 'dtcc-button'
515
+ };
516
+ return Button;
517
+ }());
518
+
519
+ var CheckList = /** @class */ (function () {
520
+ /**
521
+ * Container for a list of buttons
522
+ */
523
+ function CheckList(dt, opts) {
524
+ var _this = this;
525
+ this._s = {
526
+ buttons: [],
527
+ dt: null,
528
+ handler: function () { },
529
+ search: ''
530
+ };
531
+ this._s.dt = dt;
532
+ this._dom = {
533
+ buttons: createElement('div', 'dtcc-list-buttons'),
534
+ container: createElement('div', CheckList.classes.container),
535
+ controls: createElement('div', 'dtcc-list-controls'),
536
+ title: createElement('div', 'dtcc-list-title'),
537
+ selectAll: createElement('button', 'dtcc-list-selectAll', dt.i18n('columnControl.list.all', 'Select all')),
538
+ selectAllCount: createElement('span'),
539
+ selectNone: createElement('button', 'dtcc-list-selectNone', dt.i18n('columnControl.list.none', 'Deselect')),
540
+ selectNoneCount: createElement('span'),
541
+ search: createElement('input', CheckList.classes.input)
542
+ };
543
+ var dom = this._dom;
544
+ dom.search.setAttribute('type', 'text');
545
+ dom.container.append(dom.title);
546
+ dom.container.append(dom.controls);
547
+ dom.container.append(dom.buttons);
548
+ if (opts.select) {
549
+ dom.controls.append(dom.selectAll);
550
+ dom.controls.append(dom.selectNone);
551
+ dom.selectAll.append(dom.selectAllCount);
552
+ dom.selectNone.append(dom.selectNoneCount);
553
+ }
554
+ // Events
555
+ var searchInput = function () {
556
+ _this._s.search = dom.search.value;
557
+ _this._redraw();
558
+ };
559
+ var selectAllClick = function (e) {
560
+ _this.selectAll();
561
+ _this._s.handler(e, null, _this._s.buttons);
562
+ _this._updateCount();
563
+ };
564
+ var selectNoneClick = function (e) {
565
+ _this.selectNone();
566
+ _this._s.handler(e, null, _this._s.buttons);
567
+ _this._updateCount();
568
+ };
569
+ if (opts.search) {
570
+ dom.controls.append(dom.search);
571
+ dom.search.setAttribute('placeholder', dt.i18n('columnControl.list.search', 'Search...'));
572
+ dom.search.addEventListener('input', searchInput);
573
+ }
574
+ dom.selectAll.addEventListener('click', selectAllClick);
575
+ dom.selectNone.addEventListener('click', selectNoneClick);
576
+ dt.on('destroy', function () {
577
+ dom.selectAll.removeEventListener('click', selectAllClick);
578
+ dom.selectNone.removeEventListener('click', selectNoneClick);
579
+ dom.search.removeEventListener('input', searchInput);
580
+ });
581
+ }
582
+ /**
583
+ * Add one or more buttons to the list
584
+ *
585
+ * @param options Configuration for the button(s) to add
586
+ * @returns Self for chaining
587
+ */
588
+ CheckList.prototype.add = function (options, update) {
589
+ var _this = this;
590
+ if (!Array.isArray(options)) {
591
+ options = [options];
592
+ }
593
+ var _loop_1 = function (i) {
594
+ var option = options[i];
595
+ var btn = new Button(this_1._s.dt)
596
+ .active(option.active || false)
597
+ .handler(function (e) {
598
+ _this._s.handler(e, btn, _this._s.buttons);
599
+ _this._updateCount();
600
+ })
601
+ .icon(option.icon || '')
602
+ .text(option.label)
603
+ .value(option.value);
604
+ this_1._s.buttons.push(btn);
605
+ };
606
+ var this_1 = this;
607
+ for (var i = 0; i < options.length; i++) {
608
+ _loop_1(i);
609
+ }
610
+ var count = this._s.buttons.length;
611
+ if (update === true || update === undefined) {
612
+ this._dom.selectAllCount.innerHTML = count ? '(' + count + ')' : '';
613
+ this._redraw();
614
+ }
615
+ return this;
616
+ };
617
+ /**
618
+ * Find a button with a given value
619
+ *
620
+ * @param val Value to search for
621
+ * @returns Found button
622
+ */
623
+ CheckList.prototype.button = function (val) {
624
+ var buttons = this._s.buttons;
625
+ for (var i = 0; i < buttons.length; i++) {
626
+ if (buttons[i].value() === val) {
627
+ return buttons[i];
628
+ }
629
+ }
630
+ return null;
631
+ };
632
+ /**
633
+ * Remove all buttons from the list
634
+ *
635
+ * @returns Self for chaining
636
+ */
637
+ CheckList.prototype.clear = function () {
638
+ // Clean up the buttons
639
+ for (var i = 0; i < this._s.buttons.length; i++) {
640
+ this._s.buttons[i].destroy();
641
+ }
642
+ // Then empty them out
643
+ this._dom.buttons.replaceChildren();
644
+ this._s.buttons.length = 0;
645
+ return this;
646
+ };
647
+ /**
648
+ * Get the DOM container element to attach into the document
649
+ *
650
+ * @returns Container
651
+ */
652
+ CheckList.prototype.element = function () {
653
+ return this._dom.container;
654
+ };
655
+ /**
656
+ * Set the event handler for what happens when a button is clicked
657
+ *
658
+ * @param fn Event handler
659
+ */
660
+ CheckList.prototype.handler = function (fn) {
661
+ this._s.handler = fn;
662
+ return this;
663
+ };
664
+ /**
665
+ * Indicate that this is a search control and should listen for corresponding events
666
+ *
667
+ * @param dt DataTable instance
668
+ * @param idx Column index
669
+ */
670
+ CheckList.prototype.searchListener = function (dt, parent) {
671
+ var _this = this;
672
+ // Column control search clearing (column().ccSearchClear() method)
673
+ dt.on('cc-search-clear', function (e, colIdx) {
674
+ if (colIdx === parent.idx()) {
675
+ _this.selectNone();
676
+ _this._s.handler(e, null, _this._s.buttons);
677
+ _this._s.search = '';
678
+ _this._dom.search.value = '';
679
+ _this._redraw();
680
+ _this._updateCount();
681
+ }
682
+ });
683
+ return this;
684
+ };
685
+ /**
686
+ * Select all buttons
687
+ *
688
+ * @returns Self for chaining
689
+ */
690
+ CheckList.prototype.selectAll = function () {
691
+ for (var i = 0; i < this._s.buttons.length; i++) {
692
+ this._s.buttons[i].active(true);
693
+ }
694
+ return this;
695
+ };
696
+ /**
697
+ * Deselect all buttons
698
+ *
699
+ * @returns Self for chaining
700
+ */
701
+ CheckList.prototype.selectNone = function () {
702
+ for (var i = 0; i < this._s.buttons.length; i++) {
703
+ this._s.buttons[i].active(false);
704
+ }
705
+ return this;
706
+ };
707
+ /**
708
+ * Set the list's title
709
+ *
710
+ * @param title Display title
711
+ * @returns Button instance
712
+ */
713
+ CheckList.prototype.title = function (title) {
714
+ this._dom.title.innerHTML = title;
715
+ return this;
716
+ };
717
+ CheckList.prototype.values = function (values) {
718
+ var i;
719
+ var result = [];
720
+ var buttons = this._s.buttons;
721
+ if (values !== undefined) {
722
+ for (i = 0; i < buttons.length; i++) {
723
+ if (values.includes(buttons[i].value())) {
724
+ buttons[i].active(true);
725
+ }
726
+ }
727
+ this._updateCount();
728
+ return this;
729
+ }
730
+ for (i = 0; i < buttons.length; i++) {
731
+ if (buttons[i].active()) {
732
+ result.push(buttons[i].value());
733
+ }
734
+ }
735
+ return result;
736
+ };
737
+ /**
738
+ * Update the deselect counter
739
+ */
740
+ CheckList.prototype._updateCount = function () {
741
+ var count = this.values().length;
742
+ this._dom.selectNoneCount.innerHTML = count ? '(' + count + ')' : '';
743
+ };
744
+ /**
745
+ * Add the buttons to the page - taking into account filtering
746
+ */
747
+ CheckList.prototype._redraw = function () {
748
+ var buttons = this._s.buttons;
749
+ var el = this._dom.buttons;
750
+ var searchTerm = this._s.search.toLowerCase();
751
+ el.replaceChildren();
752
+ for (var i = 0; i < buttons.length; i++) {
753
+ var btn = buttons[i];
754
+ if (!searchTerm ||
755
+ btn
756
+ .text()
757
+ .toLowerCase()
758
+ .includes(searchTerm)) {
759
+ el.appendChild(btn.element());
760
+ }
761
+ }
762
+ };
763
+ CheckList.classes = {
764
+ container: 'dtcc-list',
765
+ input: 'dtcc-list-search'
766
+ };
767
+ return CheckList;
768
+ }());
769
+
770
+ var colVis = {
771
+ defaults: {
772
+ className: 'colVis',
773
+ columns: '',
774
+ search: false,
775
+ select: false,
776
+ title: 'Column visibility'
777
+ },
778
+ init: function (config) {
779
+ var dt = this.dt();
780
+ var checkList = new CheckList(dt, {
781
+ search: config.search,
782
+ select: config.select
783
+ })
784
+ .title(dt.i18n('columnControl.colVis', config.title))
785
+ .handler(function (e, btn, buttons) {
786
+ if (btn) {
787
+ btn.active(!btn.active());
788
+ }
789
+ apply(buttons);
790
+ });
791
+ // Need to apply in a loop to allow for select all / select none
792
+ var apply = function (buttons) {
793
+ for (var i = 0; i < buttons.length; i++) {
794
+ var btn = buttons[i];
795
+ var idx = btn.value();
796
+ var col = dt.column(idx);
797
+ if (btn.active() && !col.visible()) {
798
+ col.visible(true);
799
+ }
800
+ else if (!btn.active() && col.visible()) {
801
+ col.visible(false);
802
+ }
803
+ }
804
+ };
805
+ var rebuild = function () {
806
+ var columns = dt.columns(config.columns);
807
+ columns.every(function () {
808
+ checkList.add({
809
+ active: this.visible(),
810
+ label: this.title(),
811
+ value: this.index()
812
+ });
813
+ });
814
+ };
815
+ rebuild();
816
+ dt.on('column-visibility', function (e, s, colIdx, state) {
817
+ var btn = checkList.button(colIdx);
818
+ if (btn) {
819
+ btn.active(state);
820
+ }
821
+ });
822
+ dt.on('columns-reordered', function (e, details) {
823
+ checkList.clear();
824
+ rebuild();
825
+ });
826
+ return checkList.element();
827
+ }
828
+ };
829
+
830
+ var colVisDropdown = {
831
+ defaults: {
832
+ className: 'colVis',
833
+ columns: '',
834
+ search: false,
835
+ select: false,
836
+ text: 'Column visibility',
837
+ title: 'Column visibility'
838
+ },
839
+ extend: function (config) {
840
+ var dt = this.dt();
841
+ return {
842
+ extend: 'dropdown',
843
+ icon: 'columns',
844
+ text: dt.i18n('columnControl.colVisDropdown', config.text),
845
+ content: [
846
+ Object.assign(config, {
847
+ extend: 'colVis'
848
+ })
849
+ ]
850
+ };
851
+ }
852
+ };
853
+
854
+ var reorder = {
855
+ defaults: {
856
+ className: 'reorder',
857
+ icon: 'move',
858
+ text: 'Reorder columns'
859
+ },
860
+ init: function (config) {
861
+ var _this = this;
862
+ var dt = this.dt();
863
+ var btn = new Button(dt)
864
+ .text(dt.i18n('columnControl.reorder', config.text))
865
+ .icon(config.icon)
866
+ .className(config.className)
867
+ .handler(function () {
868
+ var idx = _this.idx();
869
+ if (idx > 0) {
870
+ dt.colReorder.move(idx, idx - 1);
871
+ }
872
+ });
873
+ if (this.idx() === 0) {
874
+ btn.enable(false);
875
+ }
876
+ dt.on('columns-reordered', function (e, details) {
877
+ btn.enable(_this.idx() > 0);
878
+ });
879
+ // If ColReorder wasn't initialised on this DataTable, then we need to add it
880
+ if (!dt.init().colReorder) {
881
+ new DataTable.ColReorder(dt, {});
882
+ }
883
+ return btn.element();
884
+ }
885
+ };
886
+
887
+ var reorderLeft = {
888
+ defaults: {
889
+ className: 'reorderLeft',
890
+ icon: 'moveLeft',
891
+ text: 'Move column left'
892
+ },
893
+ init: function (config) {
894
+ var _this = this;
895
+ var dt = this.dt();
896
+ var btn = new Button(dt)
897
+ .text(dt.i18n('columnControl.reorderLeft', config.text))
898
+ .icon(config.icon)
899
+ .className(config.className)
900
+ .handler(function () {
901
+ var idx = _this.idx();
902
+ // TODO account for visibility
903
+ if (idx > 0) {
904
+ dt.colReorder.move(idx, idx - 1);
905
+ }
906
+ });
907
+ if (this.idx() === 0) {
908
+ btn.enable(false);
909
+ }
910
+ dt.on('columns-reordered', function (e, details) {
911
+ btn.enable(_this.idx() > 0);
912
+ });
913
+ return btn.element();
914
+ }
915
+ };
916
+
917
+ var reorderRight = {
918
+ defaults: {
919
+ className: 'reorderRight',
920
+ icon: 'moveRight',
921
+ text: 'Move column right'
922
+ },
923
+ init: function (config) {
924
+ var _this = this;
925
+ var dt = this.dt();
926
+ var btn = new Button(dt)
927
+ .text(dt.i18n('columnControl.reorderRight', config.text))
928
+ .icon(config.icon)
929
+ .className(config.className)
930
+ .handler(function () {
931
+ var idx = _this.idx();
932
+ if (idx < dt.columns().count() - 1) {
933
+ dt.colReorder.move(idx, idx + 1);
934
+ }
935
+ });
936
+ if (this.idx() === dt.columns().count() - 1) {
937
+ btn.enable(false);
938
+ }
939
+ dt.on('columns-reordered', function (e, details) {
940
+ btn.enable(_this.idx() < dt.columns().count() - 1);
941
+ });
942
+ return btn.element();
943
+ }
944
+ };
945
+
946
+ var order = {
947
+ defaults: {
948
+ className: 'order',
949
+ iconAsc: 'orderAsc',
950
+ iconDesc: 'orderDesc',
951
+ iconNone: 'orderNone',
952
+ statusOnly: false,
953
+ text: 'Toggle ordering'
954
+ },
955
+ init: function (config) {
956
+ var _this = this;
957
+ var dt = this.dt();
958
+ var btn = new Button(dt)
959
+ .text(dt.i18n('columnControl.order', config.text))
960
+ .icon('orderAsc')
961
+ .className(config.className);
962
+ if (!config.statusOnly) {
963
+ dt.order.listener(btn.element(), this.idx(), function () { });
964
+ }
965
+ dt.on('order', function (e, s, order) {
966
+ var found = order.find(function (o) { return o.col === _this.idx(); });
967
+ if (!found) {
968
+ btn.active(false).icon(config.iconNone);
969
+ }
970
+ else if (found.dir === 'asc') {
971
+ btn.active(true).icon(config.iconAsc);
972
+ }
973
+ else if (found.dir === 'desc') {
974
+ btn.active(true).icon(config.iconDesc);
975
+ }
976
+ });
977
+ return btn.element();
978
+ }
979
+ };
980
+
981
+ var orderAddAsc = {
982
+ defaults: {
983
+ className: 'orderAddAsc',
984
+ icon: 'orderAddAsc',
985
+ text: 'Add Sort Ascending'
986
+ },
987
+ init: function (config) {
988
+ var _this = this;
989
+ var dt = this.dt();
990
+ var btn = new Button(dt)
991
+ .text(dt.i18n('columnControl.orderAddAsc', config.text))
992
+ .icon(config.icon)
993
+ .className(config.className)
994
+ .handler(function () {
995
+ var order = dt.order();
996
+ order.push([_this.idx(), 'asc']);
997
+ dt.draw();
998
+ });
999
+ dt.on('order', function (e, s, order) {
1000
+ var found = order.some(function (o) { return o.col === _this.idx(); });
1001
+ btn.enable(!found);
1002
+ });
1003
+ return btn.element();
1004
+ }
1005
+ };
1006
+
1007
+ var orderAddDesc = {
1008
+ defaults: {
1009
+ className: 'orderAddDesc',
1010
+ icon: 'orderAddDesc',
1011
+ text: 'Add Sort Descending'
1012
+ },
1013
+ init: function (config) {
1014
+ var _this = this;
1015
+ var dt = this.dt();
1016
+ var btn = new Button(dt)
1017
+ .text(dt.i18n('columnControl.orderAddDesc', config.text))
1018
+ .icon(config.icon)
1019
+ .className(config.className)
1020
+ .handler(function () {
1021
+ var order = dt.order();
1022
+ order.push([_this.idx(), 'desc']);
1023
+ dt.draw();
1024
+ });
1025
+ dt.on('order', function (e, s, order) {
1026
+ var found = order.some(function (o) { return o.col === _this.idx(); });
1027
+ btn.enable(!found);
1028
+ });
1029
+ return btn.element();
1030
+ }
1031
+ };
1032
+
1033
+ var orderAsc = {
1034
+ defaults: {
1035
+ className: 'orderAsc',
1036
+ icon: 'orderAsc',
1037
+ text: 'Sort Ascending'
1038
+ },
1039
+ init: function (config) {
1040
+ var _this = this;
1041
+ var dt = this.dt();
1042
+ var btn = new Button(dt)
1043
+ .text(dt.i18n('columnControl.orderAsc', config.text))
1044
+ .icon(config.icon)
1045
+ .className(config.className)
1046
+ .handler(function () {
1047
+ _this.dt()
1048
+ .order([
1049
+ {
1050
+ idx: _this.idx(),
1051
+ dir: 'asc'
1052
+ }
1053
+ ])
1054
+ .draw();
1055
+ });
1056
+ dt.on('order', function (e, s, order) {
1057
+ var found = order.some(function (o) { return o.col === _this.idx() && o.dir === 'asc'; });
1058
+ btn.active(found);
1059
+ });
1060
+ return btn.element();
1061
+ }
1062
+ };
1063
+
1064
+ var orderClear = {
1065
+ defaults: {
1066
+ className: 'orderClear',
1067
+ icon: 'orderClear',
1068
+ text: 'Clear sort'
1069
+ },
1070
+ init: function (config) {
1071
+ var dt = this.dt();
1072
+ var btn = new Button(dt)
1073
+ .text(dt.i18n('columnControl.orderClear', config.text))
1074
+ .icon(config.icon)
1075
+ .className(config.className)
1076
+ .handler(function () {
1077
+ dt.order([]).draw();
1078
+ });
1079
+ dt.on('order', function (e, s, order) {
1080
+ btn.enable(order.length > 0);
1081
+ });
1082
+ if (dt.order().length === 0) {
1083
+ btn.enable(false);
1084
+ }
1085
+ return btn.element();
1086
+ }
1087
+ };
1088
+
1089
+ var orderDesc = {
1090
+ defaults: {
1091
+ className: 'orderDesc',
1092
+ icon: 'orderDesc',
1093
+ text: 'Sort Descending'
1094
+ },
1095
+ init: function (config) {
1096
+ var _this = this;
1097
+ var dt = this.dt();
1098
+ var btn = new Button(dt)
1099
+ .text(dt.i18n('columnControl.orderDesc', config.text))
1100
+ .icon(config.icon)
1101
+ .className(config.className)
1102
+ .handler(function () {
1103
+ _this.dt()
1104
+ .order([
1105
+ {
1106
+ idx: _this.idx(),
1107
+ dir: 'desc'
1108
+ }
1109
+ ])
1110
+ .draw();
1111
+ });
1112
+ dt.on('order', function (e, s, order) {
1113
+ var found = order.some(function (o) { return o.col === _this.idx() && o.dir === 'desc'; });
1114
+ btn.active(found);
1115
+ });
1116
+ return btn.element();
1117
+ }
1118
+ };
1119
+
1120
+ var orderRemove = {
1121
+ defaults: {
1122
+ className: 'orderRemove',
1123
+ icon: 'orderRemove',
1124
+ text: 'Remove from sort'
1125
+ },
1126
+ init: function (config) {
1127
+ var _this = this;
1128
+ var dt = this.dt();
1129
+ var btn = new Button(dt)
1130
+ .text(dt.i18n('columnControl.orderRemove', config.text))
1131
+ .icon(config.icon)
1132
+ .className(config.className)
1133
+ .handler(function () {
1134
+ // Remove the current column from the ordering array, then reorder the table
1135
+ var order = dt.order();
1136
+ var idx = order.findIndex(function (o) { return o[0] === _this.idx(); });
1137
+ order.splice(idx, 1);
1138
+ dt.order(order).draw();
1139
+ });
1140
+ dt.on('order', function (e, s, order) {
1141
+ var found = order.some(function (o) { return o.col === _this.idx(); });
1142
+ btn.enable(found);
1143
+ });
1144
+ btn.enable(false);
1145
+ return btn.element();
1146
+ }
1147
+ };
1148
+
1149
+ var orderStatus = {
1150
+ defaults: {
1151
+ className: 'order',
1152
+ iconAsc: 'orderAsc',
1153
+ iconDesc: 'orderDesc',
1154
+ iconNone: 'orderNone',
1155
+ statusOnly: true,
1156
+ text: 'Sort status'
1157
+ },
1158
+ extend: function (config) {
1159
+ return Object.assign(config, { extend: 'order' });
1160
+ }
1161
+ };
1162
+
1163
+ var SearchInput = /** @class */ (function () {
1164
+ /**
1165
+ * Create a container element, for consistent DOM structure and styling
1166
+ */
1167
+ function SearchInput(dt, idx) {
1168
+ var _this = this;
1169
+ this._type = 'text';
1170
+ this._dt = dt;
1171
+ this._idx = idx;
1172
+ this._dom = {
1173
+ clear: createElement('span', 'dtcc-search-clear', icons['x']),
1174
+ container: createElement('div', SearchInput.classes.container),
1175
+ typeIcon: createElement('div', 'dtcc-search-type-icon'),
1176
+ searchIcon: createElement('div', 'dtcc-search-icon', icons['search']),
1177
+ input: createElement('input', SearchInput.classes.input),
1178
+ inputs: createElement('div'),
1179
+ select: createElement('select', SearchInput.classes.select),
1180
+ title: createElement('div', 'dtcc-search-title')
1181
+ };
1182
+ var dom = this._dom;
1183
+ var originalIdx = idx;
1184
+ dom.input.setAttribute('type', 'text');
1185
+ dom.container.append(dom.title, dom.inputs);
1186
+ dom.inputs.append(dom.typeIcon, dom.select, dom.searchIcon, dom.clear, dom.input);
1187
+ // Listeners
1188
+ var inputInput = function () {
1189
+ _this.runSearch();
1190
+ };
1191
+ var selectInput = function () {
1192
+ dom.typeIcon.innerHTML = icons[dom.select.value];
1193
+ _this.runSearch();
1194
+ };
1195
+ var clearClick = function () {
1196
+ _this.clear();
1197
+ };
1198
+ dom.input.addEventListener('input', inputInput);
1199
+ dom.select.addEventListener('input', selectInput);
1200
+ dom.clear.addEventListener('click', clearClick);
1201
+ dt.on('destroy', function () {
1202
+ dom.input.removeEventListener('input', inputInput);
1203
+ dom.select.removeEventListener('input', selectInput);
1204
+ dom.clear.removeEventListener('click', clearClick);
1205
+ });
1206
+ // State handling - all components that use this class have the same state saving structure
1207
+ // so shared handling can be performed here.
1208
+ dt.on('stateSaveParams', function (e, s, data) {
1209
+ if (!data.columnControl) {
1210
+ data.columnControl = {};
1211
+ }
1212
+ if (!data.columnControl[idx]) {
1213
+ data.columnControl[idx] = {};
1214
+ }
1215
+ data.columnControl[idx].searchInput = {
1216
+ logic: dom.select.value,
1217
+ type: _this._type,
1218
+ value: dom.input.value
1219
+ };
1220
+ });
1221
+ dt.on('stateLoaded', function (e, s, state) {
1222
+ _this._stateLoad(state);
1223
+ });
1224
+ // Same as for ColumnControl - reassign a column index if needed.
1225
+ dt.on('columns-reordered', function (e, details) {
1226
+ _this._idx = dt.colReorder.transpose(originalIdx, 'fromOriginal');
1227
+ });
1228
+ // Column control search clearing (column().ccSearchClear() method)
1229
+ dt.on('cc-search-clear', function (e, colIdx) {
1230
+ if (colIdx === _this._idx) {
1231
+ _this.clear();
1232
+ }
1233
+ });
1234
+ }
1235
+ /**
1236
+ * Add a class to the container
1237
+ *
1238
+ * @param name Class name to add
1239
+ * @returns Self for chaining
1240
+ */
1241
+ SearchInput.prototype.addClass = function (name) {
1242
+ this._dom.container.classList.add(name);
1243
+ return this;
1244
+ };
1245
+ /**
1246
+ * Clear any applied search
1247
+ *
1248
+ * @returns Self for chaining
1249
+ */
1250
+ SearchInput.prototype.clear = function () {
1251
+ this.set(this._dom.select.children[0].getAttribute('value'), '');
1252
+ return this;
1253
+ };
1254
+ /**
1255
+ * Set the clear icon feature can be used or not
1256
+ *
1257
+ * @param set Flag
1258
+ * @returns Self for chaining
1259
+ */
1260
+ SearchInput.prototype.clearable = function (set) {
1261
+ // Note there is no add here as it is added by default and never used after setup, so
1262
+ // no need.
1263
+ if (!set) {
1264
+ this._dom.clear.remove();
1265
+ }
1266
+ return this;
1267
+ };
1268
+ /**
1269
+ * Get the container element
1270
+ *
1271
+ * @returns The container element
1272
+ */
1273
+ SearchInput.prototype.element = function () {
1274
+ return this._dom.container;
1275
+ };
1276
+ /**
1277
+ * Get the HTML input element for this control
1278
+ *
1279
+ * @returns HTML Input element
1280
+ */
1281
+ SearchInput.prototype.input = function () {
1282
+ return this._dom.input;
1283
+ };
1284
+ /**
1285
+ * Set the list of options for the dropdown
1286
+ *
1287
+ * @param opts List of options
1288
+ * @returns Self for chaining
1289
+ */
1290
+ SearchInput.prototype.options = function (opts) {
1291
+ var select = this._dom.select;
1292
+ for (var i = 0; i < opts.length; i++) {
1293
+ select.add(new Option(opts[i].label, opts[i].value));
1294
+ }
1295
+ // Initial icon
1296
+ this._dom.typeIcon.innerHTML = icons[opts[0].value];
1297
+ return this;
1298
+ };
1299
+ /**
1300
+ * Set the placeholder attribute for the input element
1301
+ *
1302
+ * @param placeholder Placeholder string
1303
+ * @returns Self for chaining
1304
+ */
1305
+ SearchInput.prototype.placeholder = function (placeholder) {
1306
+ if (placeholder) {
1307
+ var columnTitle = this._dt.column(this._idx).title();
1308
+ this._dom.input.placeholder = placeholder.replace('[title]', columnTitle);
1309
+ }
1310
+ return this;
1311
+ };
1312
+ /**
1313
+ * Run the search method
1314
+ */
1315
+ SearchInput.prototype.runSearch = function () {
1316
+ var dom = this._dom;
1317
+ var isActive = dom.select.value === 'empty' ||
1318
+ dom.select.value === 'notEmpty' ||
1319
+ dom.input.value !== '';
1320
+ dom.container.classList.toggle('dtcc-search_active', isActive);
1321
+ if (this._search &&
1322
+ (this._lastValue !== dom.input.value || this._lastType !== dom.select.value)) {
1323
+ this._search(dom.select.value, dom.input.value, this._loadingState);
1324
+ this._lastValue = dom.input.value;
1325
+ this._lastType = dom.select.value;
1326
+ }
1327
+ };
1328
+ /**
1329
+ * Set the function that will be run when a search operation is required. Note that this can
1330
+ * trigger the function to run if there is a saved state.
1331
+ *
1332
+ * @param fn Search callback
1333
+ * @returns Self for chaining
1334
+ */
1335
+ SearchInput.prototype.search = function (fn) {
1336
+ this._search = fn;
1337
+ // If there is a saved state, load it now that set up is done.
1338
+ this._stateLoad(this._dt.state.loaded());
1339
+ return this;
1340
+ };
1341
+ /**
1342
+ * Set a value for the search input
1343
+ *
1344
+ * @param logic Logic type
1345
+ * @param val Value
1346
+ * @returns Self for chaining
1347
+ */
1348
+ SearchInput.prototype.set = function (logic, val) {
1349
+ var dom = this._dom;
1350
+ dom.input.value = val;
1351
+ dom.select.value = logic;
1352
+ dom.typeIcon.innerHTML = icons[dom.select.value];
1353
+ this.runSearch();
1354
+ return this;
1355
+ };
1356
+ /**
1357
+ * Set the text that will be shown as the title for the control
1358
+ *
1359
+ * @param text Set the title text
1360
+ * @returns Self for chaining
1361
+ */
1362
+ SearchInput.prototype.title = function (text) {
1363
+ if (text) {
1364
+ var columnTitle = this._dt.column(this._idx).title();
1365
+ this._dom.title.innerHTML = text.replace('[title]', columnTitle);
1366
+ }
1367
+ return this;
1368
+ };
1369
+ /**
1370
+ * Set the title attribute for the input element
1371
+ *
1372
+ * @param title Title attribute string
1373
+ * @returns Self for chaining
1374
+ */
1375
+ SearchInput.prototype.titleAttr = function (title) {
1376
+ if (title) {
1377
+ var columnTitle = this._dt.column(this._idx).title();
1378
+ this._dom.input.title = title.replace('[title]', columnTitle);
1379
+ }
1380
+ return this;
1381
+ };
1382
+ SearchInput.prototype.type = function (t) {
1383
+ this._type = t;
1384
+ return this;
1385
+ };
1386
+ /**
1387
+ * Load a DataTables state
1388
+ *
1389
+ * @param state State object being loaded
1390
+ */
1391
+ SearchInput.prototype._stateLoad = function (state) {
1392
+ var _a, _b;
1393
+ var dom = this._dom;
1394
+ var idx = this._idx;
1395
+ var loadedState = (_b = (_a = state === null || state === void 0 ? void 0 : state.columnControl) === null || _a === void 0 ? void 0 : _a[idx]) === null || _b === void 0 ? void 0 : _b.searchInput;
1396
+ if (loadedState) {
1397
+ // The search callback needs to know if we are loading an existing state or not
1398
+ // so it can determine if it needs to draw the table. If it was a user input, then
1399
+ // it redraws, if it was a state load, then there should be no redraw.
1400
+ this._loadingState = true;
1401
+ dom.select.value = loadedState.logic;
1402
+ dom.input.value = loadedState.value;
1403
+ dom.select.dispatchEvent(new Event('input'));
1404
+ this._loadingState = false;
1405
+ }
1406
+ };
1407
+ SearchInput.classes = {
1408
+ container: ['dtcc-content', 'dtcc-search'],
1409
+ input: '',
1410
+ select: ''
1411
+ };
1412
+ return SearchInput;
1413
+ }());
1414
+
1415
+ var searchDateTime = {
1416
+ defaults: {
1417
+ clear: true,
1418
+ placeholder: '',
1419
+ title: '',
1420
+ titleAttr: ''
1421
+ },
1422
+ init: function (config) {
1423
+ var _this = this;
1424
+ var fromPicker = false;
1425
+ var moment = DataTable.use('moment');
1426
+ var luxon = DataTable.use('luxon');
1427
+ var dt = this.dt();
1428
+ var displayFormat = '';
1429
+ var dateTime;
1430
+ var searchInput = new SearchInput(dt, this.idx())
1431
+ .type('date')
1432
+ .addClass('dtcc-searchDate')
1433
+ .clearable(config.clear)
1434
+ .placeholder(config.placeholder)
1435
+ .title(config.title)
1436
+ .titleAttr(config.titleAttr)
1437
+ .options([
1438
+ { label: 'Equals', value: 'equal' },
1439
+ { label: 'Does not equal', value: 'notEqual' },
1440
+ { label: 'After', value: 'greater' },
1441
+ { label: 'Before', value: 'less' },
1442
+ { label: 'Empty', value: 'empty' },
1443
+ { label: 'Not empty', value: 'notEmpty' }
1444
+ ])
1445
+ .search(function (searchType, searchTerm, loadingState) {
1446
+ var column = dt.column(_this.idx());
1447
+ var search = searchTerm === ''
1448
+ ? ''
1449
+ : dateToNum(dateTime && fromPicker ? dateTime.val() : searchTerm.trim(), displayFormat, moment, luxon);
1450
+ if (searchType === 'empty') {
1451
+ column.search.fixed('dtcc', function (haystack) { return !haystack; });
1452
+ }
1453
+ else if (searchType === 'notEmpty') {
1454
+ column.search.fixed('dtcc', function (haystack) { return !!haystack; });
1455
+ }
1456
+ else if (column.search.fixed('dtcc') === '' && search === '') {
1457
+ // No change - don't do anything
1458
+ return;
1459
+ }
1460
+ else if (!search) {
1461
+ // Clear search
1462
+ column.search.fixed('dtcc', '');
1463
+ }
1464
+ else if (searchType === 'equal') {
1465
+ // Use a function for matching - weak typing
1466
+ // Note that the haystack in the search function is the rendered date - it
1467
+ // might need to be converted back to a date
1468
+ column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) == search; });
1469
+ }
1470
+ else if (searchType === 'notEqual') {
1471
+ column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) != search; });
1472
+ }
1473
+ else if (searchType === 'greater') {
1474
+ column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) > search; });
1475
+ }
1476
+ else if (searchType === 'less') {
1477
+ column.search.fixed('dtcc', function (haystack) { return dateToNum(haystack, displayFormat, moment, luxon) < search; });
1478
+ }
1479
+ // If in a dropdown, set the parent levels as active
1480
+ if (config._parents) {
1481
+ config._parents.forEach(function (btn) {
1482
+ return btn.activeList(_this.unique(), !!column.search.fixed('dtcc'));
1483
+ });
1484
+ }
1485
+ if (!loadingState) {
1486
+ column.draw();
1487
+ }
1488
+ });
1489
+ // Once data has been loaded we can run DateTime with the specified format
1490
+ dt.ready(function () {
1491
+ var DateTime = DataTable.use('datetime');
1492
+ displayFormat = getFormat(dt, _this.idx());
1493
+ if (DateTime) {
1494
+ dateTime = new DateTime(searchInput.input(), {
1495
+ format: displayFormat,
1496
+ onChange: function () {
1497
+ fromPicker = true;
1498
+ searchInput.runSearch();
1499
+ fromPicker = false;
1500
+ }
1501
+ });
1502
+ }
1503
+ });
1504
+ return searchInput.element();
1505
+ }
1506
+ };
1507
+ /**
1508
+ * Determine the formatting string for the date time information in the colum
1509
+ *
1510
+ * @param dt DataTable instance
1511
+ * @param column Column index
1512
+ * @returns Date / time formatting string
1513
+ */
1514
+ function getFormat(dt, column) {
1515
+ var type = dt.column(column).type();
1516
+ if (!type) {
1517
+ // Assume that it is ISO unless otherwise specified - that is all DataTables can do anyway
1518
+ return 'YYYY-MM-DD';
1519
+ }
1520
+ else if (type === 'datetime') {
1521
+ // If no format was specified in the DT type, then we need to use Moment / Luxon's default
1522
+ // locale formatting.
1523
+ var moment = DataTable.use('moment');
1524
+ var luxon = DataTable.use('luxon');
1525
+ if (moment) {
1526
+ return moment().creationData().locale._longDateFormat.L;
1527
+ }
1528
+ if (luxon) {
1529
+ // Luxon doesn't appear to provide a way to let us get the default locale formatting
1530
+ // string, so we need to attempt to decode it.
1531
+ return luxon.DateTime.fromISO('1999-08-07')
1532
+ .toLocaleString()
1533
+ .replace('07', 'dd')
1534
+ .replace('7', 'd')
1535
+ .replace('08', 'MM')
1536
+ .replace('8', 'M')
1537
+ .replace('1999', 'yyyy')
1538
+ .replace('99', 'yy');
1539
+ }
1540
+ }
1541
+ else if (type.includes('datetime-')) {
1542
+ // Column was specified with a particular display format - we can extract that format from
1543
+ // the type, as it is part of the type name.
1544
+ return type.replace(/datetime-/g, '');
1545
+ }
1546
+ else if (type.includes('moment')) {
1547
+ return type.replace(/moment-/g, '');
1548
+ }
1549
+ else if (type.includes('luxon')) {
1550
+ return type.replace(/luxon-/g, '');
1551
+ }
1552
+ return 'YYYY-MM-DD';
1553
+ }
1554
+ /**
1555
+ * Convert from a source date / time value (usually a string) to a timestamp for comparisons.
1556
+ *
1557
+ * @param input Input value
1558
+ * @param srcFormat String format of the input
1559
+ * @param moment Moment instance, if it is available
1560
+ * @param luxon Luxon object, if it is available
1561
+ * @returns Time stamp - milliseconds
1562
+ */
1563
+ function dateToNum(input, srcFormat, moment, luxon) {
1564
+ if (input === '') {
1565
+ return '';
1566
+ }
1567
+ else if (input instanceof Date) {
1568
+ return input.getTime();
1569
+ }
1570
+ else if (srcFormat !== 'YYYY-MM-DD' && (moment || luxon)) {
1571
+ return moment
1572
+ ? moment(input, srcFormat).unix() * 1000
1573
+ : luxon.DateTime.fromFormat(input, srcFormat).toMillis();
1574
+ }
1575
+ return new Date(input).getTime();
1576
+ }
1577
+
1578
+ /** Set the options to show in the list */
1579
+ function setOptions(checkList, opts) {
1580
+ var existing = checkList.values();
1581
+ checkList.clear();
1582
+ for (var i = 0; i < opts.length; i++) {
1583
+ if (typeof opts[i] === 'object') {
1584
+ checkList.add({
1585
+ active: false,
1586
+ label: opts[i].label,
1587
+ value: opts[i].value
1588
+ }, i === opts.length - 1);
1589
+ }
1590
+ else {
1591
+ checkList.add({
1592
+ active: false,
1593
+ label: opts[i],
1594
+ value: opts[i]
1595
+ }, i === opts.length - 1);
1596
+ }
1597
+ }
1598
+ if (existing.length) {
1599
+ checkList.values(existing);
1600
+ }
1601
+ }
1602
+ /** Load a saved state */
1603
+ function getState(columnIdx, state) {
1604
+ var _a, _b;
1605
+ var loadedState = (_b = (_a = state === null || state === void 0 ? void 0 : state.columnControl) === null || _a === void 0 ? void 0 : _a[columnIdx]) === null || _b === void 0 ? void 0 : _b.searchList;
1606
+ if (loadedState) {
1607
+ return loadedState;
1608
+ }
1609
+ }
1610
+ /** Get the options for a column from a DT JSON object */
1611
+ function getJsonOptions(dt, idx) {
1612
+ var _a;
1613
+ var json = (_a = dt.ajax.json()) === null || _a === void 0 ? void 0 : _a.columnControl;
1614
+ var column = dt.column(idx);
1615
+ var name = column.name();
1616
+ var dataSrc = column.dataSrc();
1617
+ if (json && json[name]) {
1618
+ // Found options matching the column's name - top priority
1619
+ return json[name];
1620
+ }
1621
+ else if (json && typeof dataSrc === 'string' && json[dataSrc]) {
1622
+ // Found options matching the column's data source string
1623
+ return json[dataSrc];
1624
+ }
1625
+ else if (json && json[idx]) {
1626
+ // Found options matching the column's data index
1627
+ return json[idx];
1628
+ }
1629
+ return null;
1630
+ }
1631
+ function reloadOptions(dt, config, idx, checkList, loadedValues) {
1632
+ var _a;
1633
+ // Was there options specified in the Ajax return?
1634
+ var json = (_a = dt.ajax.json()) === null || _a === void 0 ? void 0 : _a.columnControl;
1635
+ var options = [];
1636
+ var jsonOptions = getJsonOptions(dt, idx);
1637
+ if (jsonOptions) {
1638
+ options = jsonOptions;
1639
+ }
1640
+ else if (json && config.ajaxOnly) {
1641
+ // Ajax only options - need to hide the search list
1642
+ checkList.element().style.display = 'none';
1643
+ // Check if the parent buttons should be hidden as well (they will be if there
1644
+ // is no visible content in them)
1645
+ if (config._parents) {
1646
+ config._parents.forEach(function (btn) { return btn.checkDisplay(); });
1647
+ }
1648
+ // No point in doing any further processing here
1649
+ return;
1650
+ }
1651
+ else {
1652
+ // Either no ajax object (i.e. not an Ajax table), or no matching ajax options
1653
+ // for this column - get the values for the column, taking into account
1654
+ // orthogonal rendering
1655
+ var found_1 = {};
1656
+ dt.cells('*', idx, { order: idx }).every(function () {
1657
+ var filter = this.render('filter');
1658
+ if (!found_1[filter]) {
1659
+ found_1[filter] = true;
1660
+ options.push({
1661
+ label: this.render('display'),
1662
+ value: filter
1663
+ });
1664
+ }
1665
+ });
1666
+ }
1667
+ setOptions(checkList, options);
1668
+ // If there was a state loaded at start up, then we need to set the visual
1669
+ // appearance to match
1670
+ if (loadedValues) {
1671
+ checkList.values(loadedValues);
1672
+ }
1673
+ }
1674
+ var searchList = {
1675
+ defaults: {
1676
+ ajaxOnly: true,
1677
+ className: 'searchList',
1678
+ options: null,
1679
+ search: true,
1680
+ select: true,
1681
+ title: ''
1682
+ },
1683
+ init: function (config) {
1684
+ var _this = this;
1685
+ var loadedValues = null;
1686
+ var dt = this.dt();
1687
+ // The search can be applied from a stored start at start up before the options are
1688
+ // available. It can also be applied by user input, so it is generalised into this function.
1689
+ var applySearch = function (values) {
1690
+ var col = dt.column(_this.idx());
1691
+ if (!values) {
1692
+ return;
1693
+ }
1694
+ else if (values.length === 0) {
1695
+ // Nothing selected - clear the filter
1696
+ col.search.fixed('dtcc-list', '');
1697
+ }
1698
+ else {
1699
+ // Find all matching options from the list of values
1700
+ col.search.fixed('dtcc-list', function (val) {
1701
+ return values.includes(val);
1702
+ });
1703
+ }
1704
+ // If in a dropdown, set the parent levels as active
1705
+ if (config._parents) {
1706
+ config._parents.forEach(function (btn) { return btn.activeList(_this.unique(), !!values.length); });
1707
+ }
1708
+ };
1709
+ var checkList = new CheckList(dt, {
1710
+ search: config.search,
1711
+ select: config.select
1712
+ })
1713
+ .searchListener(dt, this)
1714
+ .title(dt
1715
+ .i18n('columnControl.searchList', config.title)
1716
+ .replace('[title]', dt.column(this.idx()).title()))
1717
+ .handler(function (e, btn) {
1718
+ if (btn) {
1719
+ btn.active(!btn.active());
1720
+ }
1721
+ applySearch(checkList.values());
1722
+ dt.draw();
1723
+ });
1724
+ if (config.options) {
1725
+ setOptions(checkList, config.options);
1726
+ }
1727
+ else {
1728
+ dt.ready(function () {
1729
+ reloadOptions(dt, config, _this.idx(), checkList, loadedValues);
1730
+ });
1731
+ }
1732
+ // Xhr event listener for updates of options
1733
+ dt.on('xhr', function (e, s, json) {
1734
+ reloadOptions(dt, config, _this.idx(), checkList, loadedValues);
1735
+ });
1736
+ // Unlike the SearchInput based search contents, CheckList does not handle state saving
1737
+ // (since the mechanism for column visibility is different), so state saving is handled
1738
+ // here.
1739
+ dt.on('stateLoaded', function (e, s, state) {
1740
+ var values = getState(_this.idx(), state);
1741
+ if (values) {
1742
+ checkList.values(values);
1743
+ }
1744
+ });
1745
+ dt.on('stateSaveParams', function (e, s, data) {
1746
+ var idx = _this.idx();
1747
+ if (!data.columnControl) {
1748
+ data.columnControl = {};
1749
+ }
1750
+ if (!data.columnControl[idx]) {
1751
+ data.columnControl[idx] = {};
1752
+ }
1753
+ // If the table isn't yet ready, then the options for the list won't have been
1754
+ // populated (above) and therefore there can't be an values. In such a case
1755
+ // use the last saved values and this will refresh on the next draw.
1756
+ data.columnControl[idx].searchList = dt.ready()
1757
+ ? checkList.values()
1758
+ : loadedValues;
1759
+ });
1760
+ loadedValues = getState(this.idx(), dt.state.loaded());
1761
+ applySearch(loadedValues);
1762
+ return checkList.element();
1763
+ }
1764
+ };
1765
+
1766
+ var searchNumber = {
1767
+ defaults: {
1768
+ clear: true,
1769
+ placeholder: '',
1770
+ title: '',
1771
+ titleAttr: ''
1772
+ },
1773
+ init: function (config) {
1774
+ var _this = this;
1775
+ var dt = this.dt();
1776
+ var searchInput = new SearchInput(dt, this.idx())
1777
+ .type('num')
1778
+ .addClass('dtcc-searchNumber')
1779
+ .clearable(config.clear)
1780
+ .placeholder(config.placeholder)
1781
+ .title(config.title)
1782
+ .titleAttr(config.titleAttr)
1783
+ .options([
1784
+ { label: 'Equals', value: 'equal' },
1785
+ { label: 'Does not equal', value: 'notEqual' },
1786
+ { label: 'Greater than', value: 'greater' },
1787
+ { label: 'Greater or equal', value: 'greaterOrEqual' },
1788
+ { label: 'Less than', value: 'less' },
1789
+ { label: 'Less or equal', value: 'lessOrEqual' },
1790
+ { label: 'Empty', value: 'empty' },
1791
+ { label: 'Not empty', value: 'notEmpty' }
1792
+ ])
1793
+ .search(function (searchType, searchTerm, loadingState) {
1794
+ var column = dt.column(_this.idx());
1795
+ if (searchType === 'empty') {
1796
+ column.search.fixed('dtcc', function (haystack) { return !haystack; });
1797
+ }
1798
+ else if (searchType === 'notEmpty') {
1799
+ column.search.fixed('dtcc', function (haystack) { return !!haystack; });
1800
+ }
1801
+ else if (column.search.fixed('dtcc') === '' && searchTerm === '') {
1802
+ // No change - don't do anything
1803
+ return;
1804
+ }
1805
+ else if (searchTerm === '') {
1806
+ // Clear search
1807
+ column.search.fixed('dtcc', '');
1808
+ }
1809
+ else if (searchType === 'equal') {
1810
+ // Use a function for matching - weak typing
1811
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) == searchTerm; });
1812
+ }
1813
+ else if (searchType === 'notEqual') {
1814
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) != searchTerm; });
1815
+ }
1816
+ else if (searchType === 'greater') {
1817
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) > searchTerm; });
1818
+ }
1819
+ else if (searchType === 'greaterOrEqual') {
1820
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) >= searchTerm; });
1821
+ }
1822
+ else if (searchType === 'less') {
1823
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) < searchTerm; });
1824
+ }
1825
+ else if (searchType === 'lessOrEqual') {
1826
+ column.search.fixed('dtcc', function (haystack) { return stringToNum(haystack) <= searchTerm; });
1827
+ }
1828
+ // If in a dropdown, set the parents as active
1829
+ if (config._parents) {
1830
+ config._parents.forEach(function (btn) {
1831
+ return btn.activeList(_this.unique(), !!column.search.fixed('dtcc'));
1832
+ });
1833
+ }
1834
+ if (!loadingState) {
1835
+ column.draw();
1836
+ }
1837
+ });
1838
+ // Set a numeric input type, per BBC's guidelines
1839
+ searchInput.input().setAttribute('inputmode', 'numeric');
1840
+ searchInput.input().setAttribute('pattern', '[0-9]*');
1841
+ return searchInput.element();
1842
+ }
1843
+ };
1844
+ var _re_html = /<([^>]*>)/g;
1845
+ var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi;
1846
+ function stringToNum(d) {
1847
+ if (d !== 0 && (!d || d === '-')) {
1848
+ return -Infinity;
1849
+ }
1850
+ var type = typeof d;
1851
+ if (type === 'number' || type === 'bigint') {
1852
+ return d;
1853
+ }
1854
+ if (d.replace) {
1855
+ d = d.replace(_re_html, '').replace(_re_formatted_numeric, '');
1856
+ }
1857
+ return d * 1;
1858
+ }
1859
+
1860
+ var searchText = {
1861
+ defaults: {
1862
+ clear: true,
1863
+ placeholder: '',
1864
+ title: '',
1865
+ titleAttr: ''
1866
+ },
1867
+ init: function (config) {
1868
+ var _this = this;
1869
+ var dt = this.dt();
1870
+ var searchInput = new SearchInput(dt, this.idx())
1871
+ .addClass('dtcc-searchText')
1872
+ .clearable(config.clear)
1873
+ .placeholder(config.placeholder)
1874
+ .title(config.title)
1875
+ .titleAttr(config.titleAttr)
1876
+ .options([
1877
+ { label: 'Contains', value: 'contains' },
1878
+ { label: 'Does not contain', value: 'notContains' },
1879
+ { label: 'Equals', value: 'equal' },
1880
+ { label: 'Does not equal', value: 'notEqual' },
1881
+ { label: 'Starts', value: 'starts' },
1882
+ { label: 'Ends', value: 'ends' },
1883
+ { label: 'Empty', value: 'empty' },
1884
+ { label: 'Not empty', value: 'notEmpty' }
1885
+ ])
1886
+ .search(function (searchType, searchTerm, loadingState) {
1887
+ var column = dt.column(_this.idx());
1888
+ searchTerm = searchTerm.toLowerCase();
1889
+ if (searchType === 'empty') {
1890
+ column.search.fixed('dtcc', function (haystack) { return !haystack; });
1891
+ }
1892
+ else if (searchType === 'notEmpty') {
1893
+ column.search.fixed('dtcc', function (haystack) { return !!haystack; });
1894
+ }
1895
+ else if (column.search.fixed('dtcc') === '' && searchTerm === '') {
1896
+ // No change - don't do anything
1897
+ return;
1898
+ }
1899
+ else if (searchTerm === '') {
1900
+ // Clear search
1901
+ column.search.fixed('dtcc', '');
1902
+ }
1903
+ else if (searchType === 'equal') {
1904
+ // Use a function for exact matching
1905
+ column.search.fixed('dtcc', function (haystack) { return haystack.toLowerCase() == searchTerm; });
1906
+ }
1907
+ else if (searchType === 'notEqual') {
1908
+ column.search.fixed('dtcc', function (haystack) { return haystack.toLowerCase() != searchTerm; });
1909
+ }
1910
+ else if (searchType === 'contains') {
1911
+ // Use the built in smart search
1912
+ column.search.fixed('dtcc', searchTerm);
1913
+ }
1914
+ else if (searchType === 'notContains') {
1915
+ // Use the built in smart search
1916
+ column.search.fixed('dtcc', function (haystack) { return !haystack.toLowerCase().includes(searchTerm); });
1917
+ }
1918
+ else if (searchType === 'starts') {
1919
+ // Use a function for startsWith case insensitive search
1920
+ column.search.fixed('dtcc', function (haystack) {
1921
+ return haystack.toLowerCase().startsWith(searchTerm);
1922
+ });
1923
+ }
1924
+ else if (searchType === 'ends') {
1925
+ column.search.fixed('dtcc', function (haystack) {
1926
+ return haystack.toLowerCase().endsWith(searchTerm);
1927
+ });
1928
+ }
1929
+ // If in a dropdown, set the parent levels as active
1930
+ if (config._parents) {
1931
+ config._parents.forEach(function (btn) {
1932
+ return btn.activeList(_this.unique(), !!column.search.fixed('dtcc'));
1933
+ });
1934
+ }
1935
+ if (!loadingState) {
1936
+ column.draw();
1937
+ }
1938
+ });
1939
+ return searchInput.element();
1940
+ }
1941
+ };
1942
+
1943
+ var search = {
1944
+ defaults: {
1945
+ allowSearchList: false
1946
+ },
1947
+ init: function (config) {
1948
+ var _this = this;
1949
+ var _a, _b, _c;
1950
+ var dt = this.dt();
1951
+ var idx = this.idx();
1952
+ var displayEl;
1953
+ var loadedState = (_c = (_b = (_a = dt.state.loaded()) === null || _a === void 0 ? void 0 : _a.columnControl) === null || _b === void 0 ? void 0 : _b[idx]) === null || _c === void 0 ? void 0 : _c.searchInput;
1954
+ var initType = function (type) {
1955
+ var json = getJsonOptions(dt, idx);
1956
+ // Attempt to match what type of search should be shown
1957
+ if (config.allowSearchList && json) {
1958
+ // We've got a list of JSON options, and are allowed to show the searchList
1959
+ return searchList.init.call(_this, Object.assign({}, searchList.defaults, config));
1960
+ }
1961
+ else if (type === 'date' || type.startsWith('datetime')) {
1962
+ // Date types
1963
+ return searchDateTime.init.call(_this, Object.assign({}, searchDateTime.defaults, config));
1964
+ }
1965
+ else if (type.includes('num')) {
1966
+ // Number types
1967
+ return searchNumber.init.call(_this, Object.assign({}, searchNumber.defaults, config));
1968
+ }
1969
+ else {
1970
+ // Everything else
1971
+ return searchText.init.call(_this, Object.assign({}, searchText.defaults, config));
1972
+ }
1973
+ };
1974
+ // If we know the type from the saved state, we can load it immediately. This is required
1975
+ // to allow the state to be applied to the table and the first draw to have a filter
1976
+ // applied (if it is needed).
1977
+ if (loadedState) {
1978
+ displayEl = initType(loadedState.type);
1979
+ }
1980
+ else {
1981
+ // Wait until we can get the data type for the column and the run the corresponding type
1982
+ displayEl = document.createElement('div');
1983
+ dt.ready(function () {
1984
+ var column = dt.column(idx);
1985
+ var display = initType(column.type());
1986
+ displayEl.replaceWith(display);
1987
+ });
1988
+ }
1989
+ return displayEl;
1990
+ }
1991
+ };
1992
+
1993
+ var searchClear = {
1994
+ defaults: {
1995
+ className: 'searchClear',
1996
+ icon: 'searchClear',
1997
+ text: 'Clear Search'
1998
+ },
1999
+ init: function (config) {
2000
+ var _this = this;
2001
+ var dt = this.dt();
2002
+ var btn = new Button(dt)
2003
+ .text(dt.i18n('columnControl.searchClear', config.text))
2004
+ .icon(config.icon)
2005
+ .className(config.className)
2006
+ .handler(function () {
2007
+ dt.column(_this.idx()).ccSearchClear();
2008
+ })
2009
+ .enable(false);
2010
+ dt.on('draw', function () {
2011
+ // change enable state
2012
+ var search = dt.column(_this.idx()).search.fixed('dtcc');
2013
+ var searchList = dt.column(_this.idx()).search.fixed('dtcc-list');
2014
+ btn.enable(!!(search || searchList));
2015
+ });
2016
+ return btn.element();
2017
+ }
2018
+ };
2019
+
2020
+ var searchDropdown = {
2021
+ defaults: {
2022
+ ajaxOnly: true,
2023
+ allowSearchList: true,
2024
+ className: 'searchDropdown',
2025
+ clear: true,
2026
+ columns: '',
2027
+ options: null,
2028
+ placeholder: '',
2029
+ search: true,
2030
+ select: true,
2031
+ text: 'Search',
2032
+ title: '',
2033
+ titleAttr: ''
2034
+ },
2035
+ extend: function (config) {
2036
+ var dt = this.dt();
2037
+ return {
2038
+ extend: 'dropdown',
2039
+ icon: 'search',
2040
+ text: dt.i18n('columnControl.searchDropdown', config.text),
2041
+ content: [
2042
+ Object.assign(config, {
2043
+ extend: 'search'
2044
+ })
2045
+ ]
2046
+ };
2047
+ }
2048
+ };
2049
+
2050
+ var spacer = {
2051
+ defaults: {
2052
+ className: 'dtcc-spacer',
2053
+ text: ''
2054
+ },
2055
+ init: function (config) {
2056
+ var dt = this.dt();
2057
+ var spacer = createElement('div', config.className, dt.i18n('columnControl.spacer', config.text));
2058
+ return spacer;
2059
+ }
2060
+ };
2061
+
2062
+ var title = {
2063
+ defaults: {
2064
+ className: 'dtcc-title',
2065
+ text: null
2066
+ },
2067
+ init: function (config) {
2068
+ var dt = this.dt();
2069
+ var title = dt.column(this.idx()).title();
2070
+ var text = config.text === null ? '[title]' : config.text;
2071
+ var el = createElement('div', config.className, text.replace('[title]', title));
2072
+ return el;
2073
+ }
2074
+ };
2075
+
2076
+ var contentTypes = {
2077
+ colVis: colVis,
2078
+ colVisDropdown: colVisDropdown,
2079
+ dropdown: dropdownContent,
2080
+ reorder: reorder,
2081
+ reorderLeft: reorderLeft,
2082
+ reorderRight: reorderRight,
2083
+ order: order,
2084
+ orderAddAsc: orderAddAsc,
2085
+ orderAddDesc: orderAddDesc,
2086
+ orderAsc: orderAsc,
2087
+ orderClear: orderClear,
2088
+ orderDesc: orderDesc,
2089
+ orderRemove: orderRemove,
2090
+ orderStatus: orderStatus,
2091
+ search: search,
2092
+ searchClear: searchClear,
2093
+ searchDropdown: searchDropdown,
2094
+ searchDateTime: searchDateTime,
2095
+ searchList: searchList,
2096
+ searchNumber: searchNumber,
2097
+ searchText: searchText,
2098
+ spacer: spacer,
2099
+ title: title
2100
+ };
2101
+
2102
+ /**
2103
+ *
2104
+ */
2105
+ var ColumnControl = /** @class */ (function () {
2106
+ /**
2107
+ * Create a new ColumnControl instance to control a DataTables column.
2108
+ *
2109
+ * @param dt DataTables API instance
2110
+ * @param columnIdx Column index to operation on
2111
+ * @param opts Configuration options
2112
+ */
2113
+ function ColumnControl(dt, columnIdx, opts) {
2114
+ var _this = this;
2115
+ this._dom = {
2116
+ target: null,
2117
+ wrapper: null
2118
+ };
2119
+ this._c = {};
2120
+ this._s = {
2121
+ columnIdx: null,
2122
+ unique: null
2123
+ };
2124
+ this._dt = dt;
2125
+ this._s.columnIdx = columnIdx;
2126
+ this._s.unique = Math.random();
2127
+ var originalIdx = columnIdx;
2128
+ Object.assign(this._c, ColumnControl.defaults, opts);
2129
+ this._dom.target = this._target();
2130
+ if (opts.className) {
2131
+ addClass(this._dom.target.closest('tr'), opts.className);
2132
+ }
2133
+ if (this._c.content) {
2134
+ // If column reordering can be done, we reassign the column index here, and before the
2135
+ // plugins can add their own listeners.
2136
+ dt.on('columns-reordered', function (e, details) {
2137
+ _this._s.columnIdx = dt.colReorder.transpose(originalIdx, 'fromOriginal');
2138
+ });
2139
+ this._dom.wrapper = document.createElement('span');
2140
+ this._dom.wrapper.classList.add('dtcc');
2141
+ this._dom.target.appendChild(this._dom.wrapper);
2142
+ this._c.content.forEach(function (content) {
2143
+ var _a = _this.resolve(content), plugin = _a.plugin, config = _a.config;
2144
+ var el = plugin.init.call(_this, config);
2145
+ _this._dom.wrapper.appendChild(el);
2146
+ });
2147
+ dt.on('destroy', function () {
2148
+ _this._dom.wrapper.remove();
2149
+ });
2150
+ }
2151
+ }
2152
+ /**
2153
+ * Get the DataTables API instance that hosts this instance of ColumnControl
2154
+ *
2155
+ * @returns DataTables API instance
2156
+ */
2157
+ ColumnControl.prototype.dt = function () {
2158
+ return this._dt;
2159
+ };
2160
+ /**
2161
+ * Get what column index this instance of ColumnControl is operating on
2162
+ *
2163
+ * @returns Column index
2164
+ */
2165
+ ColumnControl.prototype.idx = function () {
2166
+ return this._s.columnIdx;
2167
+ };
2168
+ /**
2169
+ * Covert the options from `content` in the DataTable initialisation for this instance into a
2170
+ * resolved plugin and options.
2171
+ *
2172
+ * @param content The dev's supplied configuration for the content
2173
+ * @returns Resolved plugin information
2174
+ */
2175
+ ColumnControl.prototype.resolve = function (content) {
2176
+ var plugin = null;
2177
+ var config = null;
2178
+ var type = null;
2179
+ if (typeof content === 'string') {
2180
+ // Simple content - uses default options
2181
+ type = content;
2182
+ plugin = ColumnControl.content[type];
2183
+ config = Object.assign({}, plugin === null || plugin === void 0 ? void 0 : plugin.defaults);
2184
+ }
2185
+ else if (Array.isArray(content)) {
2186
+ // An array is a shorthand for a dropdown with its default options
2187
+ type = 'dropdown';
2188
+ plugin = ColumnControl.content[type];
2189
+ config = Object.assign({}, plugin === null || plugin === void 0 ? void 0 : plugin.defaults, {
2190
+ content: content
2191
+ });
2192
+ }
2193
+ else if (content.extend) {
2194
+ // Content with custom options
2195
+ type = content.extend;
2196
+ plugin = ColumnControl.content[type];
2197
+ config = Object.assign({}, plugin === null || plugin === void 0 ? void 0 : plugin.defaults, content);
2198
+ }
2199
+ if (!plugin) {
2200
+ throw new Error('Unknown ColumnControl content type: ' + type);
2201
+ }
2202
+ // If the plugin is a wrapper around another type - e.g. the colVisDropdown
2203
+ if (plugin.extend) {
2204
+ var self_1 = plugin.extend.call(this, config);
2205
+ return this.resolve(self_1);
2206
+ }
2207
+ return {
2208
+ config: config,
2209
+ type: type,
2210
+ plugin: plugin
2211
+ };
2212
+ };
2213
+ /**
2214
+ * Get the unique id for the instance
2215
+ *
2216
+ * @returns Instant unique id
2217
+ */
2218
+ ColumnControl.prototype.unique = function () {
2219
+ return this._s.unique;
2220
+ };
2221
+ /**
2222
+ * Resolve the configured target into a DOM element
2223
+ */
2224
+ ColumnControl.prototype._target = function () {
2225
+ var target = this._c.target;
2226
+ var column = this._dt.column(this._s.columnIdx);
2227
+ var node;
2228
+ var className = 'header';
2229
+ // Header row index
2230
+ if (typeof target === 'number') {
2231
+ node = column.header(target);
2232
+ }
2233
+ else {
2234
+ var parts = target.split(':');
2235
+ var isHeader = parts[0] === 'tfoot' ? false : true;
2236
+ var row = parts[1] ? parseInt(parts[1]) : 0;
2237
+ if (isHeader) {
2238
+ node = column.header(row);
2239
+ }
2240
+ else {
2241
+ node = column.footer(row);
2242
+ className = 'footer';
2243
+ }
2244
+ }
2245
+ return node.querySelector('div.dt-column-' + className);
2246
+ };
2247
+ // Classes for common UI
2248
+ ColumnControl.Button = Button;
2249
+ ColumnControl.CheckList = CheckList;
2250
+ ColumnControl.SearchInput = SearchInput;
2251
+ /** Content plugins */
2252
+ ColumnControl.content = contentTypes;
2253
+ /** Defaults for ColumnControl */
2254
+ ColumnControl.defaults = {
2255
+ className: '',
2256
+ content: null,
2257
+ target: 0,
2258
+ };
2259
+ /** SVG icons that can be used by the content plugins */
2260
+ ColumnControl.icons = icons;
2261
+ /** Version */
2262
+ ColumnControl.version = '0.9.2';
2263
+ return ColumnControl;
2264
+ }());
2265
+
2266
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2267
+ * DataTables API integration
2268
+ */
2269
+ DataTable.ColumnControl = ColumnControl;
2270
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2271
+ * DataTables listeners for initialisation
2272
+ */
2273
+ // Create header / footer rows that don't exist, but have been referenced in the ColumnControl
2274
+ // targets. This needs to be done _before_ the header / footer structure is detected.
2275
+ $(document).on('i18n.dt', function (e, settings) {
2276
+ if (e.namespace !== 'dt') {
2277
+ return;
2278
+ }
2279
+ var api = new DataTable.Api(settings);
2280
+ var thead = api.table().header();
2281
+ var tableInit = settings.oInit.columnControl;
2282
+ var defaultInit = ColumnControl.defaults;
2283
+ var baseTargets = [];
2284
+ var ackTargets = {};
2285
+ // Determine if there is only one header row initially. If there is, we might append more
2286
+ // after it. Mark the top row as the header row using `titleRow` in the DataTables configuration
2287
+ if (thead.querySelectorAll('tr').length <= 1 && settings.titleRow === null) {
2288
+ settings.titleRow = 0;
2289
+ }
2290
+ identifyTargets(baseTargets, tableInit);
2291
+ identifyTargets(baseTargets, defaultInit);
2292
+ api.columns().every(function (i) {
2293
+ var columnInit = this.init().columnControl;
2294
+ identifyTargets(baseTargets, columnInit);
2295
+ });
2296
+ for (var i = 0; i < baseTargets.length; i++) {
2297
+ assetTarget(ackTargets, baseTargets[i], api);
2298
+ }
2299
+ });
2300
+ // Initialisation of ColumnControl instances - has to be done _after_ the header / footer structure
2301
+ // is detected by DataTables.
2302
+ $(document).on('preInit.dt', function (e, settings) {
2303
+ if (e.namespace !== 'dt') {
2304
+ return;
2305
+ }
2306
+ var api = new DataTable.Api(settings);
2307
+ var tableInit = settings.oInit.columnControl;
2308
+ var defaultInit = ColumnControl.defaults;
2309
+ var baseTargets = [];
2310
+ identifyTargets(baseTargets, tableInit);
2311
+ identifyTargets(baseTargets, defaultInit);
2312
+ api.columns().every(function (i) {
2313
+ var columnInit = this.init().columnControl;
2314
+ var targets = identifyTargets(baseTargets.slice(), columnInit);
2315
+ for (var i_1 = 0; i_1 < targets.length; i_1++) {
2316
+ // Each of the column, table and defaults configuration can be an array of config
2317
+ // objects, an array of content, or a configuration object. There might be multiple
2318
+ // targets for each one, and they might not exist! Therefore this is more complex
2319
+ // than it might be desirable.
2320
+ var columnTargetInit = getOptionsForTarget(targets[i_1], columnInit);
2321
+ var tableTargetInit = getOptionsForTarget(targets[i_1], tableInit);
2322
+ var defaultTargetInit = getOptionsForTarget(targets[i_1], defaultInit);
2323
+ if (defaultTargetInit || tableTargetInit || columnTargetInit) {
2324
+ new ColumnControl(api, this.index(), Object.assign({}, defaultTargetInit || {}, tableTargetInit || {}, columnTargetInit || {}));
2325
+ }
2326
+ }
2327
+ });
2328
+ });
2329
+ DataTable.Api.registerPlural('columns().ccSearchClear()', 'column().ccSearchClear()', function () {
2330
+ var ctx = this;
2331
+ return this.iterator('column', function (settings, idx) {
2332
+ ctx.trigger('cc-search-clear', [idx]);
2333
+ });
2334
+ });
2335
+ DataTable.ext.buttons.ccSearchClear = {
2336
+ text: 'Clear search',
2337
+ init: function (dt, node, config) {
2338
+ var _this = this;
2339
+ dt.on('draw', function () {
2340
+ var enabled = false;
2341
+ var glob = !!dt.search();
2342
+ // No point in wasting clock cycles if we already know it will be enabled
2343
+ if (!glob) {
2344
+ dt.columns().every(function () {
2345
+ if (this.search.fixed('dtcc') || this.search.fixed('dtcc-list')) {
2346
+ enabled = true;
2347
+ }
2348
+ });
2349
+ }
2350
+ _this.enable(glob || enabled);
2351
+ });
2352
+ this.enable(false);
2353
+ },
2354
+ action: function (e, dt, node, config) {
2355
+ dt.search('');
2356
+ dt.columns().ccSearchClear();
2357
+ }
2358
+ };
2359
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2360
+ * Initialisation support - this is more involved than normal as targets might
2361
+ * need to be created, and also options needs to be resolved into a standard
2362
+ * ColumnControl configuration object, from the various forms allowed in the
2363
+ * DataTables configuration.
2364
+ */
2365
+ /**
2366
+ * Given a ColumnControl target, make sure that it exists. If not, create it.
2367
+ *
2368
+ * @param ackTargets Cache for list of targets that have already been found or created
2369
+ * @param target Current target
2370
+ * @param dt DataTable API
2371
+ * @returns Void
2372
+ */
2373
+ function assetTarget(ackTargets, target, dt) {
2374
+ // Check if we already know about the target - if so, we know that it must already be in place
2375
+ if (ackTargets[target]) {
2376
+ return;
2377
+ }
2378
+ var isHeader = true; // false for footer
2379
+ var row = 0;
2380
+ if (typeof target === 'number') {
2381
+ row = target;
2382
+ }
2383
+ else {
2384
+ var parts = target.split(':');
2385
+ if (parts[0] === 'tfoot') {
2386
+ isHeader = false;
2387
+ }
2388
+ if (parts[1]) {
2389
+ row = parseInt(parts[1]);
2390
+ }
2391
+ }
2392
+ // The header / footer have not yet had their structure read, so they aren't available via
2393
+ // the API. As such we need to do our own DOM tweaking
2394
+ var node = isHeader ? dt.table().header() : dt.table().footer();
2395
+ // If the node doesn't exist yet, we need to create it
2396
+ if (!node.querySelectorAll('tr')[row]) {
2397
+ var columns = dt.columns().count();
2398
+ var tr = createElement('tr');
2399
+ tr.setAttribute('data-dt-order', 'disable');
2400
+ for (var i = 0; i < columns; i++) {
2401
+ tr.appendChild(createElement('td'));
2402
+ }
2403
+ node.appendChild(tr);
2404
+ }
2405
+ ackTargets[target] = true;
2406
+ }
2407
+ /**
2408
+ * Given a target, get the config object for it from the parameter passed in
2409
+ *
2410
+ * @param target ColumnControl target
2411
+ * @param input The dev's configuration
2412
+ * @returns The resolved config object, if found
2413
+ */
2414
+ function getOptionsForTarget(target, input) {
2415
+ var defaultTarget = ColumnControl.defaults.target;
2416
+ var selfTarget;
2417
+ if (isIContentArray(input)) {
2418
+ // Top level content array - e.g. `columnControl: ['order']`
2419
+ if (defaultTarget === target) {
2420
+ return {
2421
+ target: defaultTarget,
2422
+ content: input
2423
+ };
2424
+ }
2425
+ }
2426
+ else if (Array.isArray(input)) {
2427
+ // Top level array, some items of which will be configuration objects (possibly not all)
2428
+ for (var i = 0; i < input.length; i++) {
2429
+ var item = input[i];
2430
+ if (isIContentArray(item)) {
2431
+ // A content array, e.g. the inner array from: `columnControl: [['order']]
2432
+ if (defaultTarget === target) {
2433
+ return {
2434
+ target: defaultTarget,
2435
+ content: item
2436
+ };
2437
+ }
2438
+ }
2439
+ else if (isIConfig(item)) {
2440
+ // A config object, e.g. the object from: `columnControl: [{content: []}]`
2441
+ selfTarget = item.target !== undefined ? item.target : defaultTarget;
2442
+ if (target === selfTarget) {
2443
+ return item;
2444
+ }
2445
+ }
2446
+ else {
2447
+ // A content object
2448
+ if (target === defaultTarget) {
2449
+ return {
2450
+ target: defaultTarget,
2451
+ content: input
2452
+ };
2453
+ }
2454
+ }
2455
+ }
2456
+ }
2457
+ else if (typeof input === 'object') {
2458
+ // An object can be either a config object, or an extending content object
2459
+ if (isIConfig(input)) {
2460
+ // Config object: columnControl: {content: []}
2461
+ selfTarget = input.target !== undefined ? input.target : defaultTarget;
2462
+ if (target === selfTarget) {
2463
+ return input;
2464
+ }
2465
+ }
2466
+ else {
2467
+ // content object: columnControl: [{extend: 'order'}]
2468
+ if (target === defaultTarget) {
2469
+ return {
2470
+ target: defaultTarget,
2471
+ content: input
2472
+ };
2473
+ }
2474
+ }
2475
+ }
2476
+ }
2477
+ /**
2478
+ * Get a list of all targets from the configuration objects / arrays
2479
+ *
2480
+ * @param targets Established list of targets - mutated
2481
+ * @param input Configuration object / array
2482
+ * @returns Updated array
2483
+ */
2484
+ function identifyTargets(targets, input) {
2485
+ function add(target) {
2486
+ if (!targets.includes(target)) {
2487
+ targets.push(target);
2488
+ }
2489
+ }
2490
+ if (Array.isArray(input)) {
2491
+ // Array of options, or an array of content
2492
+ input.forEach(function (item) {
2493
+ add(typeof item === 'object' && item.target !== undefined
2494
+ ? item.target
2495
+ : ColumnControl.defaults.target);
2496
+ });
2497
+ }
2498
+ else if (typeof input === 'object') {
2499
+ // Full options defined: { target: x, content: [] }
2500
+ add(input.target !== undefined ? input.target : ColumnControl.defaults.target);
2501
+ }
2502
+ return targets;
2503
+ }
2504
+ /**
2505
+ * Check if an item is a configuration object or not
2506
+ *
2507
+ * @param item Item to check
2508
+ * @returns true if it is a config object
2509
+ */
2510
+ function isIConfig(item) {
2511
+ return typeof item === 'object' && item.target !== undefined
2512
+ ? true
2513
+ : false;
2514
+ }
2515
+ /**
2516
+ * Determine if an array contains only content items or not
2517
+ *
2518
+ * @param arr Array to check
2519
+ * @returns true if is content only, false if not (i.e. is an array with configuration objects).
2520
+ */
2521
+ function isIContentArray(arr) {
2522
+ var detectedConfig = false;
2523
+ if (!Array.isArray(arr)) {
2524
+ return false;
2525
+ }
2526
+ for (var i = 0; i < arr.length; i++) {
2527
+ if (isIConfig(arr[i])) {
2528
+ detectedConfig = true;
2529
+ break;
2530
+ }
2531
+ }
2532
+ return !detectedConfig;
2533
+ }
2534
+
2535
+
2536
+ return DataTable;
2537
+ }));