@danielgindi/dgtable.js 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4365 @@
1
+ /*!
2
+ * @danielgindi/dgtable.js 2.0.0
3
+ * git://github.com/danielgindi/dgtable.js.git
4
+ */
5
+ (function (global, factory) {
6
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@danielgindi/dom-utils/lib/ScrollHelper.js'), require('@danielgindi/dom-utils/lib/Css.js'), require('@danielgindi/dom-utils/lib/DomCompat.js'), require('@danielgindi/virtual-list-helper'), require('@danielgindi/dom-utils/lib/DomEventsSink.js')) :
7
+ typeof define === 'function' && define.amd ? define(['@danielgindi/dom-utils/lib/ScrollHelper.js', '@danielgindi/dom-utils/lib/Css.js', '@danielgindi/dom-utils/lib/DomCompat.js', '@danielgindi/virtual-list-helper', '@danielgindi/dom-utils/lib/DomEventsSink.js'], factory) :
8
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.DGTable = factory(global.domUtilsScrollHelper, global.domUtilsCss, global.domUtilsDomCompat, global.VirtualListHelper, global.domUtilsDomEventsSink));
9
+ })(this, (function (ScrollHelper_js, Css_js, DomCompat_js, VirtualListHelper, DomEventsSink) { 'use strict';
10
+
11
+ const find = function find(array, predicate) {
12
+ for (let i = 0, len = array.length; i >= 0 && i < len; i += 1) {
13
+ if (predicate(array[i], i, array))
14
+ return array[i];
15
+ }
16
+ };
17
+
18
+ const htmlEncode = function htmlEncode(text) {
19
+ return text.replace(/&/g, "&amp;").
20
+ replace(/</g, "&lt;").
21
+ replace(/>/g, "&gt;").
22
+ replace(/'/g, "&#39;").
23
+ replace(/"/g, "&quot;").
24
+ replace(/\n/g, '<br />');
25
+ };
26
+
27
+ // Define class RowCollection
28
+ function RowCollection() {
29
+
30
+ // Instantiate an Array. Seems like the `.length = ` of an inherited Array does not work well.
31
+ // I will not use the IFRAME solution either in fear of memory leaks, and we're supporting large datasets...
32
+ let collection = [];
33
+
34
+ // Synthetically set the 'prototype'
35
+ Object.assign(collection, RowCollection.prototype);
36
+
37
+ // Call initializer
38
+ collection.initialize.apply(collection, arguments);
39
+
40
+ return collection;
41
+ }
42
+
43
+ // Inherit Array
44
+ RowCollection.prototype = [];
45
+
46
+ RowCollection.prototype.initialize = function (options) {
47
+
48
+ options = options || {};
49
+
50
+ /** @field {string} sortColumn */
51
+ this.sortColumn = options.sortColumn == null ? [] : options.sortColumn;
52
+ };
53
+
54
+ /**
55
+ * @param {Object|Object[]} rows - row or array of rows to add to this collection
56
+ * @param {number?} at - position to insert rows at
57
+ */
58
+ RowCollection.prototype.add = function (rows, at) {
59
+ let isArray = 'splice' in rows && 'length' in rows,i,len;
60
+ if (isArray) {
61
+ if (typeof at === 'number') {
62
+ for (i = 0, len = rows.length; i < len; i++) {
63
+ this.splice(at++, 0, rows[i]);
64
+ }
65
+ } else {
66
+ for (i = 0, len = rows.length; i < len; i++) {
67
+ this.push(rows[i]);
68
+ }
69
+ }
70
+ } else {
71
+ if (typeof at === 'number') {
72
+ this.splice(at, 0, rows);
73
+ } else {
74
+ this.push(rows);
75
+ }
76
+ }
77
+ };
78
+
79
+ /**
80
+ * @param {Object|Object[]=} rows Row or array of rows to add to this collection
81
+ */
82
+ RowCollection.prototype.reset = function (rows) {
83
+ this.length = 0;
84
+ if (rows) {
85
+ this.add(rows);
86
+ }
87
+ };
88
+
89
+ /**
90
+ * @param {Function} filterFunc - Filtering function
91
+ * @param {Object|null?} args - Options to pass to the function
92
+ * @returns {RowCollection} success result
93
+ */
94
+ RowCollection.prototype.filteredCollection = function (filterFunc, args) {
95
+ if (filterFunc && args) {
96
+ let rows = new RowCollection({
97
+ sortColumn: this.sortColumn,
98
+ onComparatorRequired: this.onComparatorRequired,
99
+ customSortingProvider: this.customSortingProvider
100
+ });
101
+
102
+ for (let i = 0, len = this.length, row; i < len; i++) {
103
+ row = this[i];
104
+ if (filterFunc(row, args)) {
105
+ row['__i'] = i;
106
+ rows.push(row);
107
+ }
108
+ }
109
+ return rows;
110
+ } else {
111
+ return null;
112
+ }
113
+ };
114
+
115
+ /**
116
+ * @type {function(columnName: string, descending: boolean, defaultComparator: function(a,b):number)|null|undefined}
117
+ */
118
+ RowCollection.prototype.onComparatorRequired = null;
119
+ /**
120
+ * @type {function(data: any[], sort: function(any[]):any[]):any[]|null|undefined}
121
+ */
122
+ RowCollection.prototype.customSortingProvider = null;
123
+
124
+ let nativeSort = RowCollection.prototype.sort;
125
+
126
+ function getDefaultComparator(column, descending) {
127
+ let columnName = column.column;
128
+ let comparePath = column.comparePath || columnName;
129
+ if (typeof comparePath === 'string') {
130
+ comparePath = comparePath.split('.');
131
+ }
132
+ let pathLength = comparePath.length,
133
+ hasPath = pathLength > 1,
134
+ i;
135
+
136
+ let lessVal = descending ? 1 : -1,moreVal = descending ? -1 : 1;
137
+ return function (leftRow, rightRow) {
138
+ let leftVal = leftRow[comparePath[0]],
139
+ rightVal = rightRow[comparePath[0]];
140
+ if (hasPath) {
141
+ for (i = 1; i < pathLength; i++) {
142
+ leftVal = leftVal && leftVal[comparePath[i]];
143
+ rightVal = rightVal && rightVal[comparePath[i]];
144
+ }
145
+ }
146
+ if (leftVal === rightVal) return 0;
147
+ if (leftVal == null) return lessVal;
148
+ if (rightVal == null) return moreVal;
149
+ if (leftVal < rightVal) return lessVal;
150
+ return moreVal;
151
+ };
152
+ }
153
+
154
+ /**
155
+ * @returns {Function|undefined} the comparator that was used
156
+ */
157
+ RowCollection.prototype.sort = function () {
158
+ let comparator;
159
+
160
+ if (this.sortColumn.length) {
161
+ let comparators = [];
162
+
163
+ for (let i = 0; i < this.sortColumn.length; i++) {
164
+ comparator = null;
165
+ const defaultComparator = getDefaultComparator(this.sortColumn[i], this.sortColumn[i].descending);
166
+ if (this.onComparatorRequired) {
167
+ comparator = this.onComparatorRequired(this.sortColumn[i].column, this.sortColumn[i].descending, defaultComparator);
168
+ }
169
+ if (!comparator) {
170
+ comparator = defaultComparator;
171
+ }
172
+ comparators.push(comparator.bind(this));
173
+ }
174
+
175
+ if (comparators.length === 1) {
176
+ comparator = comparators[0];
177
+ } else {
178
+ let len = comparators.length,
179
+ value;
180
+
181
+ comparator = function (leftRow, rightRow) {
182
+ for (let i = 0; i < len; i++) {
183
+ value = comparators[i](leftRow, rightRow);
184
+ if (value !== 0) {
185
+ return value;
186
+ }
187
+ }
188
+ return value;
189
+ };
190
+ }
191
+
192
+ const sorter = (data) => nativeSort.call(data, comparator);
193
+
194
+ if (this.customSortingProvider) {
195
+ let results = this.customSortingProvider(this, sorter);
196
+ if (results !== this) {
197
+ this.splice(0, this.length, ...results);
198
+ }
199
+ } else {
200
+ sorter(this);
201
+ }
202
+ }
203
+
204
+ return comparator;
205
+ };
206
+
207
+ // Define class RowCollection
208
+ function ColumnCollection() {
209
+
210
+ // Instantiate an Array. Seems like the `.length = ` of an inherited Array does not work well.
211
+ // I will not use the IFRAME solution either in fear of memory leaks, and we're supporting large datasets...
212
+ let collection = [];
213
+
214
+ // Synthetically set the 'prototype'
215
+ Object.assign(collection, ColumnCollection.prototype);
216
+
217
+ // Call initializer
218
+ collection.initialize.apply(collection, arguments);
219
+
220
+ return collection;
221
+ }
222
+
223
+ // Inherit Array
224
+ ColumnCollection.prototype = [];
225
+
226
+ ColumnCollection.prototype.initialize = function () {
227
+
228
+ };
229
+
230
+ /**
231
+ * Get the column by this name
232
+ * @param {string} column column name
233
+ * @returns {Object} the column object
234
+ */
235
+ ColumnCollection.prototype.get = function (column) {
236
+ for (let i = 0, len = this.length; i < len; i++) {
237
+ if (this[i].name === column) {
238
+ return this[i];
239
+ }
240
+ }
241
+ return null;
242
+ };
243
+
244
+ /**
245
+ * Get the index of the column by this name
246
+ * @param {string} column column name
247
+ * @returns {int} the index of this column
248
+ */
249
+ ColumnCollection.prototype.indexOf = function (column) {
250
+ for (let i = 0, len = this.length; i < len; i++) {
251
+ if (this[i].name === column) {
252
+ return i;
253
+ }
254
+ }
255
+ return -1;
256
+ };
257
+
258
+ /**
259
+ * Get the column by the specified order
260
+ * @param {number} order the column's order
261
+ * @returns {Object} the column object
262
+ */
263
+ ColumnCollection.prototype.getByOrder = function (order) {
264
+ for (let i = 0, len = this.length; i < len; i++) {
265
+ if (this[i].order === order) {
266
+ return this[i];
267
+ }
268
+ }
269
+ return null;
270
+ };
271
+
272
+ /**
273
+ * Normalize order
274
+ * @returns {ColumnCollection} self
275
+ */
276
+ ColumnCollection.prototype.normalizeOrder = function () {
277
+ let ordered = [],i;
278
+ for (i = 0; i < this.length; i++) {
279
+ ordered.push(this[i]);
280
+ }
281
+ ordered.sort(function (col1, col2) {return col1.order < col2.order ? -1 : col1.order > col2.order ? 1 : 0;});
282
+ for (i = 0; i < ordered.length; i++) {
283
+ ordered[i].order = i;
284
+ }
285
+ return this;
286
+ };
287
+
288
+ /**
289
+ * Get the array of columns, order by the order property
290
+ * @returns {Array<Object>} ordered array of columns
291
+ */
292
+ ColumnCollection.prototype.getColumns = function () {
293
+ let cols = [];
294
+ for (let i = 0, column; i < this.length; i++) {
295
+ column = this[i];
296
+ cols.push(column);
297
+ }
298
+ cols.sort((col1, col2) => col1.order < col2.order ? -1 : col1.order > col2.order ? 1 : 0);
299
+ return cols;
300
+ };
301
+
302
+ /**
303
+ * Get the array of visible columns, order by the order property
304
+ * @returns {Array<Object>} ordered array of visible columns
305
+ */
306
+ ColumnCollection.prototype.getVisibleColumns = function () {
307
+ let cols = [];
308
+ for (let i = 0, column; i < this.length; i++) {
309
+ column = this[i];
310
+ if (column.visible) {
311
+ cols.push(column);
312
+ }
313
+ }
314
+ cols.sort((col1, col2) => col1.order < col2.order ? -1 : col1.order > col2.order ? 1 : 0);
315
+ return cols;
316
+ };
317
+
318
+ /**
319
+ * @returns {int} maximum order currently in the array
320
+ */
321
+ ColumnCollection.prototype.getMaxOrder = function () {
322
+ let order = 0;
323
+ for (let i = 0, column; i < this.length; i++) {
324
+ column = this[i];
325
+ if (column.order > order) {
326
+ order = column.order;
327
+ }
328
+ }
329
+ return order;
330
+ };
331
+
332
+ /**
333
+ * Move a column to a new spot in the collection
334
+ * @param {Object} src the column to move
335
+ * @param {Object} dest the destination column
336
+ * @returns {ColumnCollection} self
337
+ */
338
+ ColumnCollection.prototype.moveColumn = function (src, dest) {
339
+ if (src && dest) {
340
+ let srcOrder = src.order,destOrder = dest.order,i,col;
341
+ if (srcOrder < destOrder) {
342
+ for (i = srcOrder + 1; i <= destOrder; i++) {
343
+ col = this.getByOrder(i);
344
+ col.order--;
345
+ }
346
+ } else {
347
+ for (i = srcOrder - 1; i >= destOrder; i--) {
348
+ col = this.getByOrder(i);
349
+ col.order++;
350
+ }
351
+ }
352
+ src.order = destOrder;
353
+ }
354
+ return this;
355
+ };
356
+
357
+ /* eslint-env browser */
358
+
359
+
360
+ // saveSelection/restoreSelection courtesy of Tim Down, with my improvements
361
+ // https://stackoverflow.com/questions/13949059/persisting-the-changes-of-range-objects-after-selection-in-html/13950376#13950376
362
+
363
+ function isChildOf(child, parent) {
364
+ while ((child = child.parentNode) && child !== parent);
365
+ return !!child;
366
+ }
367
+
368
+ class SelectionHelper {
369
+
370
+ static saveSelection(el) {
371
+ let range = window.getSelection().getRangeAt(0);
372
+
373
+ if (el !== range.commonAncestorContainer && !isChildOf(range.commonAncestorContainer, el))
374
+ return null;
375
+
376
+ let preSelectionRange = range.cloneRange();
377
+ preSelectionRange.selectNodeContents(el);
378
+ preSelectionRange.setEnd(range.startContainer, range.startOffset);
379
+ let start = preSelectionRange.toString().length;
380
+
381
+ return {
382
+ start: start,
383
+ end: start + range.toString().length
384
+ };
385
+ }
386
+
387
+ static restoreSelection(el, savedSel) {
388
+ let charIndex = 0;
389
+ let nodeStack = [el],node,foundStart = false,stop = false;
390
+ let range = document.createRange();
391
+ range.setStart(el, 0);
392
+ range.collapse(true);
393
+
394
+ while (!stop && (node = nodeStack.pop())) {
395
+ if (node.nodeType === 3) {
396
+ let nextCharIndex = charIndex + node.length;
397
+ if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
398
+ range.setStart(node, savedSel.start - charIndex);
399
+ foundStart = true;
400
+ }
401
+ if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
402
+ range.setEnd(node, savedSel.end - charIndex);
403
+ stop = true;
404
+ }
405
+ charIndex = nextCharIndex;
406
+ } else {
407
+ let i = node.childNodes.length;
408
+ while (i--) {
409
+ nodeStack.push(node.childNodes[i]);
410
+ }
411
+ }
412
+ }
413
+
414
+ let sel = window.getSelection();
415
+ sel.removeAllRanges();
416
+ sel.addRange(range);
417
+ }
418
+ }
419
+
420
+ function ByColumnFilter(row, args) {
421
+
422
+ let column = args.column;
423
+ let keyword = args.keyword == null ? '' : args.keyword.toString();
424
+
425
+ if (!keyword || !column) return true;
426
+
427
+ let actualVal = row[column];
428
+ if (actualVal == null) {
429
+ return false;
430
+ }
431
+
432
+ actualVal = actualVal.toString();
433
+
434
+ if (!args.caseSensitive) {
435
+ actualVal = actualVal.toLowerCase();
436
+ keyword = keyword.toLowerCase();
437
+ }
438
+
439
+ return actualVal.indexOf(keyword) !== -1;
440
+ }
441
+
442
+ function mitt (n) {return { all: n = n || new Map(), on: function (t, e) {var i = n.get(t);i ? i.push(e) : n.set(t, [e]);}, off: function (t, e) {var i = n.get(t);i && (e ? i.splice(i.indexOf(e) >>> 0, 1) : n.set(t, []));}, emit: function (t, e) {var i = n.get(t);i && i.slice().map(function (n) {n(e);}), (i = n.get("*")) && i.slice().map(function (n) {n(t, e);});} };}
443
+
444
+ /* eslint-env browser */
445
+
446
+
447
+ const nativeIndexOf = Array.prototype.indexOf;
448
+
449
+ let createElement = document.createElement.bind(document);
450
+ const hasOwnProperty = Object.prototype.hasOwnProperty;
451
+
452
+ const IsSafeSymbol = Symbol('safe');
453
+ const HoverInEventSymbol = Symbol('hover_in');
454
+ const HoverOutEventSymbol = Symbol('hover_out');
455
+ const RowClickEventSymbol = Symbol('row_click');
456
+ const PreviewCellSymbol = Symbol('preview_cell');
457
+ const OriginalCellSymbol = Symbol('cell');
458
+
459
+ function webkitRenderBugfix(el) {
460
+ // BUGFIX: WebKit has a bug where it does not relayout, and this affects us because scrollbars
461
+ // are still calculated even though they are not there yet. This is the last resort.
462
+ let oldDisplay = el.style.display;
463
+ el.style.display = 'none';
464
+ //noinspection BadExpressionStatementJS
465
+ el.offsetHeight; // No need to store this anywhere, the reference is enough
466
+ el.style.display = oldDisplay;
467
+ return el;
468
+ }
469
+
470
+ function relativizeElement(el) {
471
+ if (!['relative', 'absolute', 'fixed'].includes(getComputedStyle(el).position)) {
472
+ el.style.position = 'relative';
473
+ }
474
+ }
475
+
476
+ const isInputElementEvent = (event) => /^(?:INPUT|TEXTAREA|BUTTON|SELECT)$/.test(event.target.tagName);
477
+
478
+ // noinspection JSUnusedGlobalSymbols
479
+ class DGTable {
480
+ /**
481
+ * @param {DGTable.Options?} options - initialization options
482
+ */
483
+ constructor(options) {
484
+ this._init(options);
485
+
486
+ /**
487
+ * @public
488
+ * @expose
489
+ * @type {string}
490
+ */
491
+ this.VERSION = DGTable.VERSION;
492
+ }
493
+
494
+ /**
495
+ * @param {DGTable.Options?} options - initialization options
496
+ */
497
+ _init(options) {
498
+ options = options || {};
499
+
500
+ /**
501
+ * @private
502
+ * @type {DGTable.Options}
503
+ * */
504
+ let o = this._o = {};
505
+
506
+ /**
507
+ * @private
508
+ * This is for encapsulating private data */
509
+ let p = this._p = {
510
+ eventsSink: new DomEventsSink(),
511
+ mitt: mitt(),
512
+ /** @type {boolean} */
513
+ tableSkeletonNeedsRendering: true
514
+ };
515
+
516
+ /**
517
+ * @public
518
+ * @expose
519
+ * */
520
+ this.el = options.el && options.el instanceof Element ? options.el : document.createElement('div');
521
+
522
+ if (this.el !== options.el) {
523
+ this.el.classList.add(options.className || 'dgtable-wrapper');
524
+ }
525
+
526
+ p.eventsSink.add(this.el, 'dragend.colresize', this._onEndDragColumnHeader.bind(this));
527
+
528
+ /**
529
+ * @private
530
+ * @field {boolean} virtualTable */
531
+ o.virtualTable = options.virtualTable === undefined ? true : !!options.virtualTable;
532
+
533
+ /**
534
+ * @private
535
+ * @field {number} estimatedRowHeight */
536
+ o.estimatedRowHeight = options.estimatedRowHeight || undefined;
537
+
538
+ /**
539
+ * @private
540
+ * @field {number} rowsBufferSize */
541
+ o.rowsBufferSize = options.rowsBufferSize || 3;
542
+
543
+ /**
544
+ * @private
545
+ * @field {number} minColumnWidth */
546
+ o.minColumnWidth = Math.max(options.minColumnWidth || 35, 0);
547
+
548
+ /**
549
+ * @private
550
+ * @field {number} resizeAreaWidth */
551
+ o.resizeAreaWidth = options.resizeAreaWidth || 8;
552
+
553
+ /**
554
+ * @private
555
+ * @field {boolean} resizableColumns */
556
+ o.resizableColumns = options.resizableColumns === undefined ? true : !!options.resizableColumns;
557
+
558
+ /**
559
+ * @private
560
+ * @field {boolean} movableColumns */
561
+ o.movableColumns = options.movableColumns === undefined ? true : !!options.movableColumns;
562
+
563
+ /**
564
+ * @private
565
+ * @field {number} sortableColumns */
566
+ o.sortableColumns = options.sortableColumns === undefined ? 1 : parseInt(options.sortableColumns, 10) || 1;
567
+
568
+ /**
569
+ * @private
570
+ * @field {boolean} adjustColumnWidthForSortArrow */
571
+ o.adjustColumnWidthForSortArrow = options.adjustColumnWidthForSortArrow === undefined ? true : !!options.adjustColumnWidthForSortArrow;
572
+
573
+ /**
574
+ * @private
575
+ * @field {boolean} convertColumnWidthsToRelative */
576
+ o.convertColumnWidthsToRelative = options.convertColumnWidthsToRelative === undefined ? false : !!options.convertColumnWidthsToRelative;
577
+
578
+ /**
579
+ * @private
580
+ * @field {boolean} autoFillTableWidth */
581
+ o.autoFillTableWidth = options.autoFillTableWidth === undefined ? false : !!options.autoFillTableWidth;
582
+
583
+ /**
584
+ * @private
585
+ * @field {boolean} allowCancelSort */
586
+ o.allowCancelSort = options.allowCancelSort === undefined ? true : !!options.allowCancelSort;
587
+
588
+ /**
589
+ * @private
590
+ * @field {string} cellClasses */
591
+ o.cellClasses = options.cellClasses === undefined ? '' : options.cellClasses;
592
+
593
+ /**
594
+ * @private
595
+ * @field {string} resizerClassName */
596
+ o.resizerClassName = options.resizerClassName === undefined ? 'dgtable-resize' : options.resizerClassName;
597
+
598
+ /**
599
+ * @private
600
+ * @field {string} tableClassName */
601
+ o.tableClassName = options.tableClassName === undefined ? 'dgtable' : options.tableClassName;
602
+
603
+ /**
604
+ * @private
605
+ * @field {boolean} allowCellPreview */
606
+ o.allowCellPreview = options.allowCellPreview === undefined ? true : options.allowCellPreview;
607
+
608
+ /**
609
+ * @private
610
+ * @field {boolean} allowHeaderCellPreview */
611
+ o.allowHeaderCellPreview = options.allowHeaderCellPreview === undefined ? true : options.allowHeaderCellPreview;
612
+
613
+ /**
614
+ * @private
615
+ * @field {string} cellPreviewClassName */
616
+ o.cellPreviewClassName = options.cellPreviewClassName === undefined ? 'dgtable-cell-preview' : options.cellPreviewClassName;
617
+
618
+ /**
619
+ * @private
620
+ * @field {boolean} cellPreviewAutoBackground */
621
+ o.cellPreviewAutoBackground = options.cellPreviewAutoBackground === undefined ? true : options.cellPreviewAutoBackground;
622
+
623
+ /**
624
+ * @private
625
+ * @field {function(columnName: string, descending: boolean, defaultComparator: function(a,b):number):(function(a,b):number)} onComparatorRequired */
626
+ o.onComparatorRequired = options.onComparatorRequired === undefined ? null : options.onComparatorRequired;
627
+ if (!o.onComparatorRequired && typeof options['comparatorCallback'] === 'function') {
628
+ o.onComparatorRequired = options['comparatorCallback'];
629
+ }
630
+
631
+ o.customSortingProvider = options.customSortingProvider === undefined ? null : options.customSortingProvider;
632
+
633
+ /**
634
+ * @private
635
+ * @field {boolean} width */
636
+ o.width = options.width === undefined ? DGTable.Width.NONE : options.width;
637
+
638
+ /**
639
+ * @private
640
+ * @field {boolean} relativeWidthGrowsToFillWidth */
641
+ o.relativeWidthGrowsToFillWidth = options.relativeWidthGrowsToFillWidth === undefined ? true : !!options.relativeWidthGrowsToFillWidth;
642
+
643
+ /**
644
+ * @private
645
+ * @field {boolean} relativeWidthShrinksToFillWidth */
646
+ o.relativeWidthShrinksToFillWidth = options.relativeWidthShrinksToFillWidth === undefined ? false : !!options.relativeWidthShrinksToFillWidth;
647
+
648
+ this.setCellFormatter(options.cellFormatter);
649
+ this.setHeaderCellFormatter(options.headerCellFormatter);
650
+ this.setFilter(options.filter);
651
+
652
+ /** @private
653
+ * @field {number} height */
654
+ o.height = options.height;
655
+
656
+ // Prepare columns
657
+ this.setColumns(options.columns || [], false);
658
+
659
+ // Set sorting columns
660
+ let sortColumns = [];
661
+
662
+ if (options.sortColumn) {
663
+
664
+ let tmpSortColumns = options.sortColumn;
665
+
666
+ if (tmpSortColumns && !Array.isArray(tmpSortColumns)) {
667
+ tmpSortColumns = [tmpSortColumns];
668
+ }
669
+
670
+ if (tmpSortColumns) {
671
+ for (let i = 0, len = tmpSortColumns.length; i < len; i++) {
672
+ let sortColumn = tmpSortColumns[i];
673
+ if (typeof sortColumn === 'string') {
674
+ sortColumn = { column: sortColumn, descending: false };
675
+ }
676
+ let col = p.columns.get(sortColumn.column);
677
+ if (!col) continue;
678
+
679
+ sortColumns.push({
680
+ column: sortColumn.column,
681
+ comparePath: col.comparePath || col.dataPath,
682
+ descending: sortColumn.descending
683
+ });
684
+ }
685
+ }
686
+ }
687
+
688
+ /** @field {RowCollection} _rows */
689
+ p.rows = new RowCollection({ sortColumn: sortColumns });
690
+ p.rows.onComparatorRequired = (column, descending, defaultComparator) => {
691
+ if (o.onComparatorRequired) {
692
+ return o.onComparatorRequired(column, descending, defaultComparator);
693
+ }
694
+ };
695
+ p.rows.customSortingProvider = (data, sort) => {
696
+ if (o.customSortingProvider) {
697
+ return o.customSortingProvider(data, sort);
698
+ } else {
699
+ return sort(data);
700
+ }
701
+ };
702
+
703
+ /** @private
704
+ * @field {RowCollection} _filteredRows */
705
+ p.filteredRows = null;
706
+
707
+ p.scrollbarWidth = 0;
708
+ p.lastVirtualScrollHeight = 0;
709
+
710
+ this._setupHovers();
711
+ }
712
+
713
+ _setupHovers() {
714
+ const p = this._p;
715
+
716
+ /*
717
+ Setup hover mechanism.
718
+ We need this to be high performance, as there may be MANY cells to call this on, on creation and destruction.
719
+ */
720
+
721
+ /**
722
+ * @param {MouseEvent} event
723
+ * @this {HTMLElement}
724
+ * */
725
+ let hoverMouseOverHandler = (event) => {
726
+ let cell = event.currentTarget;
727
+ let target = event.relatedTarget;
728
+ if (target === cell || cell.contains(target))
729
+ return;
730
+ if (cell[PreviewCellSymbol] && (
731
+ target === cell[PreviewCellSymbol] || cell[PreviewCellSymbol].contains(target)))
732
+ return;
733
+ this._cellMouseOverEvent(cell);
734
+ };
735
+
736
+ /**
737
+ * @param {MouseEvent} event
738
+ * @this {HTMLElement}
739
+ * */
740
+ let hoverMouseOutHandler = (event) => {
741
+ let cell = event.currentTarget[OriginalCellSymbol] || event.currentTarget;
742
+ let target = event.relatedTarget;
743
+ if (target === this || cell.contains(target))
744
+ return;
745
+ if (cell[PreviewCellSymbol] && (
746
+ target === cell[PreviewCellSymbol] || cell[PreviewCellSymbol].contains(target)))
747
+ return;
748
+ this._cellMouseOutEvent(cell);
749
+ };
750
+
751
+ /**
752
+ * @param {HTMLElement} el cell or header-cell
753
+ * */
754
+ p._bindCellHoverIn = (el) => {
755
+ if (!el[HoverInEventSymbol]) {
756
+ el.addEventListener('mouseover', el[HoverInEventSymbol] = hoverMouseOverHandler);
757
+ }
758
+ };
759
+
760
+ /**
761
+ * @param {HTMLElement} el cell or header-cell
762
+ * */
763
+ p._unbindCellHoverIn = (el) => {
764
+ if (el[HoverInEventSymbol]) {
765
+ el.removeEventListener('mouseover', el[HoverInEventSymbol]);
766
+ el[HoverInEventSymbol] = null;
767
+ }
768
+ };
769
+
770
+ /**
771
+ * @param {HTMLElement} el cell or header-cell
772
+ * @returns {DGTable} self
773
+ * */
774
+ p._bindCellHoverOut = (el) => {
775
+ if (!el[HoverOutEventSymbol]) {
776
+ el.addEventListener('mouseout', el[HoverOutEventSymbol] = hoverMouseOutHandler);
777
+ }
778
+ };
779
+
780
+ /**
781
+ * @param {HTMLElement} el cell or header-cell
782
+ * @returns {DGTable} self
783
+ * */
784
+ p._unbindCellHoverOut = (el) => {
785
+ if (el[HoverOutEventSymbol]) {
786
+ el.removeEventListener('mouseout', el[HoverOutEventSymbol]);
787
+ el[HoverOutEventSymbol] = null;
788
+ }
789
+ };
790
+ }
791
+
792
+ _setupVirtualTable() {
793
+ const p = this._p,o = this._o;
794
+
795
+ const tableClassName = o.tableClassName,
796
+ rowClassName = tableClassName + '-row',
797
+ altRowClassName = tableClassName + '-row-alt',
798
+ cellClassName = tableClassName + '-cell';
799
+
800
+ let visibleColumns = p.visibleColumns,
801
+ colCount = visibleColumns.length;
802
+
803
+ p.notifyRendererOfColumnsConfig = () => {
804
+ visibleColumns = p.visibleColumns;
805
+ colCount = visibleColumns.length;
806
+
807
+ for (let colIndex = 0, column; colIndex < colCount; colIndex++) {
808
+ column = visibleColumns[colIndex];
809
+ column._finalWidth = column.actualWidthConsideringScrollbarWidth || column.actualWidth;
810
+ }
811
+ };
812
+
813
+ p.virtualListHelper = new VirtualListHelper({
814
+ list: p.table,
815
+ itemsParent: p.tbody,
816
+ autoVirtualWrapperWidth: false,
817
+ virtual: o.virtualTable,
818
+ buffer: o.rowsBufferSize,
819
+ estimatedItemHeight: o.estimatedRowHeight ? o.estimatedRowHeight : p.virtualRowHeight || 40,
820
+ itemElementCreatorFn: () => {
821
+ return createElement('div');
822
+ },
823
+ onItemRender: (row, virtualIndex) => {
824
+ const rows = p.filteredRows || p.rows,
825
+ isDataFiltered = !!p.filteredRows,
826
+ allowCellPreview = o.allowCellPreview;
827
+
828
+ row.className = rowClassName;
829
+ if (virtualIndex % 2 === 1)
830
+ row.className += ' ' + altRowClassName;
831
+
832
+ let rowData = rows[virtualIndex];
833
+ let rowIndex = isDataFiltered ? rowData['__i'] : virtualIndex;
834
+
835
+ row['vIndex'] = virtualIndex;
836
+ row['index'] = rowIndex;
837
+
838
+ for (let colIndex = 0; colIndex < colCount; colIndex++) {
839
+ let column = visibleColumns[colIndex];
840
+ let cell = createElement('div');
841
+ cell['columnName'] = column.name;
842
+ cell.setAttribute('data-column', column.name);
843
+ cell.className = cellClassName;
844
+ cell.style.width = column._finalWidth + 'px';
845
+ if (column.cellClasses) cell.className += ' ' + column.cellClasses;
846
+ if (allowCellPreview) {
847
+ p._bindCellHoverIn(cell);
848
+ }
849
+
850
+ let cellInner = cell.appendChild(createElement('div'));
851
+ cellInner.innerHTML = this._getHtmlForCell(rowData, column);
852
+
853
+ row.appendChild(cell);
854
+ }
855
+
856
+ row.addEventListener('click', row[RowClickEventSymbol] = (event) => {
857
+ this.emit('rowclick', {
858
+ event: event,
859
+ filteredRowIndex: virtualIndex,
860
+ rowIndex: rowIndex,
861
+ rowEl: row,
862
+ rowData: rowData
863
+ });
864
+ });
865
+
866
+ this.emit('rowcreate', {
867
+ filteredRowIndex: virtualIndex,
868
+ rowIndex: rowIndex,
869
+ rowEl: row,
870
+ rowData: rowData
871
+ });
872
+ },
873
+
874
+ onItemUnrender: (row) => {
875
+ if (row[RowClickEventSymbol]) {
876
+ row.removeEventListener('click', row[RowClickEventSymbol]);
877
+ }
878
+
879
+ this._unbindCellEventsForRow(row);
880
+
881
+ this.emit('rowdestroy', row);
882
+ },
883
+
884
+ onScrollHeightChange: (height) => {
885
+ // only recalculate scrollbar width if height increased. we reset it in other situations.
886
+ if (height > p._lastVirtualScrollHeight && !p.scrollbarWidth) {
887
+ this._updateLastCellWidthFromScrollbar();
888
+ }
889
+
890
+ p._lastVirtualScrollHeight = height;
891
+ }
892
+ });
893
+
894
+ p.virtualListHelper.setCount((p.filteredRows ?? p.rows).length);
895
+
896
+ p.notifyRendererOfColumnsConfig();
897
+ }
898
+
899
+ trigger(eventName) {
900
+ const p = this._p;
901
+ if (!p) return;
902
+
903
+ let events = p.events;
904
+
905
+ if (hasOwnProperty.call(events, eventName)) {
906
+ let callbacks = events[eventName];
907
+ for (let i = 0; i < callbacks.length; i++) {
908
+ let item = callbacks[i];
909
+ if (item.once) {
910
+ callbacks.splice(i--, 1);
911
+ }
912
+ item.cb.apply(this, Array.prototype.slice.call(arguments, 1));
913
+ }
914
+ }
915
+
916
+ return this;
917
+ }
918
+
919
+ /**
920
+ * Register an event handler
921
+ * @param {(string|'*')?} event
922
+ * @param {function(any)} handler
923
+ * @returns {DGTable}
924
+ */
925
+ on(/**string|'*'*/event, /**Function?*/handler) {
926
+ this._p.mitt.on(event, handler);
927
+ return this;
928
+ }
929
+
930
+ /**
931
+ * Register a one time event handler
932
+ * @param {(string|'*')?} event
933
+ * @param {function(any)} handler
934
+ * @returns {DGTable}
935
+ */
936
+ once(/**string|'*'*/event, /**Function?*/handler) {
937
+ let wrapped = (value) => {
938
+ this._p.mitt.off(event, wrapped);
939
+ handler(value);
940
+ };
941
+ this._p.mitt.on(event, wrapped);
942
+ return this;
943
+ }
944
+
945
+ /**
946
+ * Remove an `handler` for `event`, all events for `event`, or all events completely.
947
+ * @param {(string|'*')?} event
948
+ * @param {function(any)} handler
949
+ * @returns {DGTable}
950
+ */
951
+ off(/**(string|'*')?*/event, /**Function?*/handler) {
952
+ if (!event && !event) {
953
+ this._p.mitt.all.clear();
954
+ } else {
955
+ this._p.mitt.off(event, handler);
956
+ }
957
+ return this;
958
+ }
959
+
960
+ /**
961
+ * Emit an event
962
+ * @param {string} event
963
+ * @param {any?} value
964
+ * @returns {DGTable}
965
+ */
966
+ emit(/**string|'*'*/event, /**any?*/value) {
967
+ this._p.mitt.emit(event, value);
968
+ return this;
969
+ }
970
+
971
+ /**
972
+ * Detect column width mode
973
+ * @private
974
+ * @param {Number|string} width
975
+ * @param {number} minWidth
976
+ * @returns {Object} parsed width
977
+ */
978
+ _parseColumnWidth(width, minWidth) {
979
+
980
+ let widthSize = Math.max(0, parseFloat(width)),
981
+ widthMode = ColumnWidthMode.AUTO; // Default
982
+
983
+ if (widthSize > 0) {
984
+ // Well, it's sure is not AUTO, as we have a value
985
+
986
+ if (width === widthSize + '%') {
987
+ // It's a percentage!
988
+
989
+ widthMode = ColumnWidthMode.RELATIVE;
990
+ widthSize /= 100;
991
+ } else if (widthSize > 0 && widthSize < 1) {
992
+ // It's a decimal value, as a relative value!
993
+
994
+ widthMode = ColumnWidthMode.RELATIVE;
995
+ } else {
996
+ // It's an absolute size!
997
+
998
+ if (widthSize < minWidth) {
999
+ widthSize = minWidth;
1000
+ }
1001
+ widthMode = ColumnWidthMode.ABSOLUTE;
1002
+ }
1003
+ }
1004
+
1005
+ return { width: widthSize, mode: widthMode };
1006
+ }
1007
+
1008
+ /**
1009
+ * @private
1010
+ * @param {COLUMN_OPTIONS} columnData
1011
+ */
1012
+ _initColumnFromData(columnData) {
1013
+
1014
+ let parsedWidth = this._parseColumnWidth(columnData.width, columnData.ignoreMin ? 0 : this._o.minColumnWidth);
1015
+
1016
+ let col = {
1017
+ name: columnData.name,
1018
+ label: columnData.label === undefined ? columnData.name : columnData.label,
1019
+ width: parsedWidth.width,
1020
+ widthMode: parsedWidth.mode,
1021
+ resizable: columnData.resizable === undefined ? true : columnData.resizable,
1022
+ sortable: columnData.sortable === undefined ? true : columnData.sortable,
1023
+ movable: columnData.movable === undefined ? true : columnData.movable,
1024
+ visible: columnData.visible === undefined ? true : columnData.visible,
1025
+ cellClasses: columnData.cellClasses === undefined ? this._o.cellClasses : columnData.cellClasses,
1026
+ ignoreMin: columnData.ignoreMin === undefined ? false : !!columnData.ignoreMin
1027
+ };
1028
+
1029
+ col.dataPath = columnData.dataPath === undefined ? col.name : columnData.dataPath;
1030
+ col.comparePath = columnData.comparePath === undefined ? col.dataPath : columnData.comparePath;
1031
+
1032
+ if (typeof col.dataPath === 'string') {
1033
+ col.dataPath = col.dataPath.split('.');
1034
+ }
1035
+ if (typeof col.comparePath === 'string') {
1036
+ col.comparePath = col.comparePath.split('.');
1037
+ }
1038
+
1039
+ return col;
1040
+ }
1041
+
1042
+ /**
1043
+ * Destroy, releasing all memory, events and DOM elements
1044
+ * @public
1045
+ * @expose
1046
+ */
1047
+ destroy() {
1048
+ let p = this._p || {},
1049
+ el = this.el;
1050
+
1051
+ if (this.__removed) {
1052
+ return this;
1053
+ }
1054
+
1055
+ if (p.resizer) {
1056
+ p.resizer.remove();
1057
+ p.resizer = null;
1058
+ }
1059
+
1060
+ p.virtualListHelper?.destroy();
1061
+ p.virtualListHelper = null;
1062
+
1063
+ // Using quotes for __super__ because Google Closure Compiler has a bug...
1064
+
1065
+ this._destroyHeaderCells();
1066
+
1067
+ p.table?.remove();
1068
+ p.tbody?.remove();
1069
+
1070
+ if (p.workerListeners) {
1071
+ for (let j = 0; j < p.workerListeners.length; j++) {
1072
+ let worker = p.workerListeners[j];
1073
+ worker.worker.removeEventListener('message', worker.listener, false);
1074
+ }
1075
+ p.workerListeners.length = 0;
1076
+ }
1077
+
1078
+ p.rows.length = p.columns.length = 0;
1079
+
1080
+ if (p._deferredRender) {
1081
+ clearTimeout(p._deferredRender);
1082
+ }
1083
+
1084
+ // Cleanup
1085
+ for (let prop in this) {
1086
+ if (hasOwnProperty.call(this, prop)) {
1087
+ this[prop] = null;
1088
+ }
1089
+ }
1090
+
1091
+ this.__removed = true;
1092
+
1093
+ if (el) {
1094
+ el.remove();
1095
+ }
1096
+
1097
+ return this;
1098
+ }
1099
+
1100
+ // Backwards compatibility
1101
+ close() {
1102
+ this.destroy();
1103
+ }
1104
+
1105
+ // Backwards compatibility
1106
+ remove() {
1107
+ this.destroy();
1108
+ }
1109
+
1110
+ /**
1111
+ * @private
1112
+ * @returns {DGTable} self
1113
+ */
1114
+ _unbindCellEventsForTable() {
1115
+ const p = this._p;
1116
+
1117
+ if (p.headerRow) {
1118
+ for (let i = 0, rows = p.headerRow.childNodes, rowCount = rows.length; i < rowCount; i++) {
1119
+ let rowToClean = rows[i];
1120
+ for (let j = 0, cells = rowToClean.childNodes, cellCount = cells.length; j < cellCount; j++) {
1121
+ p._unbindCellHoverIn(cells[j]);
1122
+ }
1123
+ }
1124
+ }
1125
+
1126
+ return this;
1127
+ }
1128
+
1129
+ /**
1130
+ * @private
1131
+ * @param {HTMLElement} rowToClean
1132
+ * @returns {DGTable} self
1133
+ */
1134
+ _unbindCellEventsForRow(rowToClean) {
1135
+ const p = this._p;
1136
+ for (let i = 0, cells = rowToClean.childNodes, cellCount = cells.length; i < cellCount; i++) {
1137
+ p._unbindCellHoverIn(cells[i]);
1138
+ }
1139
+ return this;
1140
+ }
1141
+
1142
+ /**
1143
+ * @public
1144
+ * @expose
1145
+ * @returns {DGTable} self
1146
+ */
1147
+ render() {
1148
+ const o = this._o,p = this._p;
1149
+
1150
+ if (!this.el.offsetParent) {
1151
+ if (!p._deferredRender) {
1152
+ p._deferredRender = setTimeout(() => {
1153
+ p._deferredRender = null;
1154
+ if (!this.__removed && this.el.offsetParent) {
1155
+ this.render();
1156
+ }
1157
+ });
1158
+ }
1159
+
1160
+ return this;
1161
+ }
1162
+
1163
+ if (p.tableSkeletonNeedsRendering === true) {
1164
+ p.tableSkeletonNeedsRendering = false;
1165
+
1166
+ if (o.width === DGTable.Width.AUTO) {
1167
+ // We need to do this to return to the specified widths instead. The arrows added to the column widths...
1168
+ this._clearSortArrows();
1169
+ }
1170
+
1171
+ let lastScrollTop = p.table && p.table.parentNode ? p.table.scrollTop : NaN,
1172
+ lastScrollHorz = p.table && p.table.parentNode ? ScrollHelper_js.getScrollHorz(p.table) : NaN;
1173
+
1174
+ this._renderSkeletonBase().
1175
+ _renderSkeletonBody().
1176
+ tableWidthChanged(true, false) // Take this chance to calculate required column widths
1177
+ ._renderSkeletonHeaderCells();
1178
+
1179
+ p.virtualListHelper.setCount((p.filteredRows ?? p.rows).length);
1180
+
1181
+ this._updateVirtualHeight();
1182
+ this._updateLastCellWidthFromScrollbar(true);
1183
+ this._updateTableWidth(true);
1184
+
1185
+ // Show sort arrows
1186
+ for (let i = 0; i < p.rows.sortColumn.length; i++) {
1187
+ this._showSortArrow(p.rows.sortColumn[i].column, p.rows.sortColumn[i].descending);
1188
+ }
1189
+ if (o.adjustColumnWidthForSortArrow && p.rows.sortColumn.length) {
1190
+ this.tableWidthChanged(true);
1191
+ } else if (!o.virtualTable) {
1192
+ this.tableWidthChanged();
1193
+ }
1194
+
1195
+ if (!isNaN(lastScrollTop))
1196
+ p.table.scrollTop = lastScrollTop;
1197
+
1198
+ if (!isNaN(lastScrollHorz)) {
1199
+ ScrollHelper_js.setScrollHorz(p.table, lastScrollHorz);
1200
+ ScrollHelper_js.setScrollHorz(p.header, lastScrollHorz);
1201
+ }
1202
+
1203
+ this.emit('renderskeleton');
1204
+ }
1205
+
1206
+ p.virtualListHelper.render();
1207
+
1208
+ this.emit('render');
1209
+ return this;
1210
+ }
1211
+
1212
+ /**
1213
+ * Forces a full render of the table
1214
+ * @public
1215
+ * @expose
1216
+ * @param {boolean=true} render - Should render now?
1217
+ * @returns {DGTable} self
1218
+ */
1219
+ clearAndRender(render) {
1220
+ let p = this._p;
1221
+
1222
+ p.tableSkeletonNeedsRendering = true;
1223
+ p.notifyRendererOfColumnsConfig?.();
1224
+
1225
+ if (render === undefined || render) {
1226
+ this.render();
1227
+ }
1228
+
1229
+ return this;
1230
+ }
1231
+
1232
+ /**
1233
+ * Calculate the size required for the table body width (which is the row's width)
1234
+ * @private
1235
+ * @returns {number} calculated width
1236
+ */
1237
+ _calculateTbodyWidth() {
1238
+ const p = this._p;
1239
+
1240
+ let tableClassName = this._o.tableClassName,
1241
+ rowClassName = tableClassName + '-row',
1242
+ cellClassName = tableClassName + '-cell',
1243
+ visibleColumns = p.visibleColumns,
1244
+ colCount = visibleColumns.length;
1245
+
1246
+ const row = createElement('div');
1247
+ row.className = rowClassName;
1248
+ row.style.float = 'left';
1249
+
1250
+ let sumActualWidth = 0;
1251
+
1252
+ for (let colIndex = 0; colIndex < colCount; colIndex++) {
1253
+ const column = visibleColumns[colIndex];
1254
+ const cell = createElement('div');
1255
+ cell.className = cellClassName;
1256
+ cell.style.width = column.actualWidth + 'px';
1257
+ if (column.cellClasses) cell.className += ' ' + column.cellClasses;
1258
+ cell.appendChild(createElement('div'));
1259
+ row.appendChild(cell);
1260
+ sumActualWidth += column.actualWidth;
1261
+ }
1262
+
1263
+ const thisWrapper = createElement('div');
1264
+ thisWrapper.className = this.el.className;
1265
+ Css_js.setCssProps(thisWrapper, {
1266
+ 'z-index': -1,
1267
+ 'position': 'absolute',
1268
+ 'left': '0',
1269
+ 'top': '-9999px',
1270
+ 'float': 'left',
1271
+ 'width': '1px',
1272
+ 'overflow': 'hidden'
1273
+ });
1274
+
1275
+ const tableDiv = createElement('div');
1276
+ tableDiv.className = tableClassName;
1277
+ thisWrapper.appendChild(tableDiv);
1278
+ const tableBodyDiv = createElement('div');
1279
+ tableBodyDiv.className = tableClassName + '-body';
1280
+ tableBodyDiv.style.width = sumActualWidth + 10000 + 'px';
1281
+ tableDiv.appendChild(tableBodyDiv);
1282
+ tableBodyDiv.appendChild(row);
1283
+
1284
+ document.body.appendChild(thisWrapper);
1285
+
1286
+ const fractionTest = createElement('div');
1287
+ Css_js.setCssProps(fractionTest, {
1288
+ border: '1.5px solid #000',
1289
+ width: '0',
1290
+ height: '0',
1291
+ position: 'absolute',
1292
+ left: '0',
1293
+ top: '-9999px'
1294
+ });
1295
+ document.body.appendChild(fractionTest);
1296
+ let fractionValue = parseFloat(getComputedStyle(fractionTest).borderWidth);
1297
+ let hasFractions = Math.round(fractionValue) !== fractionValue;
1298
+ fractionTest.remove();
1299
+
1300
+ let width = Css_js.getElementWidth(row, true, true, true);
1301
+ width -= p.scrollbarWidth || 0;
1302
+
1303
+ if (hasFractions) {
1304
+ width++;
1305
+ }
1306
+
1307
+ thisWrapper.remove();
1308
+ return width;
1309
+ }
1310
+
1311
+ /**
1312
+ * Sets the columns of the table
1313
+ * @public
1314
+ * @expose
1315
+ * @param {COLUMN_OPTIONS[]} columns - Column definitions array
1316
+ * @param {boolean=true} render - Should render now?
1317
+ * @returns {DGTable} self
1318
+ */
1319
+ setColumns(columns, render) {
1320
+ const p = this._p;
1321
+
1322
+ columns = columns || [];
1323
+
1324
+ let normalizedCols = new ColumnCollection();
1325
+ for (let i = 0, order = 0; i < columns.length; i++) {
1326
+
1327
+ let columnData = columns[i];
1328
+ let normalizedColumn = this._initColumnFromData(columnData);
1329
+
1330
+ if (columnData.order !== undefined) {
1331
+ if (columnData.order > order) {
1332
+ order = columnData.order + 1;
1333
+ }
1334
+ normalizedColumn.order = columnData.order;
1335
+ } else {
1336
+ normalizedColumn.order = order++;
1337
+ }
1338
+
1339
+ normalizedCols.push(normalizedColumn);
1340
+ }
1341
+ normalizedCols.normalizeOrder();
1342
+
1343
+ p.columns = normalizedCols;
1344
+ p.visibleColumns = normalizedCols.getVisibleColumns();
1345
+
1346
+ this._ensureVisibleColumns().clearAndRender(render);
1347
+
1348
+ return this;
1349
+ }
1350
+
1351
+ /**
1352
+ * Add a column to the table
1353
+ * @public
1354
+ * @expose
1355
+ * @param {COLUMN_OPTIONS} columnData column properties
1356
+ * @param {string|number} [before=-1] column name or order to be inserted before
1357
+ * @param {boolean=true} render - Should render now?
1358
+ * @returns {DGTable} self
1359
+ */
1360
+ addColumn(columnData, before, render) {
1361
+ const p = this._p;
1362
+ let columns = p.columns;
1363
+
1364
+ if (columnData && !columns.get(columnData.name)) {
1365
+ let beforeColumn = null;
1366
+ if (before !== undefined) {
1367
+ beforeColumn = columns.get(before) || columns.getByOrder(before);
1368
+ }
1369
+
1370
+ let column = this._initColumnFromData(columnData);
1371
+ column.order = beforeColumn ? beforeColumn.order : columns.getMaxOrder() + 1;
1372
+
1373
+ for (let i = columns.getMaxOrder(), to = column.order; i >= to; i--) {
1374
+ let col = columns.getByOrder(i);
1375
+ if (col) {
1376
+ col.order++;
1377
+ }
1378
+ }
1379
+
1380
+ columns.push(column);
1381
+ columns.normalizeOrder();
1382
+
1383
+ p.visibleColumns = columns.getVisibleColumns();
1384
+ this._ensureVisibleColumns().clearAndRender(render);
1385
+
1386
+ this.emit('addcolumn', column.name);
1387
+ }
1388
+ return this;
1389
+ }
1390
+
1391
+ /**
1392
+ * Remove a column from the table
1393
+ * @public
1394
+ * @expose
1395
+ * @param {string} column column name
1396
+ * @param {boolean=true} render - Should render now?
1397
+ * @returns {DGTable} self
1398
+ */
1399
+ removeColumn(column, render) {
1400
+ const p = this._p;
1401
+ let columns = p.columns;
1402
+
1403
+ let colIdx = columns.indexOf(column);
1404
+ if (colIdx > -1) {
1405
+ columns.splice(colIdx, 1);
1406
+ columns.normalizeOrder();
1407
+
1408
+ p.visibleColumns = columns.getVisibleColumns();
1409
+ this._ensureVisibleColumns().clearAndRender(render);
1410
+
1411
+ this.emit('removecolumn', column);
1412
+ }
1413
+ return this;
1414
+ }
1415
+
1416
+ /**
1417
+ * Sets a new cell formatter.
1418
+ * @public
1419
+ * @expose
1420
+ * @param {function(value: *, columnName: string, row: Object):string|null} [formatter=null] - The cell formatter. Should return an HTML.
1421
+ * @returns {DGTable} self
1422
+ */
1423
+ setCellFormatter(formatter) {
1424
+ if (!formatter) {
1425
+ formatter = (val) => typeof val === 'string' ? htmlEncode(val) : val;
1426
+ formatter[IsSafeSymbol] = true;
1427
+ }
1428
+
1429
+ /**
1430
+ * @private
1431
+ * @field {Function} cellFormatter */
1432
+ this._o.cellFormatter = formatter;
1433
+
1434
+ return this;
1435
+ }
1436
+
1437
+ /**
1438
+ * Sets a new header cell formatter.
1439
+ * @public
1440
+ * @expose
1441
+ * @param {function(label: string, columnName: string):string|null} [formatter=null] - The cell formatter. Should return an HTML.
1442
+ * @returns {DGTable} self
1443
+ */
1444
+ setHeaderCellFormatter(formatter) {
1445
+ /**
1446
+ * @private
1447
+ * @field {Function} headerCellFormatter */
1448
+ this._o.headerCellFormatter = formatter || function (val) {
1449
+ return typeof val === 'string' ? htmlEncode(val) : val;
1450
+ };
1451
+
1452
+ return this;
1453
+ }
1454
+
1455
+ /**
1456
+ * @public
1457
+ * @expose
1458
+ * @param {function(row:Object,args:Object):boolean|null} [filterFunc=null] - The filter function to work with filters. Default is a by-colum filter.
1459
+ * @returns {DGTable} self
1460
+ */
1461
+ setFilter(filterFunc) {
1462
+ /** @private
1463
+ * @field {Function} filter */
1464
+ this._o.filter = filterFunc;
1465
+ return this;
1466
+ }
1467
+
1468
+ /**
1469
+ * @public
1470
+ * @expose
1471
+ * @param {Object|null} args - Options to pass to the filter function
1472
+ * @returns {DGTable} self
1473
+ */
1474
+ filter(args) {
1475
+ const p = this._p;
1476
+
1477
+ let filterFunc = this._o.filter || ByColumnFilter;
1478
+
1479
+ // Deprecated use of older by-column filter
1480
+ if (typeof arguments[0] === 'string' && typeof arguments[1] === 'string') {
1481
+ args = {
1482
+ column: arguments[0],
1483
+ keyword: arguments[1],
1484
+ caseSensitive: arguments[2]
1485
+ };
1486
+ }
1487
+
1488
+ let hadFilter = !!p.filteredRows;
1489
+ if (p.filteredRows) {
1490
+ p.filteredRows = null; // Allow releasing array memory now
1491
+ }
1492
+
1493
+ // Shallow-clone the args, as the filter function may want to modify it for keeping state
1494
+ p.filterArgs = args == null ? null : typeof args === 'object' && !Array.isArray(args) ? Object.assign({}, args) : args;
1495
+
1496
+ if (p.filterArgs !== null) {
1497
+ p.filteredRows = p.rows.filteredCollection(filterFunc, p.filterArgs);
1498
+
1499
+ if (hadFilter || p.filteredRows) {
1500
+ this.clearAndRender();
1501
+ this.emit('filter', args);
1502
+ }
1503
+ } else
1504
+ {
1505
+ p.filterArgs = null;
1506
+ p.filteredRows = null;
1507
+ this.clearAndRender();
1508
+ this.emit('filterclear', {});
1509
+ }
1510
+
1511
+ return this;
1512
+ }
1513
+
1514
+ /**
1515
+ * @public
1516
+ * @expose
1517
+ * @returns {DGTable} self
1518
+ */
1519
+ clearFilter() {
1520
+ const p = this._p;
1521
+
1522
+ if (p.filteredRows) {
1523
+ p.filterArgs = null;
1524
+ p.filteredRows = null;
1525
+ this.clearAndRender();
1526
+ this.emit('filterclear', {});
1527
+ }
1528
+
1529
+ return this;
1530
+ }
1531
+
1532
+ /**
1533
+ * @private
1534
+ * @returns {DGTable} self
1535
+ */
1536
+ _refilter() {
1537
+ const p = this._p;
1538
+
1539
+ if (p.filteredRows && p.filterArgs) {
1540
+ let filterFunc = this._o.filter || ByColumnFilter;
1541
+ p.filteredRows = p.rows.filteredCollection(filterFunc, p.filterArgs);
1542
+ }
1543
+ return this;
1544
+ }
1545
+
1546
+ /**
1547
+ * Set a new label to a column
1548
+ * @public
1549
+ * @expose
1550
+ * @param {string} column Name of the column
1551
+ * @param {string} label New label for the column
1552
+ * @returns {DGTable} self
1553
+ */
1554
+ setColumnLabel(column, label) {
1555
+ const p = this._p;
1556
+
1557
+ let col = p.columns.get(column);
1558
+ if (col) {
1559
+ col.label = label === undefined ? col.name : label;
1560
+
1561
+ if (col.element) {
1562
+ for (let i = 0; i < col.element.firstChild.childNodes.length; i++) {
1563
+ let node = col.element.firstChild.childNodes[i];
1564
+ if (node.nodeType === 3) {
1565
+ node.textContent = col.label;
1566
+ break;
1567
+ }
1568
+ }
1569
+ }
1570
+ }
1571
+ return this;
1572
+ }
1573
+
1574
+ /**
1575
+ * Move a column to a new position
1576
+ * @public
1577
+ * @expose
1578
+ * @param {string|number} src Name or position of the column to be moved
1579
+ * @param {string|number} dest Name of the column currently in the desired position, or the position itself
1580
+ * @param {boolean} [visibleOnly=true] Should consider only visible columns and visible-relative indexes
1581
+ * @returns {DGTable} self
1582
+ */
1583
+ moveColumn(src, dest) {let visibleOnly = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1584
+ const o = this._o,p = this._p;
1585
+
1586
+ let columns = p.columns,
1587
+ col,destCol;
1588
+
1589
+ let columnsArray = visibleOnly ? p.visibleColumns : columns.getColumns();
1590
+
1591
+ if (typeof src === 'string') {
1592
+ col = columns.get(src);
1593
+ } else if (typeof src === 'number') {
1594
+ col = columnsArray[src];
1595
+ }
1596
+ if (typeof dest === 'string') {
1597
+ destCol = columns.get(dest);
1598
+ } else if (typeof dest === 'number') {
1599
+ destCol = columnsArray[dest];
1600
+ }
1601
+
1602
+ if (col && destCol && src !== dest) {
1603
+ let srcOrder = col.order,destOrder = destCol.order;
1604
+
1605
+ let visibleColumns = columns.moveColumn(col, destCol).getVisibleColumns();
1606
+
1607
+ if (p.visibleColumns.length !== visibleColumns.length ||
1608
+ p.visibleColumns.some((x, i) => x !== visibleColumns[i])) {
1609
+
1610
+ p.visibleColumns = visibleColumns;
1611
+ this._ensureVisibleColumns();
1612
+
1613
+ if (o.virtualTable) {
1614
+ this.clearAndRender();
1615
+ } else {
1616
+ const headerCells = DomCompat_js.scopedSelectorAll(p.headerRow, `>div.${o.tableClassName}-header-cell`);
1617
+ let beforePos = srcOrder < destOrder ? destOrder + 1 : destOrder,
1618
+ fromPos = srcOrder;
1619
+ headerCells[0].parentNode.insertBefore(headerCells[fromPos], headerCells[beforePos]);
1620
+
1621
+ let srcWidth = p.visibleColumns[srcOrder];
1622
+ srcWidth = (srcWidth.actualWidthConsideringScrollbarWidth || srcWidth.actualWidth) + 'px';
1623
+ let destWidth = p.visibleColumns[destOrder];
1624
+ destWidth = (destWidth.actualWidthConsideringScrollbarWidth || destWidth.actualWidth) + 'px';
1625
+
1626
+ let tbodyChildren = p.tbody.childNodes;
1627
+ for (let i = 0, count = tbodyChildren.length; i < count; i++) {
1628
+ let row = tbodyChildren[i];
1629
+ if (row.nodeType !== 1) continue;
1630
+ row.insertBefore(row.childNodes[fromPos], row.childNodes[beforePos]);
1631
+ row.childNodes[destOrder].firstChild.style.width = destWidth;
1632
+ row.childNodes[srcOrder].firstChild.style.width = srcWidth;
1633
+ }
1634
+ }
1635
+ }
1636
+
1637
+ this.emit('movecolumn', { name: col.name, src: srcOrder, dest: destOrder });
1638
+ }
1639
+ return this;
1640
+ }
1641
+
1642
+ /**
1643
+ * Sort the table
1644
+ * @public
1645
+ * @expose
1646
+ * @param {string?} column Name of the column to sort on (or null to remove sort arrow)
1647
+ * @param {boolean=} descending Sort in descending order
1648
+ * @param {boolean} [add=false] Should this sort be on top of the existing sort? (For multiple column sort)
1649
+ * @returns {DGTable} self
1650
+ */
1651
+ sort(column, descending, add) {
1652
+ const o = this._o,p = this._p;
1653
+
1654
+ let columns = p.columns,
1655
+ col = columns.get(column);
1656
+
1657
+ let currentSort = p.rows.sortColumn;
1658
+
1659
+ if (col) {
1660
+ if (add) {// Add the sort to current sort stack
1661
+
1662
+ for (let i = 0; i < currentSort.length; i++) {
1663
+ if (currentSort[i].column === col.name) {
1664
+ if (i < currentSort.length - 1) {
1665
+ currentSort.length = 0;
1666
+ } else {
1667
+ descending = currentSort[currentSort.length - 1].descending;
1668
+ currentSort.splice(currentSort.length - 1, 1);
1669
+ }
1670
+ break;
1671
+ }
1672
+ }
1673
+ if (o.sortableColumns > 0 /* allow manual sort when disabled */ && currentSort.length >= o.sortableColumns || currentSort.length >= p.visibleColumns.length) {
1674
+ currentSort.length = 0;
1675
+ }
1676
+
1677
+ } else {// Sort only by this column
1678
+ currentSort.length = 0;
1679
+ }
1680
+
1681
+ // Default to ascending
1682
+ descending = descending === undefined ? false : descending;
1683
+
1684
+ // Set the required column in the front of the stack
1685
+ currentSort.push({
1686
+ column: col.name,
1687
+ comparePath: col.comparePath || col.dataPath,
1688
+ descending: !!descending
1689
+ });
1690
+ } else {
1691
+ currentSort.length = 0;
1692
+ }
1693
+
1694
+ this._clearSortArrows();
1695
+
1696
+ for (let i = 0; i < currentSort.length; i++) {
1697
+ this._showSortArrow(currentSort[i].column, currentSort[i].descending);
1698
+ }
1699
+
1700
+ if (o.adjustColumnWidthForSortArrow && !p.tableSkeletonNeedsRendering) {
1701
+ this.tableWidthChanged(true);
1702
+ }
1703
+
1704
+ p.rows.sortColumn = currentSort;
1705
+
1706
+ let comparator;
1707
+ if (currentSort.length) {
1708
+ comparator = p.rows.sort(!!p.filteredRows);
1709
+ if (p.filteredRows) {
1710
+ p.filteredRows.sort(!!p.filteredRows);
1711
+ }
1712
+ }
1713
+
1714
+ if (p.virtualListHelper)
1715
+ p.virtualListHelper.invalidate().render();
1716
+
1717
+ // Build output for event, with option names that will survive compilers
1718
+ let sorts = [];
1719
+ for (let i = 0; i < currentSort.length; i++) {
1720
+ sorts.push({ 'column': currentSort[i].column, 'descending': currentSort[i].descending });
1721
+ }
1722
+ this.emit('sort', { sorts: sorts, comparator: comparator });
1723
+
1724
+ return this;
1725
+ }
1726
+
1727
+ /**
1728
+ * Re-sort the table using current sort specifiers
1729
+ * @public
1730
+ * @expose
1731
+ * @returns {DGTable} self
1732
+ */
1733
+ resort() {
1734
+ const p = this._p;
1735
+ let columns = p.columns;
1736
+
1737
+ let currentSort = p.rows.sortColumn;
1738
+ if (currentSort.length) {
1739
+
1740
+ for (let i = 0; i < currentSort.length; i++) {
1741
+ if (!columns.get(currentSort[i].column)) {
1742
+ currentSort.splice(i--, 1);
1743
+ }
1744
+ }
1745
+
1746
+ let comparator;
1747
+ p.rows.sortColumn = currentSort;
1748
+ if (currentSort.length) {
1749
+ comparator = p.rows.sort(!!p.filteredRows);
1750
+ if (p.filteredRows) {
1751
+ p.filteredRows.sort(!!p.filteredRows);
1752
+ }
1753
+ }
1754
+
1755
+ // Build output for event, with option names that will survive compilers
1756
+ let sorts = [];
1757
+ for (let i = 0; i < currentSort.length; i++) {
1758
+ sorts.push({ 'column': currentSort[i].column, 'descending': currentSort[i].descending });
1759
+ }
1760
+ this.emit('sort', { sorts: sorts, resort: true, comparator: comparator });
1761
+ }
1762
+
1763
+ return this;
1764
+ }
1765
+
1766
+ /**
1767
+ * Make sure there's at least one column visible
1768
+ * @private
1769
+ * @expose
1770
+ * @returns {DGTable} self
1771
+ */
1772
+ _ensureVisibleColumns() {
1773
+ const p = this._p;
1774
+
1775
+ if (p.visibleColumns.length === 0 && p.columns.length) {
1776
+ p.columns[0].visible = true;
1777
+ p.visibleColumns.push(p.columns[0]);
1778
+ this.emit('showcolumn', p.columns[0].name);
1779
+ }
1780
+
1781
+ return this;
1782
+ }
1783
+
1784
+ /**
1785
+ * Show or hide a column
1786
+ * @public
1787
+ * @expose
1788
+ * @param {string} column Unique column name
1789
+ * @param {boolean} visible New visibility mode for the column
1790
+ * @returns {DGTable} self
1791
+ */
1792
+ setColumnVisible(column, visible) {
1793
+ const p = this._p;
1794
+
1795
+ let col = p.columns.get(column);
1796
+
1797
+ //noinspection PointlessBooleanExpressionJS
1798
+ visible = !!visible;
1799
+
1800
+ if (col && !!col.visible !== visible) {
1801
+ col.visible = visible;
1802
+ p.visibleColumns = p.columns.getVisibleColumns();
1803
+ this.emit(visible ? 'showcolumn' : 'hidecolumn', column);
1804
+ this._ensureVisibleColumns();
1805
+ this.clearAndRender();
1806
+ }
1807
+ return this;
1808
+ }
1809
+
1810
+ /**
1811
+ * Get the visibility mode of a column
1812
+ * @public
1813
+ * @expose
1814
+ * @returns {boolean} true if visible
1815
+ */
1816
+ isColumnVisible(column) {
1817
+ const p = this._p;
1818
+ let col = p.columns.get(column);
1819
+ if (col) {
1820
+ return col.visible;
1821
+ }
1822
+ return false;
1823
+ }
1824
+
1825
+ /**
1826
+ * Globally set the minimum column width
1827
+ * @public
1828
+ * @expose
1829
+ * @param {number} minColumnWidth Minimum column width
1830
+ * @returns {DGTable} self
1831
+ */
1832
+ setMinColumnWidth(minColumnWidth) {
1833
+ let o = this._o;
1834
+ minColumnWidth = Math.max(minColumnWidth, 0);
1835
+ if (o.minColumnWidth !== minColumnWidth) {
1836
+ o.minColumnWidth = minColumnWidth;
1837
+ this.tableWidthChanged(true);
1838
+ }
1839
+ return this;
1840
+ }
1841
+
1842
+ /**
1843
+ * Get the current minimum column width
1844
+ * @public
1845
+ * @expose
1846
+ * @returns {number} Minimum column width
1847
+ */
1848
+ getMinColumnWidth() {
1849
+ return this._o.minColumnWidth;
1850
+ }
1851
+
1852
+ /**
1853
+ * Set the limit on concurrent columns sorted
1854
+ * @public
1855
+ * @expose
1856
+ * @param {number} sortableColumns How many sortable columns to allow?
1857
+ * @returns {DGTable} self
1858
+ */
1859
+ setSortableColumns(sortableColumns) {
1860
+ const p = this._p,o = this._o;
1861
+ if (o.sortableColumns !== sortableColumns) {
1862
+ o.sortableColumns = sortableColumns;
1863
+ if (p.table) {
1864
+ const headerCells = DomCompat_js.scopedSelectorAll(p.headerRow, `>div.${o.tableClassName}-header-cell`);
1865
+ for (let i = 0, len = headerCells.length; i < len; i++) {
1866
+ const cell = headerCells[i];
1867
+ cell.classList[o.sortableColumns > 0 && p.visibleColumns[i].sortable ? 'add' : 'remove']('sortable');
1868
+ }
1869
+ }
1870
+ }
1871
+ return this;
1872
+ }
1873
+
1874
+ /**
1875
+ * Get the limit on concurrent columns sorted
1876
+ * @public
1877
+ * @expose
1878
+ * @returns {number} How many sortable columns are allowed?
1879
+ */
1880
+ getSortableColumns() {
1881
+ return this._o.sortableColumns;
1882
+ }
1883
+
1884
+ /**
1885
+ * @public
1886
+ * @expose
1887
+ * @param {boolean?} movableColumns=true are the columns movable?
1888
+ * @returns {DGTable} self
1889
+ */
1890
+ setMovableColumns(movableColumns) {
1891
+ let o = this._o;
1892
+ //noinspection PointlessBooleanExpressionJS
1893
+ movableColumns = movableColumns === undefined ? true : !!movableColumns;
1894
+ if (o.movableColumns !== movableColumns) {
1895
+ o.movableColumns = movableColumns;
1896
+ }
1897
+ return this;
1898
+ }
1899
+
1900
+ /**
1901
+ * @public
1902
+ * @expose
1903
+ * @returns {boolean} are the columns movable?
1904
+ */
1905
+ getMovableColumns() {
1906
+ return this._o.movableColumns;
1907
+ }
1908
+
1909
+ /**
1910
+ * @public
1911
+ * @expose
1912
+ * @param {boolean} resizableColumns=true are the columns resizable?
1913
+ * @returns {DGTable} self
1914
+ */
1915
+ setResizableColumns(resizableColumns) {
1916
+ let o = this._o;
1917
+ //noinspection PointlessBooleanExpressionJS
1918
+ resizableColumns = resizableColumns === undefined ? true : !!resizableColumns;
1919
+ if (o.resizableColumns !== resizableColumns) {
1920
+ o.resizableColumns = resizableColumns;
1921
+ }
1922
+ return this;
1923
+ }
1924
+
1925
+ /**
1926
+ * @public
1927
+ * @expose
1928
+ * @returns {boolean} are the columns resizable?
1929
+ */
1930
+ getResizableColumns() {
1931
+ return this._o.resizableColumns;
1932
+ }
1933
+
1934
+ /**
1935
+ * Sets a functions that supplies comparators dynamically
1936
+ * @public
1937
+ * @expose
1938
+ * @param {{function(columnName: string, descending: boolean, defaultComparator: {function(a:any,b:any):number}):{function(a:any,b:any):number}}|null|undefined} comparatorCallback a function that returns the comparator for a specific column
1939
+ * @returns {DGTable} self
1940
+ */
1941
+ setOnComparatorRequired(comparatorCallback) {
1942
+ let o = this._o;
1943
+ if (o.onComparatorRequired !== comparatorCallback) {
1944
+ o.onComparatorRequired = comparatorCallback;
1945
+ }
1946
+ return this;
1947
+ }
1948
+
1949
+ // Backwards compatibility
1950
+ setComparatorCallback(comparatorCallback) {
1951
+ return this.setOnComparatorRequired(comparatorCallback);
1952
+ }
1953
+
1954
+ /**
1955
+ * sets custom sorting function for a data set
1956
+ * @public
1957
+ * @expose
1958
+ * @param {{function(data: any[], sort: function(any[]):any[]):any[]}|null|undefined} customSortingProvider provides a custom sorting function (not the comparator, but a sort() alternative) for a data set
1959
+ * @returns {DGTable} self
1960
+ */
1961
+ setCustomSortingProvider(customSortingProvider) {
1962
+ let o = this._o;
1963
+ if (o.customSortingProvider !== customSortingProvider) {
1964
+ o.customSortingProvider = customSortingProvider;
1965
+ }
1966
+ return this;
1967
+ }
1968
+
1969
+ /**
1970
+ * Set a new width to a column
1971
+ * @public
1972
+ * @expose
1973
+ * @param {string} column name of the column to resize
1974
+ * @param {number|string} width new column as pixels, or relative size (0.5, 50%)
1975
+ * @returns {DGTable} self
1976
+ */
1977
+ setColumnWidth(column, width) {
1978
+
1979
+ const p = this._p;
1980
+
1981
+ let col = p.columns.get(column);
1982
+
1983
+ let parsedWidth = this._parseColumnWidth(width, col.ignoreMin ? 0 : this._o.minColumnWidth);
1984
+
1985
+ if (col) {
1986
+ let oldWidth = this._serializeColumnWidth(col);
1987
+
1988
+ col.width = parsedWidth.width;
1989
+ col.widthMode = parsedWidth.mode;
1990
+
1991
+ let newWidth = this._serializeColumnWidth(col);
1992
+
1993
+ if (oldWidth !== newWidth) {
1994
+ this.tableWidthChanged(true); // Calculate actual sizes
1995
+ }
1996
+
1997
+ this.emit('columnwidth', { name: col.name, width: newWidth, oldWidth: oldWidth });
1998
+ }
1999
+ return this;
2000
+ }
2001
+
2002
+ /**
2003
+ * @public
2004
+ * @expose
2005
+ * @param {string} column name of the column
2006
+ * @returns {string|null} the serialized width of the specified column, or null if column not found
2007
+ */
2008
+ getColumnWidth(column) {
2009
+ const p = this._p;
2010
+
2011
+ let col = p.columns.get(column);
2012
+ if (col) {
2013
+ return this._serializeColumnWidth(col);
2014
+ }
2015
+ return null;
2016
+ }
2017
+
2018
+ /**
2019
+ * @public
2020
+ * @expose
2021
+ * @param {string} column name of the column
2022
+ * @returns {SERIALIZED_COLUMN|null} configuration for all columns
2023
+ */
2024
+ getColumnConfig(column) {
2025
+ const p = this._p;
2026
+ let col = p.columns.get(column);
2027
+ if (col) {
2028
+ return {
2029
+ 'order': col.order,
2030
+ 'width': this._serializeColumnWidth(col),
2031
+ 'visible': col.visible,
2032
+ 'label': col.label
2033
+ };
2034
+ }
2035
+ return null;
2036
+ }
2037
+
2038
+ /**
2039
+ * Returns a config object for the columns, to allow saving configurations for next time...
2040
+ * @public
2041
+ * @expose
2042
+ * @returns {Object} configuration for all columns
2043
+ */
2044
+ getColumnsConfig() {
2045
+ const p = this._p;
2046
+
2047
+ let config = {};
2048
+ for (let i = 0; i < p.columns.length; i++) {
2049
+ config[p.columns[i].name] = this.getColumnConfig(p.columns[i].name);
2050
+ }
2051
+ return config;
2052
+ }
2053
+
2054
+ /**
2055
+ * Returns an array of the currently sorted columns
2056
+ * @public
2057
+ * @expose
2058
+ * @returns {Array.<SERIALIZED_COLUMN_SORT>} configuration for all columns
2059
+ */
2060
+ getSortedColumns() {
2061
+ const p = this._p;
2062
+
2063
+ let sorted = [];
2064
+ for (let i = 0; i < p.rows.sortColumn.length; i++) {
2065
+ let sort = p.rows.sortColumn[i];
2066
+ sorted.push({ column: sort.column, descending: sort.descending });
2067
+ }
2068
+ return sorted;
2069
+ }
2070
+
2071
+ /**
2072
+ * Returns the HTML string for a specific cell. Can be used externally for special cases (i.e. when setting a fresh HTML in the cell preview through the callback).
2073
+ * @public
2074
+ * @expose
2075
+ * @param {number} rowIndex - index of the row
2076
+ * @param {string} columnName - name of the column
2077
+ * @returns {string|null} HTML string for the specified cell
2078
+ */
2079
+ getHtmlForRowCell(rowIndex, columnName) {
2080
+ const p = this._p;
2081
+
2082
+ if (rowIndex < 0 || rowIndex > p.rows.length - 1) return null;
2083
+ let column = p.columns.get(columnName);
2084
+ if (!column) return null;
2085
+ let rowData = p.rows[rowIndex];
2086
+
2087
+ return this._getHtmlForCell(rowData, column);
2088
+ }
2089
+
2090
+ /**
2091
+ * Returns the HTML string for a specific cell. Can be used externally for special cases (i.e. when setting a fresh HTML in the cell preview through the callback).
2092
+ * @public
2093
+ * @expose
2094
+ * @param {Object} rowData - row data
2095
+ * @param {Object} columnName - column data
2096
+ * @returns {string|null} HTML string for the specified cell
2097
+ */
2098
+ getHtmlForRowDataCell(rowData, columnName) {
2099
+ const p = this._p;
2100
+
2101
+ let column = p.columns.get(columnName);
2102
+ if (!column) return null;
2103
+
2104
+ return this._getHtmlForCell(rowData, column);
2105
+ }
2106
+
2107
+ /**
2108
+ * Returns the HTML string for a specific cell. Can be used externally for special cases (i.e. when setting a fresh HTML in the cell preview through the callback).
2109
+ * @private
2110
+ * @expose
2111
+ * @param {Object} rowData - row data
2112
+ * @param {Object} column - column data
2113
+ * @returns {string} HTML string for the specified cell
2114
+ */
2115
+ _getHtmlForCell(rowData, column) {
2116
+ let dataPath = column.dataPath;
2117
+ let colValue = rowData[dataPath[0]];
2118
+ for (let dataPathIndex = 1; dataPathIndex < dataPath.length; dataPathIndex++) {
2119
+ if (colValue == null) break;
2120
+ colValue = colValue && colValue[dataPath[dataPathIndex]];
2121
+ }
2122
+
2123
+ const formatter = this._o.cellFormatter;
2124
+ let content;
2125
+
2126
+ if (formatter[IsSafeSymbol]) {
2127
+ content = formatter(colValue, column.name, rowData);
2128
+ } else {
2129
+ try {
2130
+ content = formatter(colValue, column.name, rowData);
2131
+ } catch (err) {
2132
+ content = '[ERROR]';
2133
+ // eslint-disable-next-line no-console
2134
+ console.error('Failed to generate content for cell ' + column.name, err);
2135
+ }
2136
+ }
2137
+
2138
+ if (content === undefined || content === null) {
2139
+ content = '';
2140
+ }
2141
+
2142
+ return content;
2143
+ }
2144
+
2145
+ /**
2146
+ * Returns the y pos of a row by index
2147
+ * @public
2148
+ * @expose
2149
+ * @param {number} rowIndex - index of the row
2150
+ * @returns {number|null} Y pos
2151
+ */
2152
+ getRowYPos(rowIndex) {
2153
+ const p = this._p;
2154
+
2155
+ return p.virtualListHelper.getItemPosition(rowIndex) || null;
2156
+ }
2157
+
2158
+ /**
2159
+ * Returns the row data for a specific row
2160
+ * @public
2161
+ * @expose
2162
+ * @param {number} row index of the row
2163
+ * @returns {Object} Row data
2164
+ */
2165
+ getDataForRow(row) {
2166
+ const p = this._p;
2167
+
2168
+ if (row < 0 || row > p.rows.length - 1) return null;
2169
+ return p.rows[row];
2170
+ }
2171
+
2172
+ /**
2173
+ * Gets the number of rows
2174
+ * @public
2175
+ * @expose
2176
+ * @returns {number} Row count
2177
+ */
2178
+ getRowCount() {
2179
+ const p = this._p;
2180
+ return p.rows ? p.rows.length : 0;
2181
+ }
2182
+
2183
+ /**
2184
+ * Returns the actual row index for specific row (out of the full data set, not filtered)
2185
+ * @public
2186
+ * @expose
2187
+ * @param {Object} rowData - Row data to find
2188
+ * @returns {number} Row index
2189
+ */
2190
+ getIndexForRow(rowData) {
2191
+ const p = this._p;
2192
+ return p.rows.indexOf(rowData);
2193
+ }
2194
+
2195
+ /**
2196
+ * Gets the number of filtered rows
2197
+ * @public
2198
+ * @expose
2199
+ * @returns {number} Filtered row count
2200
+ */
2201
+ getFilteredRowCount() {
2202
+ const p = this._p;
2203
+ return (p.filteredRows || p.rows).length;
2204
+ }
2205
+
2206
+ /**
2207
+ * Returns the filtered row index for specific row
2208
+ * @public
2209
+ * @expose
2210
+ * @param {Object} rowData - Row data to find
2211
+ * @returns {number} Row index
2212
+ */
2213
+ getIndexForFilteredRow(rowData) {
2214
+ const p = this._p;
2215
+ return (p.filteredRows || p.rows).indexOf(rowData);
2216
+ }
2217
+
2218
+ /**
2219
+ * Returns the row data for a specific row
2220
+ * @public
2221
+ * @expose
2222
+ * @param {number} row index of the filtered row
2223
+ * @returns {Object} Row data
2224
+ */
2225
+ getDataForFilteredRow(row) {
2226
+ const p = this._p;
2227
+ if (row < 0 || row > (p.filteredRows || p.rows).length - 1) return null;
2228
+ return (p.filteredRows || p.rows)[row];
2229
+ }
2230
+
2231
+ /**
2232
+ * Returns DOM element of the header row
2233
+ * @public
2234
+ * @expose
2235
+ * @returns {Element} Row element
2236
+ */
2237
+ getHeaderRowElement() {
2238
+ return this._p.headerRow;
2239
+ }
2240
+
2241
+ /**
2242
+ * @private
2243
+ * @param {Element} el
2244
+ * @returns {number} width
2245
+ */
2246
+ _horizontalPadding(el) {
2247
+ const style = getComputedStyle(el);
2248
+ return (parseFloat(style.paddingLeft) || 0) + (
2249
+ parseFloat(style.paddingRight) || 0);
2250
+ }
2251
+
2252
+ /**
2253
+ * @private
2254
+ * @param {Element} el
2255
+ * @returns {number} width
2256
+ */
2257
+ _horizontalBorderWidth(el) {
2258
+ const style = getComputedStyle(el);
2259
+ return (parseFloat(style.borderLeftWidth) || 0) + (
2260
+ parseFloat(style.borderRightWidth) || 0);
2261
+ }
2262
+
2263
+ /**
2264
+ * @private
2265
+ * @returns {number} width
2266
+ */
2267
+ _calculateWidthAvailableForColumns() {
2268
+ const o = this._o,p = this._p;
2269
+
2270
+ // Changing display mode briefly, to prevent taking in account the parent's scrollbar width when we are the cause for it
2271
+ let oldDisplay, lastScrollTop, lastScrollLeft;
2272
+ if (p.table) {
2273
+ lastScrollTop = p.table ? p.table.scrollTop : 0;
2274
+ lastScrollLeft = p.table ? p.table.scrollLeft : 0;
2275
+
2276
+ if (o.virtualTable) {
2277
+ oldDisplay = p.table.style.display;
2278
+ p.table.style.display = 'none';
2279
+ }
2280
+ }
2281
+
2282
+ let detectedWidth = Css_js.getElementWidth(this.el);
2283
+
2284
+ if (p.table) {
2285
+ if (o.virtualTable) {
2286
+ p.table.style.display = oldDisplay;
2287
+ }
2288
+
2289
+ p.table.scrollTop = lastScrollTop;
2290
+ p.table.scrollLeft = lastScrollLeft;
2291
+ p.header.scrollLeft = lastScrollLeft;
2292
+ }
2293
+
2294
+ let tableClassName = o.tableClassName;
2295
+
2296
+ const thisWrapper = createElement('div');
2297
+ thisWrapper.className = this.el.className;
2298
+ Css_js.setCssProps(thisWrapper, {
2299
+ 'z-index': -1,
2300
+ 'position': 'absolute',
2301
+ left: '0',
2302
+ top: '-9999px'
2303
+ });
2304
+ let header = createElement('div');
2305
+ header.className = `${tableClassName}-header`;
2306
+ thisWrapper.appendChild(header);
2307
+ let headerRow = createElement('div');
2308
+ headerRow.className = `${tableClassName}-header-row`;
2309
+ header.appendChild(headerRow);
2310
+ for (let i = 0; i < p.visibleColumns.length; i++) {
2311
+ const column = p.visibleColumns[i];
2312
+ const cell = createElement('div');
2313
+ cell.className = `${tableClassName}-header-cell ${column.cellClasses || ''}`;
2314
+ cell['columnName'] = column.name;
2315
+ cell.appendChild(createElement('div'));
2316
+ headerRow.appendChild(cell);
2317
+ }
2318
+ document.body.appendChild(thisWrapper);
2319
+
2320
+ detectedWidth -= this._horizontalBorderWidth(headerRow);
2321
+
2322
+ let cells = DomCompat_js.scopedSelectorAll(headerRow, `>div.${tableClassName}-header-cell`);
2323
+ for (const cell of cells) {
2324
+ const cellStyle = getComputedStyle(cell);
2325
+ let isBoxing = cellStyle.boxSizing === 'border-box';
2326
+ if (!isBoxing) {
2327
+ detectedWidth -=
2328
+ (parseFloat(cellStyle.borderRightWidth) || 0) + (
2329
+ parseFloat(cellStyle.borderLeftWidth) || 0) +
2330
+ this._horizontalPadding(cell); // CELL's padding
2331
+
2332
+ const colName = cell['columnName'];
2333
+ const column = p.columns.get(colName);
2334
+ if (column)
2335
+ detectedWidth -= column.arrowProposedWidth || 0;
2336
+ }
2337
+ }
2338
+
2339
+ thisWrapper.remove();
2340
+
2341
+ return Math.max(0, detectedWidth);
2342
+ }
2343
+
2344
+ _getTextWidth(text) {
2345
+ let tableClassName = this._o.tableClassName;
2346
+
2347
+ const tableWrapper = createElement('div');
2348
+ tableWrapper.className = this.el.className;
2349
+ const header = createElement('div');
2350
+ header.className = tableClassName + '-header';
2351
+ const headerRow = createElement('div');
2352
+ headerRow.className = tableClassName + '-header-row';
2353
+ const cell = createElement('div');
2354
+ cell.className = tableClassName + '-header-cell';
2355
+ const cellContent = createElement('div');
2356
+ cellContent.textContent = text;
2357
+
2358
+ cell.appendChild(cellContent);
2359
+ headerRow.appendChild(cell);
2360
+ header.appendChild(headerRow);
2361
+ tableWrapper.appendChild(header);
2362
+ Css_js.setCssProps(tableWrapper, {
2363
+ position: 'absolute',
2364
+ top: '-9999px',
2365
+ visibility: 'hidden'
2366
+ });
2367
+
2368
+ document.body.appendChild(tableWrapper);
2369
+
2370
+ let width = Css_js.getElementWidth(cell);
2371
+
2372
+ tableWrapper.remove();
2373
+
2374
+ return width;
2375
+ }
2376
+
2377
+ /**
2378
+ * Notify the table that its width has changed
2379
+ * @public
2380
+ * @expose
2381
+ * @param {boolean} [forceUpdate=false]
2382
+ * @param {boolean} [renderColumns=true]
2383
+ * @returns {DGTable} self
2384
+ */
2385
+ tableWidthChanged(forceUpdate, renderColumns) {
2386
+
2387
+ let o = this._o,
2388
+ p = this._p,
2389
+ detectedWidth = this._calculateWidthAvailableForColumns(),
2390
+ sizeLeft = detectedWidth,
2391
+ relatives = 0;
2392
+
2393
+ if (!p.table) return this;
2394
+
2395
+ renderColumns = renderColumns === undefined || renderColumns;
2396
+
2397
+ let tableWidthBeforeCalculations = 0;
2398
+
2399
+ if (!p.tbody) {
2400
+ renderColumns = false;
2401
+ }
2402
+
2403
+ if (renderColumns) {
2404
+ tableWidthBeforeCalculations = parseFloat(p.tbody.style.minWidth) || 0;
2405
+ }
2406
+
2407
+ if (sizeLeft !== p.lastDetectedWidth || forceUpdate) {
2408
+ p.lastDetectedWidth = detectedWidth;
2409
+
2410
+ let absWidthTotal = 0,changedColumnIndexes = [],totalRelativePercentage = 0;
2411
+
2412
+ for (let i = 0; i < p.columns.length; i++) {
2413
+ p.columns[i].actualWidthConsideringScrollbarWidth = null;
2414
+ }
2415
+
2416
+ for (let i = 0; i < p.visibleColumns.length; i++) {
2417
+ let col = p.visibleColumns[i];
2418
+ if (col.widthMode === ColumnWidthMode.ABSOLUTE) {
2419
+ let width = col.width;
2420
+ width += col.arrowProposedWidth || 0; // Sort-arrow width
2421
+ if (!col.ignoreMin && width < o.minColumnWidth) {
2422
+ width = o.minColumnWidth;
2423
+ }
2424
+ sizeLeft -= width;
2425
+ absWidthTotal += width;
2426
+
2427
+ // Update actualWidth
2428
+ if (width !== col.actualWidth) {
2429
+ col.actualWidth = width;
2430
+ changedColumnIndexes.push(i);
2431
+ }
2432
+ } else if (col.widthMode === ColumnWidthMode.AUTO) {
2433
+ let width = this._getTextWidth(col.label) + 20;
2434
+ width += col.arrowProposedWidth || 0; // Sort-arrow width
2435
+ if (!col.ignoreMin && width < o.minColumnWidth) {
2436
+ width = o.minColumnWidth;
2437
+ }
2438
+ sizeLeft -= width;
2439
+ absWidthTotal += width;
2440
+
2441
+ // Update actualWidth
2442
+ if (width !== col.actualWidth) {
2443
+ col.actualWidth = width;
2444
+ if (!o.convertColumnWidthsToRelative) {
2445
+ changedColumnIndexes.push(i);
2446
+ }
2447
+ }
2448
+ } else if (col.widthMode === ColumnWidthMode.RELATIVE) {
2449
+ totalRelativePercentage += col.width;
2450
+ relatives++;
2451
+ }
2452
+ }
2453
+
2454
+ // Normalize relative sizes if needed
2455
+ if (o.convertColumnWidthsToRelative) {
2456
+ for (let i = 0; i < p.visibleColumns.length; i++) {
2457
+ let col = p.visibleColumns[i];
2458
+ if (col.widthMode === ColumnWidthMode.AUTO) {
2459
+ col.widthMode = ColumnWidthMode.RELATIVE;
2460
+ sizeLeft += col.actualWidth;
2461
+ col.width = col.actualWidth / absWidthTotal;
2462
+ totalRelativePercentage += col.width;
2463
+ relatives++;
2464
+ }
2465
+ }
2466
+ }
2467
+
2468
+ // Normalize relative sizes if needed
2469
+ if (relatives && (totalRelativePercentage < 1 && o.relativeWidthGrowsToFillWidth ||
2470
+ totalRelativePercentage > 1 && o.relativeWidthShrinksToFillWidth)) {
2471
+ for (let i = 0; i < p.visibleColumns.length; i++) {
2472
+ let col = p.visibleColumns[i];
2473
+ if (col.widthMode === ColumnWidthMode.RELATIVE) {
2474
+ col.width /= totalRelativePercentage;
2475
+ }
2476
+ }
2477
+ }
2478
+
2479
+ let sizeLeftForRelative = Math.max(0, sizeLeft); // Use this as the space to take the relative widths out of
2480
+ if (sizeLeftForRelative === 0) {
2481
+ sizeLeftForRelative = p.table.clientWidth;
2482
+ }
2483
+
2484
+ let minColumnWidthRelative = o.minColumnWidth / sizeLeftForRelative;
2485
+ if (isNaN(minColumnWidthRelative)) {
2486
+ minColumnWidthRelative = 0;
2487
+ }
2488
+ if (minColumnWidthRelative > 0) {
2489
+ let extraRelative = 0,delta;
2490
+
2491
+ // First pass - make sure they are all constrained to the minimum width
2492
+ for (let i = 0; i < p.visibleColumns.length; i++) {
2493
+ let col = p.visibleColumns[i];
2494
+ if (col.widthMode === ColumnWidthMode.RELATIVE) {
2495
+ if (!col.ignoreMin && col.width < minColumnWidthRelative) {
2496
+ extraRelative += minColumnWidthRelative - col.width;
2497
+ col.width = minColumnWidthRelative;
2498
+ }
2499
+ }
2500
+ }
2501
+
2502
+ // Second pass - try to take the extra width out of the other columns to compensate
2503
+ for (let i = 0; i < p.visibleColumns.length; i++) {
2504
+ let col = p.visibleColumns[i];
2505
+ if (col.widthMode === ColumnWidthMode.RELATIVE) {
2506
+ if (!col.ignoreMin && col.width > minColumnWidthRelative) {
2507
+ if (extraRelative > 0) {
2508
+ delta = Math.min(extraRelative, col.width - minColumnWidthRelative);
2509
+ col.width -= delta;
2510
+ extraRelative -= delta;
2511
+ }
2512
+ }
2513
+ }
2514
+ }
2515
+ }
2516
+
2517
+ // Try to fill width
2518
+ if (o.autoFillTableWidth && sizeLeft > 0) {
2519
+ let nonResizableTotal = 0;
2520
+ let sizeLeftToFill = sizeLeft;
2521
+
2522
+ for (let i = 0; i < p.visibleColumns.length; i++) {
2523
+ let col = p.visibleColumns[i];
2524
+ if (!col.resizable && col.widthMode === ColumnWidthMode.ABSOLUTE)
2525
+ nonResizableTotal += col.width;
2526
+
2527
+ if (col.widthMode === ColumnWidthMode.RELATIVE)
2528
+ sizeLeftToFill -= Math.round(sizeLeftForRelative * col.width);
2529
+ }
2530
+
2531
+ let conv = (detectedWidth - nonResizableTotal) / (detectedWidth - sizeLeftToFill - nonResizableTotal) || NaN;
2532
+ for (let i = 0; i < p.visibleColumns.length && sizeLeftToFill > 0; i++) {
2533
+ let col = p.visibleColumns[i];
2534
+ if (!col.resizable && col.widthMode === ColumnWidthMode.ABSOLUTE)
2535
+ continue;
2536
+
2537
+ if (col.widthMode === ColumnWidthMode.RELATIVE) {
2538
+ col.width *= conv;
2539
+ } else {
2540
+ let width = col.actualWidth * conv;
2541
+ if (col.actualWidth !== width) {
2542
+ col.actualWidth = width;
2543
+ if (changedColumnIndexes.indexOf(i) === -1)
2544
+ changedColumnIndexes.push(i);
2545
+ }
2546
+ }
2547
+ }
2548
+ }
2549
+
2550
+ // Materialize relative sizes
2551
+ for (let i = 0; i < p.visibleColumns.length; i++) {
2552
+ let col = p.visibleColumns[i];
2553
+ if (col.widthMode === ColumnWidthMode.RELATIVE) {
2554
+ let width = Math.round(sizeLeftForRelative * col.width);
2555
+ sizeLeft -= width;
2556
+ relatives--;
2557
+
2558
+ // Take care of rounding errors
2559
+ if (relatives === 0 && sizeLeft === 1) {// Take care of rounding errors
2560
+ width++;
2561
+ sizeLeft--;
2562
+ }
2563
+ if (sizeLeft === -1) {
2564
+ width--;
2565
+ sizeLeft++;
2566
+ }
2567
+
2568
+ // Update actualWidth
2569
+ if (width !== col.actualWidth) {
2570
+ col.actualWidth = width;
2571
+ changedColumnIndexes.push(i);
2572
+ }
2573
+ }
2574
+ }
2575
+
2576
+ if (p.visibleColumns.length) {
2577
+ // (There should always be at least 1 column visible, but just in case)
2578
+ p.visibleColumns[p.visibleColumns.length - 1].actualWidthConsideringScrollbarWidth =
2579
+ p.visibleColumns[p.visibleColumns.length - 1].actualWidth - (p.scrollbarWidth || 0);
2580
+ }
2581
+
2582
+ p.notifyRendererOfColumnsConfig?.();
2583
+
2584
+ if (renderColumns) {
2585
+ let tableWidth = this._calculateTbodyWidth();
2586
+
2587
+ if (tableWidthBeforeCalculations < tableWidth) {
2588
+ this._updateTableWidth(false);
2589
+ }
2590
+
2591
+ for (let i = 0; i < changedColumnIndexes.length; i++) {
2592
+ this._resizeColumnElements(changedColumnIndexes[i]);
2593
+ }
2594
+
2595
+ if (tableWidthBeforeCalculations > tableWidth) {
2596
+ this._updateTableWidth(false);
2597
+ }
2598
+ }
2599
+ }
2600
+
2601
+ return this;
2602
+ }
2603
+
2604
+ /**
2605
+ * Notify the table that its height has changed
2606
+ * @public
2607
+ * @expose
2608
+ * @returns {DGTable} self
2609
+ */
2610
+ tableHeightChanged() {
2611
+ let o = this._o,
2612
+ p = this._p;
2613
+
2614
+ if (!p.table) {
2615
+ return this;
2616
+ }
2617
+
2618
+ const tableStyle = getComputedStyle(p.table);
2619
+
2620
+ let height = Css_js.getElementHeight(this.el, true) - (
2621
+ parseFloat(tableStyle.borderTopWidth) || 0) // Subtract top border of inner element
2622
+ - (parseFloat(tableStyle.borderBottomWidth) || 0); // Subtract bottom border of inner element
2623
+
2624
+ if (height !== o.height) {
2625
+
2626
+ o.height = height;
2627
+
2628
+ if (p.tbody) {
2629
+ // At least 1 pixel - to show scrollbars correctly.
2630
+ p.tbody.style.height = Math.max(o.height - Css_js.getElementHeight(p.header, true, true, true), 1) + 'px';
2631
+ }
2632
+
2633
+ if (o.virtualTable) {
2634
+ this.clearAndRender();
2635
+ }
2636
+ }
2637
+
2638
+ return this;
2639
+ }
2640
+
2641
+ /**
2642
+ * Add rows to the table
2643
+ * @public
2644
+ * @expose
2645
+ * @param {Object[]} data - array of rows to add to the table
2646
+ * @param {number} [at=-1] - where to add the rows at
2647
+ * @param {boolean} [resort=false] - should resort all rows?
2648
+ * @param {boolean} [render=true]
2649
+ * @returns {DGTable} self
2650
+ */
2651
+ addRows(data, at, resort, render) {
2652
+ let p = this._p;
2653
+
2654
+ if (typeof at === 'boolean') {
2655
+ render = resort;
2656
+ resort = at;
2657
+ at = -1;
2658
+ }
2659
+
2660
+ if (typeof at !== 'number')
2661
+ at = -1;
2662
+
2663
+ if (at < 0 || at > p.rows.length)
2664
+ at = p.rows.length;
2665
+
2666
+ render = render === undefined ? true : !!render;
2667
+
2668
+ if (data) {
2669
+ p.rows.add(data, at);
2670
+
2671
+ if (p.filteredRows || resort && p.rows.sortColumn.length) {
2672
+
2673
+ if (resort && p.rows.sortColumn.length) {
2674
+ this.resort();
2675
+ } else {
2676
+ this._refilter();
2677
+ }
2678
+
2679
+ p.tableSkeletonNeedsRendering = true;
2680
+
2681
+ if (render) {
2682
+ // Render the skeleton with all rows from scratch
2683
+ this.render();
2684
+ }
2685
+
2686
+ } else if (render) {
2687
+ p.virtualListHelper.addItemsAt(data.length, at);
2688
+
2689
+ if (this._o.virtualTable) {
2690
+ this._updateVirtualHeight().
2691
+ _updateLastCellWidthFromScrollbar() // Detect vertical scrollbar height
2692
+ .render().
2693
+ _updateTableWidth(false); // Update table width to suit the required width considering vertical scrollbar
2694
+
2695
+ } else if (p.tbody) {
2696
+ this.render().
2697
+ _updateLastCellWidthFromScrollbar() // Detect vertical scrollbar height, and update existing last cells
2698
+ ._updateTableWidth(true); // Update table width to suit the required width considering vertical scrollbar
2699
+ }
2700
+ }
2701
+
2702
+ this.emit('addrows', { count: data.length, clear: false });
2703
+ }
2704
+ return this;
2705
+ }
2706
+
2707
+ /**
2708
+ * Removes a row from the table
2709
+ * @public
2710
+ * @expose
2711
+ * @param {number} rowIndex - index
2712
+ * @param {number} count - how many rows to remove
2713
+ * @param {boolean=true} render
2714
+ * @returns {DGTable} self
2715
+ */
2716
+ removeRows(rowIndex, count, render) {
2717
+ let p = this._p;
2718
+
2719
+ if (typeof count !== 'number' || count <= 0) return this;
2720
+
2721
+ if (rowIndex < 0 || rowIndex > p.rows.length - 1) return this;
2722
+
2723
+ p.rows.splice(rowIndex, count);
2724
+ render = render === undefined ? true : !!render;
2725
+
2726
+ if (p.filteredRows) {
2727
+ this._refilter();
2728
+
2729
+ p.tableSkeletonNeedsRendering = true;
2730
+
2731
+ if (render) {
2732
+ // Render the skeleton with all rows from scratch
2733
+ this.render();
2734
+ }
2735
+
2736
+ } else if (render) {
2737
+ p.virtualListHelper.removeItemsAt(count, rowIndex);
2738
+
2739
+ if (this._o.virtualTable) {
2740
+ this._updateVirtualHeight().
2741
+ _updateLastCellWidthFromScrollbar().
2742
+ render().
2743
+ _updateTableWidth(false); // Update table width to suit the required width considering vertical scrollbar
2744
+ } else {
2745
+ this.render().
2746
+ _updateLastCellWidthFromScrollbar().
2747
+ _updateTableWidth(true); // Update table width to suit the required width considering vertical scrollbar
2748
+ }
2749
+ }
2750
+
2751
+ return this;
2752
+ }
2753
+
2754
+ /**
2755
+ * Removes a row from the table
2756
+ * @public
2757
+ * @expose
2758
+ * @param {number} rowIndex - index
2759
+ * @param {boolean=true} render
2760
+ * @returns {DGTable} self
2761
+ */
2762
+ removeRow(rowIndex, render) {
2763
+ return this.removeRows(rowIndex, 1, render);
2764
+ }
2765
+
2766
+ /**
2767
+ * Refreshes the row specified
2768
+ * @public
2769
+ * @expose
2770
+ * @param {number} rowIndex index
2771
+ * @param {boolean} render should render the changes immediately?
2772
+ * @returns {DGTable} self
2773
+ */
2774
+ refreshRow(rowIndex) {let render = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
2775
+ let p = this._p;
2776
+
2777
+ if (rowIndex < 0 || rowIndex > p.rows.length - 1)
2778
+ return this;
2779
+
2780
+ // Find out if the row is in the rendered dataset
2781
+ let filteredRowIndex = -1;
2782
+ if (p.filteredRows && (filteredRowIndex = p.filteredRows.indexOf(p.rows[rowIndex])) === -1)
2783
+ return this;
2784
+
2785
+ if (filteredRowIndex === -1) {
2786
+ filteredRowIndex = rowIndex;
2787
+ }
2788
+
2789
+ p.virtualListHelper.refreshItemAt(filteredRowIndex);
2790
+
2791
+ if (render)
2792
+ p.virtualListHelper.render();
2793
+
2794
+ return this;
2795
+ }
2796
+
2797
+ /**
2798
+ * Get the DOM element for the specified row, if it exists
2799
+ * @public
2800
+ * @expose
2801
+ * @param {number} rowIndex index
2802
+ * @returns {Element|null} row or null
2803
+ */
2804
+ getRowElement(rowIndex) {
2805
+ let p = this._p;
2806
+
2807
+ if (rowIndex < 0 || rowIndex > p.rows.length - 1)
2808
+ return null;
2809
+
2810
+ // Find out if the row is in the rendered dataset
2811
+ let filteredRowIndex = -1;
2812
+ if (p.filteredRows && (filteredRowIndex = p.filteredRows.indexOf(p.rows[rowIndex])) === -1)
2813
+ return null;
2814
+
2815
+ if (filteredRowIndex === -1) {
2816
+ filteredRowIndex = rowIndex;
2817
+ }
2818
+
2819
+ return p.virtualListHelper.getItemElementAt(filteredRowIndex) || null;
2820
+ }
2821
+
2822
+ /**
2823
+ * Refreshes all virtual rows
2824
+ * @public
2825
+ * @expose
2826
+ * @returns {DGTable} self
2827
+ */
2828
+ refreshAllVirtualRows() {
2829
+ const p = this._p;
2830
+ p.virtualListHelper.invalidate().render();
2831
+ return this;
2832
+ }
2833
+
2834
+ /**
2835
+ * Replace the whole dataset
2836
+ * @public
2837
+ * @expose
2838
+ * @param {Object[]} data array of rows to add to the table
2839
+ * @param {boolean} [resort=false] should resort all rows?
2840
+ * @returns {DGTable} self
2841
+ */
2842
+ setRows(data, resort) {
2843
+ let p = this._p;
2844
+
2845
+ // this.scrollTop = this.$el.find('.table').scrollTop();
2846
+ p.rows.reset(data);
2847
+
2848
+ if (resort && p.rows.sortColumn.length) {
2849
+ this.resort();
2850
+ } else {
2851
+ this._refilter();
2852
+ }
2853
+
2854
+ this.clearAndRender().trigger('addrows', { count: data.length, clear: true });
2855
+
2856
+ return this;
2857
+ }
2858
+
2859
+ /**
2860
+ * Creates a URL representing the data in the specified element.
2861
+ * This uses the Blob or BlobBuilder of the modern browsers.
2862
+ * The url can be used for a Web Worker.
2863
+ * @public
2864
+ * @expose
2865
+ * @param {string} id Id of the element containing your data
2866
+ * @returns {string|null} the url, or null if not supported
2867
+ */
2868
+ getUrlForElementContent(id) {
2869
+ let blob,
2870
+ el = document.getElementById(id);
2871
+ if (el) {
2872
+ let data = el.textContent;
2873
+ if (typeof Blob === 'function') {
2874
+ blob = new Blob([data]);
2875
+ } else {
2876
+ let BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
2877
+ if (!BlobBuilder) {
2878
+ return null;
2879
+ }
2880
+ let builder = new BlobBuilder();
2881
+ builder.append(data);
2882
+ blob = builder.getBlob();
2883
+ }
2884
+ return (window.URL || window.webkitURL).createObjectURL(blob);
2885
+ }
2886
+ return null;
2887
+ }
2888
+
2889
+ /**
2890
+ * @public
2891
+ * @expose
2892
+ * @returns {boolean} A value indicating whether Web Workers are supported
2893
+ */
2894
+ isWorkerSupported() {
2895
+ return window['Worker'] instanceof Function;
2896
+ }
2897
+
2898
+ /**
2899
+ * Creates a Web Worker for updating the table.
2900
+ * @public
2901
+ * @expose
2902
+ * @param {string} url Url to the script for the Web Worker
2903
+ * @param {boolean} [start=true] if true, starts the Worker immediately
2904
+ * @param {boolean} [resort=false]
2905
+ * @returns {Worker|null} the Web Worker, or null if not supported
2906
+ */
2907
+ createWebWorker(url, start, resort) {
2908
+ if (this.isWorkerSupported()) {
2909
+ let p = this._p;
2910
+
2911
+ let worker = new Worker(url);
2912
+ let listener = (evt) => {
2913
+ if (evt.data.append) {
2914
+ this.addRows(evt.data.rows, resort);
2915
+ } else {
2916
+ this.setRows(evt.data.rows, resort);
2917
+ }
2918
+ };
2919
+ worker.addEventListener('message', listener, false);
2920
+ if (!p.workerListeners) {
2921
+ p.workerListeners = [];
2922
+ }
2923
+ p.workerListeners.push({ worker: worker, listener: listener });
2924
+ if (start || start === undefined) {
2925
+ worker.postMessage(null);
2926
+ }
2927
+ return worker;
2928
+ }
2929
+ return null;
2930
+ }
2931
+
2932
+ /**
2933
+ * Unbinds a Web Worker from the table, stopping updates.
2934
+ * @public
2935
+ * @expose
2936
+ * @param {Worker} worker the Web Worker
2937
+ * @returns {DGTable} self
2938
+ */
2939
+ unbindWebWorker(worker) {
2940
+ let p = this._p;
2941
+
2942
+ if (p.workerListeners) {
2943
+ for (let j = 0; j < p.workerListeners.length; j++) {
2944
+ if (p.workerListeners[j].worker === worker) {
2945
+ worker.removeEventListener('message', p.workerListeners[j].listener, false);
2946
+ p.workerListeners.splice(j, 1);
2947
+ j--;
2948
+ }
2949
+ }
2950
+ }
2951
+
2952
+ return this;
2953
+ }
2954
+
2955
+ /**
2956
+ * A synonym for hideCellPreview()
2957
+ * @public
2958
+ * @expose
2959
+ * @returns {DGTable} self
2960
+ */
2961
+ abortCellPreview() {
2962
+ this.hideCellPreview();
2963
+ return this;
2964
+ }
2965
+
2966
+ /**
2967
+ * Cancel a resize in progress
2968
+ * @expose
2969
+ * @private
2970
+ * @returns {DGTable} self
2971
+ */
2972
+ cancelColumnResize() {
2973
+ const p = this._p;
2974
+
2975
+ if (p.resizer) {
2976
+ p.resizer.remove();
2977
+ p.resizer = null;
2978
+ p.eventsSink.remove(document, '.colresize');
2979
+ }
2980
+
2981
+ return this;
2982
+ }
2983
+
2984
+ _onTableScrolledHorizontally() {
2985
+ const p = this._p;
2986
+
2987
+ p.header.scrollLeft = p.table.scrollLeft;
2988
+ }
2989
+
2990
+ /**previousElementSibling
2991
+ * Reverse-calculate the column to resize from mouse position
2992
+ * @private
2993
+ * @param {MouseEvent} event mouse event
2994
+ * @returns {string|null} name of the column which the mouse is over, or null if the mouse is not in resize position
2995
+ */
2996
+ _getColumnByResizePosition(event) {
2997
+ let o = this._o,
2998
+ rtl = this._isTableRtl();
2999
+
3000
+ let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
3001
+ if (headerCell[OriginalCellSymbol]) {
3002
+ headerCell = headerCell[OriginalCellSymbol];
3003
+ }
3004
+
3005
+ let previousElementSibling = headerCell.previousSibling;
3006
+ while (previousElementSibling && previousElementSibling.nodeType !== 1) {
3007
+ previousElementSibling = previousElementSibling.previousSibling;
3008
+ }
3009
+
3010
+ let firstCol = !previousElementSibling;
3011
+
3012
+ let mouseX = (event.pageX || event.clientX) - Css_js.getElementOffset(headerCell).left;
3013
+
3014
+ if (rtl) {
3015
+ if (!firstCol && Css_js.getElementWidth(headerCell, true, true, true) - mouseX <= o.resizeAreaWidth / 2) {
3016
+ return previousElementSibling['columnName'];
3017
+ } else if (mouseX <= o.resizeAreaWidth / 2) {
3018
+ return headerCell['columnName'];
3019
+ }
3020
+ } else {
3021
+ if (!firstCol && mouseX <= o.resizeAreaWidth / 2) {
3022
+ return previousElementSibling['columnName'];
3023
+ } else if (Css_js.getElementWidth(headerCell, true, true, true) - mouseX <= o.resizeAreaWidth / 2) {
3024
+ return headerCell['columnName'];
3025
+ }
3026
+ }
3027
+
3028
+ return null;
3029
+ }
3030
+
3031
+ /**
3032
+ * @param {TouchEvent} event
3033
+ */
3034
+ _onTouchStartColumnHeader(event) {
3035
+ const p = this._p;
3036
+
3037
+ if (p.currentTouchId) return;
3038
+
3039
+ let startTouch = event.changedTouches[0];
3040
+ p.currentTouchId = startTouch.identifier;
3041
+
3042
+ let cellEl = event.currentTarget;
3043
+
3044
+ let startPos = { x: startTouch.pageX, y: startTouch.pageY },
3045
+ currentPos = startPos,
3046
+ distanceTreshold = 9;
3047
+
3048
+ let tapAndHoldTimeout;
3049
+
3050
+ let unbind = function () {
3051
+ p.currentTouchId = null;
3052
+ p.eventsSink.remove(cellEl, '.colheader');
3053
+ clearTimeout(tapAndHoldTimeout);
3054
+ };
3055
+
3056
+ let fakeMouseEvent = function (name) {
3057
+ for (const k of event)
3058
+ event[k];for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {args[_key - 1] = arguments[_key];}
3059
+
3060
+ for (const obj of args) {
3061
+ for (const key of ['target', 'clientX', 'clientY', 'offsetX', 'offsetY', 'screenX', 'screenY', 'pageX', 'pageY', 'which']) {
3062
+ if (obj[key] != null)
3063
+ obj[key];
3064
+ }
3065
+ }
3066
+
3067
+ return new MouseEvent(name, event);
3068
+ };
3069
+
3070
+ cellEl.dispatchEvent(
3071
+ fakeMouseEvent('mousedown', event.changedTouches[0], { button: 0, target: event.target })
3072
+ );
3073
+
3074
+ tapAndHoldTimeout = setTimeout(() => {
3075
+ unbind();
3076
+
3077
+ p.eventsSink.
3078
+ add(cellEl, 'touchend.colheader', (event) => {
3079
+ // Prevent simulated mouse events after touchend
3080
+ if (!isInputElementEvent(event))
3081
+ event.preventDefault();
3082
+
3083
+ p.eventsSink.remove(cellEl, '.colheader');
3084
+ }, { once: true }).
3085
+ add(cellEl, 'touchcancel.colheader', (_event) => {
3086
+ p.eventsSink.remove(cellEl, '.colheader');
3087
+ }, { once: true });
3088
+
3089
+ let distanceTravelled = Math.sqrt(Math.pow(Math.abs(currentPos.x - startPos.x), 2) + Math.pow(Math.abs(currentPos.y - startPos.y), 2));
3090
+
3091
+ if (distanceTravelled < distanceTreshold) {
3092
+ this.cancelColumnResize();
3093
+
3094
+ cellEl.dispatchEvent(
3095
+ fakeMouseEvent('mouseup', event.changedTouches[0], { button: 2, target: event.target })
3096
+ );
3097
+ }
3098
+
3099
+ }, 500);
3100
+
3101
+ p.eventsSink.
3102
+ add(cellEl, 'touchend.colheader', (event) => {
3103
+ let touch = find(event.changedTouches, (touch) => touch.identifier === p.currentTouchId);
3104
+ if (!touch) return;
3105
+
3106
+ unbind();
3107
+
3108
+ // Prevent simulated mouse events after touchend
3109
+ if (!isInputElementEvent(event))
3110
+ event.preventDefault();
3111
+
3112
+ currentPos = { x: touch.pageX, y: touch.pageY };
3113
+ let distanceTravelled = Math.sqrt(Math.pow(Math.abs(currentPos.x - startPos.x), 2) + Math.pow(Math.abs(currentPos.y - startPos.y), 2));
3114
+
3115
+ if (distanceTravelled < distanceTreshold || p.resizer) {
3116
+ cellEl.dispatchEvent(
3117
+ fakeMouseEvent('mouseup', touch, { 0: 2, target: event.target })
3118
+ );
3119
+
3120
+ cellEl.dispatchEvent(
3121
+ fakeMouseEvent('click', touch, { button: 0, target: event.target })
3122
+ );
3123
+ }
3124
+
3125
+ }).
3126
+ add(cellEl, 'touchcancel.colheader', unbind).
3127
+ add(cellEl, 'touchmove.colheader', (event) => {
3128
+ let touch = find(event.changedTouches, (touch) => touch.identifier === p.currentTouchId);
3129
+ if (!touch) return;
3130
+
3131
+ // Keep track of current position, so we know if we need to cancel the tap-and-hold
3132
+ currentPos = { x: touch.pageX, y: touch.pageY };
3133
+
3134
+ if (p.resizer) {
3135
+ event.preventDefault();
3136
+
3137
+ cellEl.dispatchEvent(
3138
+ fakeMouseEvent('mousemove', touch, { target: event.target })
3139
+ );
3140
+ }
3141
+ });
3142
+ }
3143
+
3144
+ /**
3145
+ * @param {MouseEvent} event
3146
+ */
3147
+ _onMouseDownColumnHeader(event) {
3148
+ if (event.button !== 0) return this; // Only treat left-clicks
3149
+
3150
+ let o = this._o,
3151
+ p = this._p,
3152
+ col = this._getColumnByResizePosition(event);
3153
+
3154
+ if (col) {
3155
+ let column = p.columns.get(col);
3156
+ if (!o.resizableColumns || !column || !column.resizable) {
3157
+ return false;
3158
+ }
3159
+
3160
+ let rtl = this._isTableRtl();
3161
+
3162
+ if (p.resizer) {
3163
+ p.resizer.remove();
3164
+ }
3165
+ p.resizer = createElement('div');
3166
+ p.resizer.className = o.resizerClassName;
3167
+ Css_js.setCssProps(p.resizer, {
3168
+ position: 'absolute',
3169
+ display: 'block',
3170
+ zIndex: -1,
3171
+ visibility: 'hidden',
3172
+ width: '2px',
3173
+ background: '#000',
3174
+ opacity: 0.7
3175
+ });
3176
+ this.el.appendChild(p.resizer);
3177
+
3178
+ let selectedHeaderCell = column.element,
3179
+ commonAncestor = p.resizer.parentNode;
3180
+
3181
+ const commonAncestorStyle = getComputedStyle(commonAncestor);
3182
+ const selectedHeaderCellStyle = getComputedStyle(selectedHeaderCell);
3183
+
3184
+ let posCol = Css_js.getElementOffset(selectedHeaderCell),
3185
+ posRelative = Css_js.getElementOffset(commonAncestor);
3186
+ posRelative.left += parseFloat(commonAncestorStyle.borderLeftWidth) || 0;
3187
+ posRelative.top += parseFloat(commonAncestorStyle.borderTopWidth) || 0;
3188
+ posCol.left -= posRelative.left;
3189
+ posCol.top -= posRelative.top;
3190
+ posCol.top -= parseFloat(selectedHeaderCellStyle.borderTopWidth) || 0;
3191
+ let resizerWidth = Css_js.getElementWidth(p.resizer, true, true, true);
3192
+ if (rtl) {
3193
+ posCol.left -= Math.ceil((parseFloat(selectedHeaderCellStyle.borderLeftWidth) || 0) / 2);
3194
+ posCol.left -= Math.ceil(resizerWidth / 2);
3195
+ } else {
3196
+ posCol.left += Css_js.getElementWidth(selectedHeaderCell, true, true, true);
3197
+ posCol.left += Math.ceil((parseFloat(selectedHeaderCellStyle.borderRightWidth) || 0) / 2);
3198
+ posCol.left -= Math.ceil(resizerWidth / 2);
3199
+ }
3200
+
3201
+ Css_js.setCssProps(p.resizer, {
3202
+ 'z-index': '10',
3203
+ 'visibility': 'visible',
3204
+ 'left': posCol.left + 'px',
3205
+ 'top': posCol.top + 'px',
3206
+ 'height': Css_js.getElementHeight(this.el, false, false, false) + 'px'
3207
+ });
3208
+ p.resizer['columnName'] = selectedHeaderCell['columnName'];
3209
+
3210
+ try {p.resizer.style.zIndex = '';}
3211
+ catch (ignored) {/* we're ok with this */}
3212
+
3213
+ p.eventsSink.
3214
+ add(document, 'mousemove.colresize', this._onMouseMoveResizeArea.bind(this)).
3215
+ add(document, 'mouseup.colresize', this._onEndDragColumnHeader.bind(this));
3216
+
3217
+ event.preventDefault();
3218
+ }
3219
+ }
3220
+
3221
+ /**
3222
+ * @param {MouseEvent} event event
3223
+ */
3224
+ _onMouseMoveColumnHeader(event) {
3225
+ let o = this._o,
3226
+ p = this._p;
3227
+
3228
+ if (o.resizableColumns) {
3229
+ let col = this._getColumnByResizePosition(event);
3230
+ let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
3231
+ if (!col || !p.columns.get(col).resizable) {
3232
+ headerCell.style.cursor = '';
3233
+ } else {
3234
+ headerCell.style.cursor = 'e-resize';
3235
+ }
3236
+ }
3237
+ }
3238
+
3239
+ /**
3240
+ * @param {MouseEvent} event
3241
+ */
3242
+ _onMouseUpColumnHeader(event) {
3243
+ if (event.button === 2) {
3244
+ let o = this._o;
3245
+ let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
3246
+ let bounds = Css_js.getElementOffset(headerCell);
3247
+ bounds['width'] = Css_js.getElementWidth(headerCell, true, true, true);
3248
+ bounds['height'] = Css_js.getElementHeight(headerCell, true, true, true);
3249
+ this.emit('headercontextmenu', {
3250
+ name: headerCell['columnName'],
3251
+ pageX: event.pageX,
3252
+ pageY: event.pageY,
3253
+ bounds: bounds
3254
+ });
3255
+ }
3256
+ return this;
3257
+ }
3258
+
3259
+ /**
3260
+ * @private
3261
+ * @param {MouseEvent} event event
3262
+ */
3263
+ _onMouseLeaveColumnHeader(event) {
3264
+ let o = this._o;
3265
+ let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
3266
+ headerCell.style.cursor = '';
3267
+ }
3268
+
3269
+ /**
3270
+ * @private
3271
+ * @param {MouseEvent} event event
3272
+ */
3273
+ _onClickColumnHeader(event) {
3274
+ if (isInputElementEvent(event))
3275
+ return;
3276
+
3277
+ if (!this._getColumnByResizePosition(event)) {
3278
+ let o = this._o,
3279
+ p = this._p;
3280
+
3281
+ let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
3282
+ if (o.sortableColumns) {
3283
+ let column = p.columns.get(headerCell['columnName']);
3284
+ let currentSort = p.rows.sortColumn;
3285
+ if (column && column.sortable) {
3286
+ let shouldAdd = true;
3287
+
3288
+ let lastSort = currentSort.length ? currentSort[currentSort.length - 1] : null;
3289
+
3290
+ if (lastSort && lastSort.column === column.name) {
3291
+ if (!lastSort.descending || !o.allowCancelSort) {
3292
+ lastSort.descending = !lastSort.descending;
3293
+ } else {
3294
+ shouldAdd = false;
3295
+ currentSort.splice(currentSort.length - 1, 1);
3296
+ }
3297
+ }
3298
+
3299
+ if (shouldAdd) {
3300
+ this.sort(column.name, undefined, true).render();
3301
+ } else {
3302
+ this.sort(); // just refresh current situation
3303
+ }
3304
+ }
3305
+ }
3306
+ }
3307
+ }
3308
+
3309
+ /**
3310
+ * @private
3311
+ * @param {DragEvent} event event
3312
+ */
3313
+ _onStartDragColumnHeader(event) {
3314
+ let o = this._o,
3315
+ p = this._p;
3316
+
3317
+ if (o.movableColumns) {
3318
+ let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
3319
+ let column = p.columns.get(headerCell['columnName']);
3320
+ if (column && column.movable) {
3321
+ headerCell.style.opacity = 0.35;
3322
+ p.dragId = Math.random() * 0x9999999; // Recognize this ID on drop
3323
+ event.dataTransfer.setData('text', JSON.stringify({ dragId: p.dragId, column: column.name }));
3324
+ } else {
3325
+ event.preventDefault();
3326
+ }
3327
+ } else {
3328
+ event.preventDefault();
3329
+ }
3330
+
3331
+ return undefined;
3332
+ }
3333
+
3334
+ /**
3335
+ * @private
3336
+ * @param {MouseEvent} event event
3337
+ */
3338
+ _onMouseMoveResizeArea(event) {
3339
+
3340
+ let p = this._p;
3341
+
3342
+ let column = p.columns.get(p.resizer['columnName']);
3343
+ let rtl = this._isTableRtl();
3344
+
3345
+ let selectedHeaderCell = column.element,
3346
+ commonAncestor = p.resizer.parentNode;
3347
+
3348
+ const commonAncestorStyle = getComputedStyle(commonAncestor);
3349
+ const selectedHeaderCellStyle = getComputedStyle(selectedHeaderCell);
3350
+
3351
+ let posCol = Css_js.getElementOffset(selectedHeaderCell),
3352
+ posRelative = Css_js.getElementOffset(commonAncestor);
3353
+ posRelative.left += parseFloat(commonAncestorStyle.borderLeftWidth) || 0;
3354
+ posCol.left -= posRelative.left;
3355
+ let resizerWidth = Css_js.getElementWidth(p.resizer, true, true, true);
3356
+
3357
+ let isBoxing = selectedHeaderCellStyle.boxSizing === 'border-box';
3358
+
3359
+ let actualX = event.pageX - posRelative.left;
3360
+ let minX = posCol.left;
3361
+
3362
+ minX -= Math.ceil(resizerWidth / 2);
3363
+
3364
+ if (rtl) {
3365
+ minX += Css_js.getElementWidth(selectedHeaderCell, true, true, true);
3366
+ minX -= column.ignoreMin ? 0 : this._o.minColumnWidth;
3367
+
3368
+ if (!isBoxing) {
3369
+ minX -= Math.ceil((parseFloat(selectedHeaderCellStyle.borderLeftWidth) || 0) / 2);
3370
+ minX -= this._horizontalPadding(selectedHeaderCell);
3371
+ }
3372
+
3373
+ if (actualX > minX) {
3374
+ actualX = minX;
3375
+ }
3376
+ } else {
3377
+ minX += column.ignoreMin ? 0 : this._o.minColumnWidth;
3378
+
3379
+ if (!isBoxing) {
3380
+ minX += Math.ceil((parseFloat(selectedHeaderCellStyle.borderRightWidth) || 0) / 2);
3381
+ minX += this._horizontalPadding(selectedHeaderCell);
3382
+ }
3383
+
3384
+ if (actualX < minX) {
3385
+ actualX = minX;
3386
+ }
3387
+ }
3388
+
3389
+ p.resizer.style.left = actualX + 'px';
3390
+ }
3391
+
3392
+ /**
3393
+ * @private
3394
+ * @param {DragEvent} event event
3395
+ */
3396
+ _onEndDragColumnHeader(event) {
3397
+
3398
+ let o = this._o,
3399
+ p = this._p;
3400
+
3401
+ if (!p.resizer) {
3402
+ event.target.style.opacity = null;
3403
+ } else {
3404
+ p.eventsSink.remove(document, '.colresize');
3405
+
3406
+ let column = p.columns.get(p.resizer['columnName']);
3407
+ let rtl = this._isTableRtl();
3408
+
3409
+ let selectedHeaderCell = column.element,
3410
+ selectedHeaderCellInner = selectedHeaderCell.firstChild,
3411
+ commonAncestor = p.resizer.parentNode;
3412
+
3413
+ const commonAncestorStyle = getComputedStyle(commonAncestor);
3414
+ const selectedHeaderCellStyle = getComputedStyle(selectedHeaderCell);
3415
+
3416
+ let posCol = Css_js.getElementOffset(selectedHeaderCell),
3417
+ posRelative = Css_js.getElementOffset(commonAncestor);
3418
+ posRelative.left += parseFloat(commonAncestorStyle.borderLeftWidth) || 0;
3419
+ posCol.left -= posRelative.left;
3420
+ let resizerWidth = Css_js.getElementWidth(p.resizer, true, true, true);
3421
+
3422
+ let isBoxing = selectedHeaderCellStyle.boxSizing === 'border-box';
3423
+
3424
+ let actualX = event.pageX - posRelative.left;
3425
+ let baseX = posCol.left,minX = posCol.left;
3426
+ let width = 0;
3427
+
3428
+ baseX -= Math.ceil(resizerWidth / 2);
3429
+
3430
+ if (rtl) {
3431
+ if (!isBoxing) {
3432
+ actualX += this._horizontalPadding(selectedHeaderCell);
3433
+ const innerComputedStyle = getComputedStyle(selectedHeaderCellInner || selectedHeaderCell);
3434
+ actualX += parseFloat(innerComputedStyle.borderLeftWidth) || 0;
3435
+ actualX += parseFloat(innerComputedStyle.borderRightWidth) || 0;
3436
+ actualX += column.arrowProposedWidth || 0; // Sort-arrow width
3437
+ }
3438
+
3439
+ baseX += Css_js.getElementWidth(selectedHeaderCell, true, true, true);
3440
+
3441
+ minX = baseX - (column.ignoreMin ? 0 : this._o.minColumnWidth);
3442
+ if (actualX > minX) {
3443
+ actualX = minX;
3444
+ }
3445
+
3446
+ width = baseX - actualX;
3447
+ } else {
3448
+ if (!isBoxing) {
3449
+ actualX -= this._horizontalPadding(selectedHeaderCell);
3450
+ const innerComputedStyle = getComputedStyle(selectedHeaderCellInner || selectedHeaderCell);
3451
+ actualX -= parseFloat(innerComputedStyle.borderLeftWidth) || 0;
3452
+ actualX -= parseFloat(innerComputedStyle.borderRightWidth) || 0;
3453
+ actualX -= column.arrowProposedWidth || 0; // Sort-arrow width
3454
+ }
3455
+
3456
+ minX = baseX + (column.ignoreMin ? 0 : this._o.minColumnWidth);
3457
+ if (actualX < minX) {
3458
+ actualX = minX;
3459
+ }
3460
+
3461
+ width = actualX - baseX;
3462
+ }
3463
+
3464
+ p.resizer.remove();
3465
+ p.resizer = null;
3466
+
3467
+ let sizeToSet = width;
3468
+
3469
+ if (column.widthMode === ColumnWidthMode.RELATIVE) {
3470
+ let sizeLeft = this._calculateWidthAvailableForColumns();
3471
+ //sizeLeft -= p.table.offsetWidth - p.table.clientWidth;
3472
+
3473
+ let totalRelativePercentage = 0;
3474
+ let relatives = 0;
3475
+
3476
+ for (let i = 0; i < p.visibleColumns.length; i++) {
3477
+ let col = p.visibleColumns[i];
3478
+ if (col.name === column.name) continue;
3479
+
3480
+ if (col.widthMode === ColumnWidthMode.RELATIVE) {
3481
+ totalRelativePercentage += col.width;
3482
+ relatives++;
3483
+ } else {
3484
+ sizeLeft -= col.actualWidth;
3485
+ }
3486
+ }
3487
+
3488
+ sizeLeft = Math.max(1, sizeLeft);
3489
+ if (sizeLeft === 1)
3490
+ sizeLeft = p.table.clientWidth;
3491
+ sizeToSet = width / sizeLeft;
3492
+
3493
+ if (relatives > 0) {
3494
+ // When there's more than one relative overall,
3495
+ // we can do relative enlarging/shrinking.
3496
+ // Otherwise, we can end up having a 0 width.
3497
+
3498
+ let unNormalizedSizeToSet = sizeToSet / ((1 - sizeToSet) / totalRelativePercentage);
3499
+
3500
+ totalRelativePercentage += sizeToSet;
3501
+
3502
+ // Account for relative widths scaling later
3503
+ if (totalRelativePercentage < 1 && o.relativeWidthGrowsToFillWidth ||
3504
+ totalRelativePercentage > 1 && o.relativeWidthShrinksToFillWidth) {
3505
+ sizeToSet = unNormalizedSizeToSet;
3506
+ }
3507
+ }
3508
+
3509
+ sizeToSet *= 100;
3510
+ sizeToSet += '%';
3511
+ }
3512
+
3513
+ this.setColumnWidth(column.name, sizeToSet);
3514
+ }
3515
+ }
3516
+
3517
+ /**
3518
+ * @private
3519
+ * @param {DragEvent} event event
3520
+ */
3521
+ _onDragEnterColumnHeader(event) {
3522
+ let o = this._o,
3523
+ p = this._p;
3524
+
3525
+ if (o.movableColumns) {
3526
+ let dataTransferred = event.dataTransfer.getData('text');
3527
+ if (dataTransferred) {
3528
+ dataTransferred = JSON.parse(dataTransferred);
3529
+ } else
3530
+ {
3531
+ dataTransferred = null; // WebKit does not provide the dataTransfer on dragenter?..
3532
+ }
3533
+
3534
+ let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
3535
+ if (!dataTransferred ||
3536
+ p.dragId === dataTransferred.dragId && headerCell['columnName'] !== dataTransferred.column) {
3537
+
3538
+ let column = p.columns.get(headerCell['columnName']);
3539
+ if (column && (column.movable || column !== p.visibleColumns[0])) {
3540
+ headerCell.classList.add('drag-over');
3541
+ }
3542
+ }
3543
+ }
3544
+ }
3545
+
3546
+ /**
3547
+ * @private
3548
+ * @param {DragEvent} event event
3549
+ */
3550
+ _onDragOverColumnHeader(event) {
3551
+ event.preventDefault();
3552
+ }
3553
+
3554
+ /**
3555
+ * @private
3556
+ * @param {DragEvent} event event
3557
+ */
3558
+ _onDragLeaveColumnHeader(event) {
3559
+ let o = this._o;
3560
+ let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
3561
+ if (!event.relatedTarget.contains(headerCell.firstChild)) {
3562
+ headerCell.classList.remove('drag-over');
3563
+ }
3564
+ }
3565
+
3566
+ /**
3567
+ * @private
3568
+ * @param {DragEvent} event event
3569
+ */
3570
+ _onDropColumnHeader(event) {
3571
+ event.preventDefault();
3572
+
3573
+ let o = this._o,
3574
+ p = this._p;
3575
+
3576
+ let dataTransferred = JSON.parse(event.dataTransfer.getData('text'));
3577
+ let headerCell = event.target.closest(`div.${o.tableClassName}-header-cell,div.${o.cellPreviewClassName}`);
3578
+ if (o.movableColumns && dataTransferred.dragId === p.dragId) {
3579
+ let srcColName = dataTransferred.column,
3580
+ destColName = headerCell['columnName'],
3581
+ srcCol = p.columns.get(srcColName),
3582
+ destCol = p.columns.get(destColName);
3583
+ if (srcCol && destCol && srcCol.movable && (destCol.movable || destCol !== p.visibleColumns[0])) {
3584
+ this.moveColumn(srcColName, destColName);
3585
+ }
3586
+ }
3587
+ headerCell.classList.remove('drag-over');
3588
+ }
3589
+
3590
+ /**
3591
+ * @private
3592
+ * @returns {DGTable} self
3593
+ */
3594
+ _clearSortArrows() {
3595
+ let p = this._p;
3596
+
3597
+ if (p.table) {
3598
+ let tableClassName = this._o.tableClassName;
3599
+ let sortedColumns = DomCompat_js.scopedSelectorAll(p.headerRow, `>div.${tableClassName}-header-cell.sorted`);
3600
+ let arrows = Array.prototype.slice.call(sortedColumns, 0).map((el) => DomCompat_js.scopedSelector(el, '>div>.sort-arrow')).filter((el) => !!el);
3601
+ for (const arrow of arrows) {
3602
+ let col = p.columns.get(arrow.parentNode.parentNode['columnName']);
3603
+ if (col) {
3604
+ col.arrowProposedWidth = 0;
3605
+ }
3606
+ arrow.remove();
3607
+ }
3608
+ for (const sortedColumn of sortedColumns) {
3609
+ sortedColumn.classList.remove('sorted', 'desc');
3610
+ }
3611
+ }
3612
+ return this;
3613
+ }
3614
+
3615
+ /**
3616
+ * @private
3617
+ * @param {string} column the name of the sort column
3618
+ * @param {boolean} descending table is sorted descending
3619
+ * @returns {boolean} self
3620
+ */
3621
+ _showSortArrow(column, descending) {
3622
+ let p = this._p;
3623
+
3624
+ let col = p.columns.get(column);
3625
+ if (!col) return false;
3626
+
3627
+ let arrow = createElement('span');
3628
+ arrow.className = 'sort-arrow';
3629
+
3630
+ if (col.element) {
3631
+ col.element.className += descending ? ' sorted desc' : ' sorted';
3632
+ col.element.firstChild.insertBefore(arrow, col.element.firstChild.firstChild);
3633
+ }
3634
+
3635
+ if (col.widthMode !== ColumnWidthMode.RELATIVE && this._o.adjustColumnWidthForSortArrow) {
3636
+ col.arrowProposedWidth = arrow.scrollWidth + (
3637
+ parseFloat(getComputedStyle(arrow).marginRight) || 0) + (
3638
+ parseFloat(getComputedStyle(arrow).marginLeft) || 0);
3639
+ }
3640
+
3641
+ return true;
3642
+ }
3643
+
3644
+ /**
3645
+ * @private
3646
+ * @param {number} cellIndex index of the column in the DOM
3647
+ * @returns {DGTable} self
3648
+ */
3649
+ _resizeColumnElements(cellIndex) {
3650
+ let p = this._p;
3651
+
3652
+ const headerCells = p.headerRow.querySelectorAll(`div.${this._o.tableClassName}-header-cell`);
3653
+ const headerCell = headerCells[cellIndex];
3654
+ let col = p.columns.get(headerCell['columnName']);
3655
+
3656
+ if (col) {
3657
+ headerCell.style.width = (col.actualWidthConsideringScrollbarWidth || col.actualWidth) + 'px';
3658
+
3659
+ let width = (col.actualWidthConsideringScrollbarWidth || col.actualWidth) + 'px';
3660
+ let tbodyChildren = p.tbody.childNodes;
3661
+ for (let i = 0, count = tbodyChildren.length; i < count; i++) {
3662
+ let rowEl = tbodyChildren[i];
3663
+ if (rowEl.nodeType !== 1) continue;
3664
+ rowEl.childNodes[cellIndex].style.width = width;
3665
+ }
3666
+ }
3667
+
3668
+ return this;
3669
+ }
3670
+
3671
+ /**
3672
+ * @returns {DGTable} self
3673
+ * */
3674
+ _destroyHeaderCells() {
3675
+ let p = this._p;
3676
+
3677
+ if (p.headerRow) {
3678
+ p.headerRow = null;
3679
+ }
3680
+ return this;
3681
+ }
3682
+
3683
+ /**
3684
+ * @private
3685
+ * @returns {DGTable} self
3686
+ */
3687
+ _renderSkeletonBase() {
3688
+ let p = this._p,
3689
+ o = this._o;
3690
+
3691
+ // Clean up old elements
3692
+
3693
+ p.virtualListHelper?.destroy();
3694
+ p.virtualListHelper = null;
3695
+
3696
+ if (p.table && o.virtualTable) {
3697
+ p.table.remove();
3698
+ p.table = p.tbody = null;
3699
+ }
3700
+
3701
+ this._destroyHeaderCells();
3702
+ p.currentTouchId = null;
3703
+ if (p.header) {
3704
+ p.header.remove();
3705
+ }
3706
+
3707
+ // Create new base elements
3708
+ let tableClassName = o.tableClassName,
3709
+ header = createElement('div'),
3710
+ headerRow = createElement('div');
3711
+
3712
+ header.className = `${tableClassName}-header`;
3713
+ headerRow.className = `${tableClassName}-header-row`;
3714
+
3715
+ p.header = header;
3716
+ p.headerRow = headerRow;
3717
+ header.appendChild(headerRow);
3718
+ this.el.prepend(header);
3719
+
3720
+ relativizeElement(this.el);
3721
+
3722
+ if (o.width === DGTable.Width.SCROLL) {
3723
+ this.el.style.overflow = 'hidden';
3724
+ } else {
3725
+ this.el.style.overflow = '';
3726
+ }
3727
+
3728
+ if (!o.height && o.virtualTable) {
3729
+ o.height = Css_js.getElementHeight(this.el, true);
3730
+ }
3731
+
3732
+ return this;
3733
+ }
3734
+
3735
+ _bindHeaderColumnEvents(columnEl) {
3736
+ const inner = columnEl.firstChild;
3737
+ columnEl.addEventListener('mousedown', this._onMouseDownColumnHeader.bind(this));
3738
+ columnEl.addEventListener('mousemove', this._onMouseMoveColumnHeader.bind(this));
3739
+ columnEl.addEventListener('mouseup', this._onMouseUpColumnHeader.bind(this));
3740
+ columnEl.addEventListener('mouseleave', this._onMouseLeaveColumnHeader.bind(this));
3741
+ columnEl.addEventListener('touchstart', this._onTouchStartColumnHeader.bind(this));
3742
+ columnEl.addEventListener('dragstart', this._onStartDragColumnHeader.bind(this));
3743
+ columnEl.addEventListener('click', this._onClickColumnHeader.bind(this));
3744
+ columnEl.addEventListener('contextmenu', (event) => {event.preventDefault();});
3745
+ inner.addEventListener('dragenter', this._onDragEnterColumnHeader.bind(this));
3746
+ inner.addEventListener('dragover', this._onDragOverColumnHeader.bind(this));
3747
+ inner.addEventListener('dragleave', this._onDragLeaveColumnHeader.bind(this));
3748
+ inner.addEventListener('drop', this._onDropColumnHeader.bind(this));
3749
+ }
3750
+
3751
+ /**
3752
+ * @private
3753
+ * @returns {DGTable} self
3754
+ */
3755
+ _renderSkeletonHeaderCells() {
3756
+ let p = this._p,
3757
+ o = this._o;
3758
+
3759
+ let allowCellPreview = o.allowCellPreview,
3760
+ allowHeaderCellPreview = o.allowHeaderCellPreview;
3761
+
3762
+ let tableClassName = o.tableClassName,
3763
+ headerCellClassName = tableClassName + '-header-cell',
3764
+ headerRow = p.headerRow;
3765
+
3766
+ // Create header cells
3767
+ for (let i = 0; i < p.visibleColumns.length; i++) {
3768
+ let column = p.visibleColumns[i];
3769
+ if (column.visible) {
3770
+ let cell = createElement('div');
3771
+ cell.draggable = true;
3772
+ cell.className = headerCellClassName;
3773
+ cell.style.width = column.actualWidth + 'px';
3774
+ if (o.sortableColumns && column.sortable) {
3775
+ cell.className += ' sortable';
3776
+ }
3777
+ cell['columnName'] = column.name;
3778
+ cell.setAttribute('data-column', column.name);
3779
+
3780
+ let cellInside = createElement('div');
3781
+ cellInside.innerHTML = o.headerCellFormatter(column.label, column.name);
3782
+ cell.appendChild(cellInside);
3783
+ if (allowCellPreview && allowHeaderCellPreview) {
3784
+ p._bindCellHoverIn(cell);
3785
+ }
3786
+ headerRow.appendChild(cell);
3787
+
3788
+ p.visibleColumns[i].element = cell;
3789
+
3790
+ this._bindHeaderColumnEvents(cell);
3791
+ this._disableCssSelect(cell);
3792
+ }
3793
+ }
3794
+
3795
+ this.emit('headerrowcreate', headerRow);
3796
+
3797
+ return this;
3798
+ }
3799
+
3800
+ /**
3801
+ * @private
3802
+ * @returns {DGTable} self
3803
+ */
3804
+ _renderSkeletonBody() {
3805
+ let p = this._p,
3806
+ o = this._o;
3807
+
3808
+ let tableClassName = o.tableClassName;
3809
+
3810
+ // Calculate virtual row heights
3811
+ if (o.virtualTable && !p.virtualRowHeight) {
3812
+ let createDummyRow = () => {
3813
+ let row = createElement('div'),
3814
+ cell = row.appendChild(createElement('div')),
3815
+ cellInner = cell.appendChild(createElement('div'));
3816
+ row.className = `${tableClassName}-row`;
3817
+ cell.className = `${tableClassName}-cell`;
3818
+ cellInner.innerHTML = '0';
3819
+ row.style.visibility = 'hidden';
3820
+ row.style.position = 'absolute';
3821
+ return row;
3822
+ };
3823
+
3824
+ const dummyWrapper = createElement('div');
3825
+ dummyWrapper.className = this.el.className;
3826
+ Css_js.setCssProps(dummyWrapper, {
3827
+ 'z-index': -1,
3828
+ 'position': 'absolute',
3829
+ 'left': '0',
3830
+ 'top': '-9999px',
3831
+ 'width': '1px',
3832
+ 'overflow': 'hidden'
3833
+ });
3834
+
3835
+ const dummyTable = createElement('div');
3836
+ dummyTable.className = tableClassName;
3837
+ dummyWrapper.appendChild(dummyTable);
3838
+
3839
+ const dummyTbody = createElement('div');
3840
+ dummyTbody.className = `${tableClassName}-body`;
3841
+ dummyTbody.style.width = '99999px';
3842
+ dummyTable.appendChild(dummyTbody);
3843
+
3844
+ document.body.appendChild(dummyWrapper);
3845
+
3846
+ let row1 = createDummyRow(),row2 = createDummyRow(),row3 = createDummyRow();
3847
+ dummyTbody.appendChild(row1);
3848
+ dummyTbody.appendChild(row2);
3849
+ dummyTbody.appendChild(row3);
3850
+
3851
+ p.virtualRowHeightFirst = Css_js.getElementHeight(row1, true, true, true);
3852
+ p.virtualRowHeight = Css_js.getElementHeight(row2, true, true, true);
3853
+ p.virtualRowHeightLast = Css_js.getElementHeight(row3, true, true, true);
3854
+
3855
+ dummyWrapper.remove();
3856
+ }
3857
+
3858
+ // Create inner table and tbody
3859
+ if (!p.table) {
3860
+ let fragment = document.createDocumentFragment();
3861
+
3862
+ // Create the inner table element
3863
+ let table = createElement('div');
3864
+ table.className = tableClassName;
3865
+
3866
+ if (o.virtualTable) {
3867
+ table.className += ' virtual';
3868
+ }
3869
+
3870
+ const tableStyle = getComputedStyle(table);
3871
+
3872
+ let tableHeight = o.height - Css_js.getElementHeight(p.header, true, true, true);
3873
+ if (tableStyle.boxSizing !== 'border-box') {
3874
+ tableHeight -= parseFloat(tableStyle.borderTopWidth) || 0;
3875
+ tableHeight -= parseFloat(tableStyle.borderBottomWidth) || 0;
3876
+ tableHeight -= parseFloat(tableStyle.paddingTop) || 0;
3877
+ tableHeight -= parseFloat(tableStyle.paddingBottom) || 0;
3878
+ }
3879
+ p.visibleHeight = tableHeight;
3880
+ Css_js.setCssProps(table, {
3881
+ height: o.height ? tableHeight + 'px' : 'auto',
3882
+ display: 'block',
3883
+ overflowY: 'auto',
3884
+ overflowX: o.width === DGTable.Width.SCROLL ? 'auto' : 'hidden'
3885
+ });
3886
+ fragment.appendChild(table);
3887
+
3888
+ // Create the "tbody" element
3889
+ let tbody = createElement('div');
3890
+ tbody.className = o.tableClassName + '-body';
3891
+ tbody.style.minHeight = '1px';
3892
+ p.table = table;
3893
+ p.tbody = tbody;
3894
+
3895
+ relativizeElement(tbody);
3896
+ relativizeElement(table);
3897
+
3898
+ table.appendChild(tbody);
3899
+ this.el.appendChild(fragment);
3900
+
3901
+ this._setupVirtualTable();
3902
+ }
3903
+
3904
+ return this;
3905
+ }
3906
+
3907
+ /**
3908
+ * @private
3909
+ * @returns {DGTable} self
3910
+ * @deprecated
3911
+ */
3912
+ _renderSkeleton() {
3913
+ return this;
3914
+ }
3915
+
3916
+ /**
3917
+ * @private
3918
+ * @returns {DGTable} self
3919
+ */
3920
+ _updateVirtualHeight() {
3921
+ const o = this._o,p = this._p;
3922
+
3923
+ if (!p.tbody)
3924
+ return this;
3925
+
3926
+ if (o.virtualTable) {
3927
+ const virtualHeight = p.virtualListHelper.estimateFullHeight();
3928
+ p.lastVirtualScrollHeight = virtualHeight;
3929
+ p.tbody.style.height = virtualHeight + 'px';
3930
+ } else {
3931
+ p.tbody.style.height = '';
3932
+ }
3933
+
3934
+ return this;
3935
+ }
3936
+
3937
+ /**
3938
+ * @private
3939
+ * @returns {DGTable} self
3940
+ */
3941
+ _updateLastCellWidthFromScrollbar(force) {
3942
+
3943
+ const p = this._p;
3944
+
3945
+ // Calculate scrollbar's width and reduce from lat column's width
3946
+ let scrollbarWidth = p.table.offsetWidth - p.table.clientWidth;
3947
+ if (scrollbarWidth !== p.scrollbarWidth || force) {
3948
+ p.scrollbarWidth = scrollbarWidth;
3949
+ for (let i = 0; i < p.columns.length; i++) {
3950
+ p.columns[i].actualWidthConsideringScrollbarWidth = null;
3951
+ }
3952
+
3953
+ if (p.scrollbarWidth > 0 && p.visibleColumns.length > 0) {
3954
+ // (There should always be at least 1 column visible, but just in case)
3955
+ let lastColIndex = p.visibleColumns.length - 1;
3956
+
3957
+ p.visibleColumns[lastColIndex].actualWidthConsideringScrollbarWidth = p.visibleColumns[lastColIndex].actualWidth - p.scrollbarWidth;
3958
+ let lastColWidth = p.visibleColumns[lastColIndex].actualWidthConsideringScrollbarWidth + 'px';
3959
+ let tbodyChildren = p.tbody.childNodes;
3960
+ for (let i = 0, count = tbodyChildren.length; i < count; i++) {
3961
+ let row = tbodyChildren[i];
3962
+ if (row.nodeType !== 1) continue;
3963
+ row.childNodes[lastColIndex].style.width = lastColWidth;
3964
+ }
3965
+
3966
+ p.headerRow.childNodes[lastColIndex].style.width = lastColWidth;
3967
+ }
3968
+
3969
+ p.notifyRendererOfColumnsConfig?.();
3970
+ }
3971
+
3972
+ return this;
3973
+ }
3974
+
3975
+ /**
3976
+ * Explicitly set the width of the table based on the sum of the column widths
3977
+ * @private
3978
+ * @param {boolean} parentSizeMayHaveChanged Parent size may have changed, treat rendering accordingly
3979
+ * @returns {DGTable} self
3980
+ */
3981
+ _updateTableWidth(parentSizeMayHaveChanged) {
3982
+ const o = this._o,p = this._p;
3983
+ let width = this._calculateTbodyWidth();
3984
+
3985
+ p.tbody.style.minWidth = width + 'px';
3986
+ p.headerRow.style.minWidth = width + (p.scrollbarWidth || 0) + 'px';
3987
+
3988
+ p.eventsSink.remove(p.table, 'scroll');
3989
+
3990
+ if (o.width === DGTable.Width.AUTO) {
3991
+ // Update wrapper element's size to fully contain the table body
3992
+
3993
+ Css_js.setElementWidth(p.table, Css_js.getElementWidth(p.tbody, true, true, true));
3994
+ Css_js.setElementWidth(this.el, Css_js.getElementWidth(p.table, true, true, true));
3995
+
3996
+ } else if (o.width === DGTable.Width.SCROLL) {
3997
+
3998
+ if (parentSizeMayHaveChanged) {
3999
+ let lastScrollTop = p.table ? p.table.scrollTop : 0,
4000
+ lastScrollLeft = p.table ? p.table.scrollLeft : 0;
4001
+
4002
+ // BUGFIX: Relayout before recording the widths
4003
+ webkitRenderBugfix(this.el);
4004
+
4005
+ p.table.crollTop = lastScrollTop;
4006
+ p.table.scrollLeft = lastScrollLeft;
4007
+ p.header.scrollLeft = lastScrollLeft;
4008
+ }
4009
+
4010
+ p.eventsSink.add(p.table, 'scroll', this._onTableScrolledHorizontally.bind(this));
4011
+ }
4012
+
4013
+ return this;
4014
+ }
4015
+
4016
+ /**
4017
+ * @private
4018
+ * @returns {boolean}
4019
+ */
4020
+ _isTableRtl() {
4021
+ return getComputedStyle(this._p.table).direction === 'rtl';
4022
+ }
4023
+
4024
+ /**
4025
+ * @private
4026
+ * @param {Object} column column object
4027
+ * @returns {string}
4028
+ */
4029
+ _serializeColumnWidth(column) {
4030
+ return column.widthMode === ColumnWidthMode.AUTO ? 'auto' :
4031
+ column.widthMode === ColumnWidthMode.RELATIVE ? column.width * 100 + '%' :
4032
+ column.width;
4033
+ }
4034
+
4035
+ /**
4036
+ * @private
4037
+ * @param {HTMLElement} el
4038
+ */
4039
+ _disableCssSelect(el) {
4040
+ const style = el.style;
4041
+ // Disable these to allow our own context menu events without interruption
4042
+ style['-webkit-touch-callout'] = 'none';
4043
+ style['-webkit-user-select'] = 'none';
4044
+ style['-moz-user-select'] = 'none';
4045
+ style['-ms-user-select'] = 'none';
4046
+ style['-o-user-select'] = 'none';
4047
+ style['user-select'] = 'none';
4048
+ }
4049
+
4050
+ /**
4051
+ * @private
4052
+ * @param {HTMLElement} el
4053
+ */
4054
+ _cellMouseOverEvent(el) {
4055
+ const o = this._o,p = this._p;
4056
+
4057
+ let elInner = el.firstElementChild;
4058
+
4059
+ if (!(elInner.scrollWidth - elInner.clientWidth > 1 ||
4060
+ elInner.scrollHeight - elInner.clientHeight > 1))
4061
+ return;
4062
+
4063
+ this.hideCellPreview();
4064
+ p.abortCellPreview = false;
4065
+
4066
+ const rowEl = el.parentElement;
4067
+ const previewCell = createElement('div');
4068
+ previewCell.innerHTML = el.innerHTML;
4069
+ previewCell.className = o.cellPreviewClassName;
4070
+
4071
+ let isHeaderCell = el.classList.contains(`${o.tableClassName}-header-cell`);
4072
+ if (isHeaderCell) {
4073
+ previewCell.classList.add('header');
4074
+ if (el.classList.contains('sortable')) {
4075
+ previewCell.classList.add('sortable');
4076
+ }
4077
+
4078
+ previewCell.draggable = true;
4079
+
4080
+ this._bindHeaderColumnEvents(previewCell);
4081
+ }
4082
+
4083
+ const elStyle = getComputedStyle(el);
4084
+ const elInnerStyle = getComputedStyle(elInner);
4085
+
4086
+ let paddingL = parseFloat(elStyle.paddingLeft) || 0,
4087
+ paddingR = parseFloat(elStyle.paddingRight) || 0,
4088
+ paddingT = parseFloat(elStyle.paddingTop) || 0,
4089
+ paddingB = parseFloat(elStyle.paddingBottom) || 0;
4090
+
4091
+ let requiredWidth = elInner.scrollWidth + (el.clientWidth - elInner.offsetWidth);
4092
+
4093
+ let borderBox = elStyle.boxSizing === 'border-box';
4094
+ if (borderBox) {
4095
+ previewCell.style.boxSizing = 'border-box';
4096
+ } else {
4097
+ requiredWidth -= paddingL + paddingR;
4098
+ previewCell.style.marginTop = (parseFloat(elStyle.borderTopWidth) || 0) + 'px';
4099
+ }
4100
+
4101
+ if (!p.transparentBgColor1) {
4102
+ // Detect browser's transparent spec
4103
+ let tempDiv = document.createElement('div');
4104
+ document.body.appendChild(tempDiv);
4105
+ tempDiv.style.backgroundColor = 'transparent';
4106
+ p.transparentBgColor1 = getComputedStyle(tempDiv).backgroundColor;
4107
+ tempDiv.style.backgroundColor = 'rgba(0,0,0,0)';
4108
+ p.transparentBgColor2 = getComputedStyle(tempDiv).backgroundColor;
4109
+ tempDiv.remove();
4110
+ }
4111
+
4112
+ let css = {
4113
+ 'box-sizing': borderBox ? 'border-box' : 'content-box',
4114
+ 'width': requiredWidth,
4115
+ 'min-height': Math.max(Css_js.getElementHeight(el), parseFloat(elStyle.minHeight) || 0) + 'px',
4116
+ 'padding-left': paddingL,
4117
+ 'padding-right': paddingR,
4118
+ 'padding-top': paddingT,
4119
+ 'padding-bottom': paddingB,
4120
+ 'overflow': 'hidden',
4121
+ 'position': 'absolute',
4122
+ 'z-index': '-1',
4123
+ 'left': '0',
4124
+ 'top': '0',
4125
+ 'cursor': elStyle.cursor
4126
+ };
4127
+
4128
+ let bgColor = elStyle.backgroundColor;
4129
+ if (bgColor === p.transparentBgColor1 || bgColor === p.transparentBgColor2) {
4130
+ bgColor = getComputedStyle(rowEl).backgroundColor;
4131
+ }
4132
+ if (bgColor === p.transparentBgColor1 || bgColor === p.transparentBgColor2) {
4133
+ bgColor = '#fff';
4134
+ }
4135
+ css['background-color'] = bgColor;
4136
+
4137
+ Css_js.setCssProps(previewCell, css);
4138
+ Css_js.setCssProps(previewCell.firstChild, {
4139
+ 'direction': elInnerStyle.direction,
4140
+ 'white-space': elInnerStyle.whiteSpace,
4141
+ 'min-height': elInnerStyle.minHeight,
4142
+ 'line-height': elInnerStyle.lineHeight,
4143
+ 'font': elInnerStyle.font
4144
+ });
4145
+
4146
+ this.el.appendChild(previewCell);
4147
+
4148
+ if (isHeaderCell) {
4149
+ this._disableCssSelect(previewCell);
4150
+ }
4151
+
4152
+ previewCell['rowVIndex'] = rowEl['vIndex'];
4153
+ let rowIndex = previewCell['rowIndex'] = rowEl['index'];
4154
+ previewCell['columnName'] = p.visibleColumns[nativeIndexOf.call(rowEl.childNodes, el)].name;
4155
+
4156
+ try {
4157
+ let selection = SelectionHelper.saveSelection(el);
4158
+ if (selection)
4159
+ SelectionHelper.restoreSelection(previewCell, selection);
4160
+ } catch (ignored) {/* we're ok with this */}
4161
+
4162
+ this.emit(
4163
+ 'cellpreview', {
4164
+ el: previewCell.firstElementChild,
4165
+ name: previewCell['columnName'],
4166
+ rowIndex: rowIndex,
4167
+ rowData: rowIndex == null ? null : p.rows[rowIndex],
4168
+ cell: el,
4169
+ cellEl: elInner
4170
+ }
4171
+ );
4172
+
4173
+ if (p.abortCellPreview) {
4174
+ previewCell.remove();
4175
+ return;
4176
+ }
4177
+
4178
+ if (rowIndex != null) {
4179
+ previewCell.addEventListener('click', (event) => {
4180
+ this.emit('rowclick', {
4181
+ event: event,
4182
+ filteredRowIndex: rowEl['vIndex'],
4183
+ rowIndex: rowIndex,
4184
+ rowEl: rowEl,
4185
+ rowData: p.rows[rowIndex]
4186
+ });
4187
+ });
4188
+ }
4189
+
4190
+ let parent = this.el;
4191
+ let scrollParent = parent === window ? document : parent;
4192
+
4193
+ const parentStyle = getComputedStyle(parent);
4194
+
4195
+ let offset = Css_js.getElementOffset(el);
4196
+ let parentOffset = Css_js.getElementOffset(parent);
4197
+ let rtl = elStyle.float === 'right';
4198
+ let prop = rtl ? 'right' : 'left';
4199
+
4200
+ // Handle RTL, go from the other side
4201
+ if (rtl) {
4202
+ let windowWidth = window.innerWidth;
4203
+ offset.right = windowWidth - (offset.left + Css_js.getElementWidth(el, true, true, true));
4204
+ parentOffset.right = windowWidth - (parentOffset.left + Css_js.getElementWidth(parent, true, true, true));
4205
+ }
4206
+
4207
+ // If the parent has borders, then it would offset the offset...
4208
+ offset.left -= parseFloat(parentStyle.borderLeftWidth) || 0;
4209
+ if (prop === 'right')
4210
+ offset.right -= parseFloat(parentStyle.borderRightWidth) || 0;
4211
+ offset.top -= parseFloat(parentStyle.borderTopWidth) || 0;
4212
+
4213
+ // Handle border widths of the element being offset
4214
+ offset[prop] += parseFloat(elStyle[`border-${prop}-width`]) || 0;
4215
+ offset.top += parseFloat(elStyle.borderTopWidth) || parseFloat(elStyle.borderBottomWidth) || 0;
4216
+
4217
+ // Subtract offsets to get offset relative to parent
4218
+ offset.left -= parentOffset.left;
4219
+ if (prop === 'right')
4220
+ offset.right -= parentOffset.right;
4221
+ offset.top -= parentOffset.top;
4222
+
4223
+ // Constrain horizontally
4224
+ let minHorz = 0,
4225
+ maxHorz = Css_js.getElementWidth(parent, false, false, false) - Css_js.getElementWidth(previewCell, true, true, true);
4226
+ offset[prop] = offset[prop] < minHorz ?
4227
+ minHorz :
4228
+ offset[prop] > maxHorz ? maxHorz : offset[prop];
4229
+
4230
+ // Constrain vertically
4231
+ let totalHeight = Css_js.getElementHeight(el, true, true, true);
4232
+ let maxTop = scrollParent.scrollTop + Css_js.getElementHeight(parent, true) - totalHeight;
4233
+ if (offset.top > maxTop) {
4234
+ offset.top = Math.max(0, maxTop);
4235
+ }
4236
+
4237
+ // Apply css to preview cell
4238
+ let previewCss = {
4239
+ 'top': offset.top + 'px',
4240
+ 'z-index': 9999
4241
+ };
4242
+ previewCss[prop] = offset[prop] + 'px';
4243
+ Css_js.setCssProps(previewCell, previewCss);
4244
+
4245
+ previewCell[OriginalCellSymbol] = el;
4246
+ p.cellPreviewCell = previewCell;
4247
+ el[PreviewCellSymbol] = previewCell;
4248
+
4249
+ p._bindCellHoverOut(el);
4250
+ p._bindCellHoverOut(previewCell);
4251
+
4252
+ // Avoid interfering with wheel scrolling the table
4253
+ previewCell.addEventListener('wheel', () => {
4254
+ // Let the table naturally scroll with the wheel
4255
+ this.hideCellPreview();
4256
+ });
4257
+ }
4258
+
4259
+ /**
4260
+ * @private
4261
+ * @param {HTMLElement} _el
4262
+ */
4263
+ _cellMouseOutEvent(_el) {
4264
+ this.hideCellPreview();
4265
+ }
4266
+
4267
+ /**
4268
+ * Hides the current cell preview,
4269
+ * or prevents the one that is currently trying to show (in the 'cellpreview' event)
4270
+ * @public
4271
+ * @expose
4272
+ * @returns {DGTable} self
4273
+ */
4274
+ hideCellPreview() {
4275
+ const p = this._p;
4276
+
4277
+ if (p.cellPreviewCell) {
4278
+ let previewCell = p.cellPreviewCell;
4279
+ let origCell = previewCell[OriginalCellSymbol];
4280
+ let selection;
4281
+
4282
+ try {
4283
+ selection = SelectionHelper.saveSelection(previewCell);
4284
+ } catch (ignored) {/* we're ok with this */}
4285
+
4286
+ p.cellPreviewCell.remove();
4287
+ p._unbindCellHoverOut(origCell);
4288
+ p._unbindCellHoverOut(previewCell);
4289
+
4290
+ try {
4291
+ if (selection)
4292
+ SelectionHelper.restoreSelection(origCell, selection);
4293
+ } catch (ignored) {/* we're ok with this */}
4294
+
4295
+ this.emit('cellpreviewdestroy', {
4296
+ el: previewCell.firstChild,
4297
+ name: previewCell['columnName'],
4298
+ rowIndex: previewCell['rowIndex'],
4299
+ rowData: previewCell['rowIndex'] == null ? null : p.rows[previewCell['rowIndex']],
4300
+ cell: origCell,
4301
+ cellEl: origCell.firstChild
4302
+ });
4303
+
4304
+ delete origCell[PreviewCellSymbol];
4305
+ delete previewCell[OriginalCellSymbol];
4306
+
4307
+ p.cellPreviewCell = null;
4308
+ p.abortCellPreview = false;
4309
+ } else {
4310
+ p.abortCellPreview = true;
4311
+ }
4312
+
4313
+ return this;
4314
+ }
4315
+ }
4316
+
4317
+ /**
4318
+ * @public
4319
+ * @expose
4320
+ * @type {string}
4321
+ */
4322
+ DGTable.VERSION = '@@VERSION';
4323
+
4324
+ // It's a shame the Google Closure Compiler does not support exposing a nested @param
4325
+
4326
+ /**
4327
+ * @typedef {Object} SERIALIZED_COLUMN
4328
+ * @property {number|null|undefined} [order=0]
4329
+ * @property {string|null|undefined} [width='auto']
4330
+ * @property {boolean|null|undefined} [visible=true]
4331
+ * */
4332
+
4333
+ /**
4334
+ * @typedef {Object} SERIALIZED_COLUMN_SORT
4335
+ * @property {string|null|undefined} [column='']
4336
+ * @property {boolean|null|undefined} [descending=false]
4337
+ * */
4338
+
4339
+ /**
4340
+ * @enum {ColumnWidthMode|number|undefined}
4341
+ * @const
4342
+ * @typedef {ColumnWidthMode}
4343
+ */
4344
+ const ColumnWidthMode = {
4345
+ /** @const*/AUTO: 0,
4346
+ /** @const*/ABSOLUTE: 1,
4347
+ /** @const*/RELATIVE: 2
4348
+ };
4349
+
4350
+ /**
4351
+ * @enum {DGTable.Width|string|undefined}
4352
+ * @const
4353
+ * @typedef {DGTable.Width}
4354
+ */
4355
+ DGTable.Width = {
4356
+ /** @const*/NONE: 'none',
4357
+ /** @const*/AUTO: 'auto',
4358
+ /** @const*/SCROLL: 'scroll'
4359
+ };
4360
+
4361
+ return DGTable;
4362
+
4363
+ }));
4364
+
4365
+ //# sourceMappingURL=lib.umd.js.map