jquery.dgtable 0.6.19 → 2.0.1

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