canvas-datagrid 0.25.3 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/events.js DELETED
@@ -1,1741 +0,0 @@
1
- /*jslint browser: true, unparam: true, todo: true, plusplus: true*/
2
- /*globals define: true, MutationObserver: false, requestAnimationFrame: false, performance: false, btoa: false*/
3
- 'use strict';
4
-
5
- import isPrintableKeyEvent from 'is-printable-key-event';
6
-
7
- export default function (self) {
8
- var wheeling;
9
- self.stopPropagation = function (e) {
10
- e.stopPropagation();
11
- };
12
- /**
13
- * Adds an event listener to the given event.
14
- * @memberof canvasDatagrid
15
- * @name addEventListener
16
- * @method
17
- * @param {string} ev The name of the event to subscribe to.
18
- * @param {function} fn The event procedure to execute when the event is raised.
19
- */
20
- self.addEventListener = function (ev, fn) {
21
- self.events[ev] = self.events[ev] || [];
22
- self.events[ev].unshift(fn);
23
- };
24
- /**
25
- * Removes the given listener function from the given event. Must be an actual reference to the function that was bound.
26
- * @memberof canvasDatagrid
27
- * @name removeEventListener
28
- * @method
29
- * @param {string} ev The name of the event to unsubscribe from.
30
- * @param {function} fn The event procedure to execute when the event is raised.
31
- */
32
- self.removeEventListener = function (ev, fn) {
33
- (self.events[ev] || []).forEach(function removeEachListener(sfn, idx) {
34
- if (fn === sfn) {
35
- self.events[ev].splice(idx, 1);
36
- }
37
- });
38
- };
39
- /**
40
- * Fires the given event, passing an event object to the event subscribers.
41
- * @memberof canvasDatagrid
42
- * @name dispatchEvent
43
- * @method
44
- * @param {number} ev The name of the event to dispatch.
45
- * @param {number} e The event object.
46
- */
47
- self.dispatchEvent = function (ev, e) {
48
- e = ev.type ? ev : e || {};
49
- ev = ev.type || ev;
50
- var defaultPrevented;
51
- function preventDefault() {
52
- defaultPrevented = true;
53
- }
54
- if (!self.events[ev]) {
55
- return;
56
- }
57
- self.events[ev].forEach(function dispatchEachEvent(fn) {
58
- e.ctx = self.ctx;
59
- e.preventDefault = preventDefault;
60
- fn.apply(self.intf, [e]);
61
- });
62
- return defaultPrevented;
63
- };
64
- self.getRatio = function () {
65
- return Math.min(
66
- self.attributes.maxPixelRatio,
67
- (window.devicePixelRatio || 1) /
68
- (self.ctx.webkitBackingStorePixelRatio ||
69
- self.ctx.mozBackingStorePixelRatio ||
70
- self.ctx.msBackingStorePixelRatio ||
71
- self.ctx.oBackingStorePixelRatio ||
72
- self.ctx.backingStorePixelRatio ||
73
- 1),
74
- );
75
- };
76
- self.resize = function (drawAfterResize) {
77
- if (!self.canvas) {
78
- return;
79
- }
80
- var x,
81
- v = {
82
- x: 0,
83
- y: 0,
84
- height: 0,
85
- width: 0,
86
- style: 'vertical-scroll-bar',
87
- },
88
- n = {
89
- x: 0,
90
- y: 0,
91
- height: 0,
92
- width: 0,
93
- style: 'horizontal-scroll-bar',
94
- },
95
- vb = {
96
- x: 0,
97
- y: 0,
98
- height: 0,
99
- width: 0,
100
- style: 'vertical-scroll-box',
101
- },
102
- nb = {
103
- x: 0,
104
- y: 0,
105
- height: 0,
106
- width: 0,
107
- style: 'horizontal-scroll-box',
108
- },
109
- co = {
110
- x: 0,
111
- y: 0,
112
- height: 0,
113
- width: 0,
114
- isCorner: true,
115
- isScrollBoxCorner: true,
116
- style: 'scroll-box-corner',
117
- },
118
- m = self.style.scrollBarBoxMargin * 2,
119
- b = self.style.scrollBarBorderWidth * 2,
120
- d = self.style.scrollBarBoxMargin * 0.5,
121
- sbw = self.style.scrollBarWidth + self.style.scrollBarBorderWidth * 2,
122
- ratio = self.getRatio(),
123
- bm = self.style.gridBorderCollapse === 'collapse' ? 1 : 2,
124
- cellBorder = self.style.cellBorderWidth * bm,
125
- columnHeaderCellBorder = self.style.columnHeaderCellBorderWidth * bm,
126
- dataHeight = 0,
127
- dataWidth = 0,
128
- dims,
129
- l = (self.data || []).length,
130
- columnHeaderCellHeight = self.getColumnHeaderCellHeight(),
131
- rowHeaderCellWidth = self.getRowHeaderCellWidth(),
132
- ch = self.style.cellHeight,
133
- s = self.getSchema();
134
- // sets actual DOM canvas element
135
- function checkScrollBoxVisibility() {
136
- self.scrollBox.horizontalBarVisible =
137
- (self.style.width !== 'auto' &&
138
- dataWidth > self.scrollBox.width &&
139
- self.style.overflowX !== 'hidden') ||
140
- self.style.overflowX === 'scroll';
141
- self.scrollBox.horizontalBoxVisible = dataWidth > self.scrollBox.width;
142
- self.scrollBox.verticalBarVisible =
143
- (self.style.height !== 'auto' &&
144
- dataHeight > self.scrollBox.height &&
145
- self.style.overflowY !== 'hidden') ||
146
- self.style.overflowY === 'scroll';
147
- self.scrollBox.verticalBoxVisible = dataHeight > self.scrollBox.height;
148
- }
149
- function setScrollBoxSize() {
150
- self.scrollBox.width = self.width - rowHeaderCellWidth;
151
- self.scrollBox.height = self.height - columnHeaderCellHeight;
152
- }
153
- function setCanvasSize() {
154
- if (self.isChildGrid) {
155
- return;
156
- }
157
- dims = {
158
- // HACK +1 ? maybe it's a magic cell border? Required to line up properly in auto height mode.
159
- height: columnHeaderCellHeight + dataHeight + cellBorder + 1,
160
- width: dataWidth + rowHeaderCellWidth + cellBorder,
161
- };
162
- ['width', 'height'].forEach(function (dim) {
163
- //TODO: support inherit
164
- if (
165
- ['auto', undefined].indexOf(self.style[dim]) !== -1 &&
166
- ['auto', undefined].indexOf(self.appliedInlineStyles[dim]) !== -1
167
- ) {
168
- self.parentNodeStyle[dim] = dims[dim] + 'px';
169
- } else if (
170
- ['auto', undefined].indexOf(self.style[dim]) == -1 &&
171
- ['auto', undefined].indexOf(self.appliedInlineStyles[dim]) == -1
172
- ) {
173
- self.parentNodeStyle[dim] = self.style[dim];
174
- if (self.isComponet) {
175
- self.canvas.style[dim] = self.style[dim];
176
- }
177
- }
178
- });
179
- }
180
- self.scrollCache.x = [];
181
- self.scrollCache.y = [];
182
- for (x = 0; x < l; x += 1) {
183
- self.scrollCache.y[x] = dataHeight;
184
- dataHeight +=
185
- ((self.sizes.rows[x] || ch) + (self.sizes.trees[x] || 0)) * self.scale +
186
- // HACK? if an expanded tree row is frozen it is necessary to add the tree row's height a second time.
187
- (self.frozenRow > x ? self.sizes.trees[x] || 0 : 0);
188
- }
189
- if (l > 1) {
190
- self.scrollCache.y[x] = dataHeight;
191
- }
192
- dataWidth =
193
- s.reduce(function reduceSchema(accumulator, column, columnIndex) {
194
- // intentional redefintion of column. This causes scrollCache to be in the correct order
195
- column = s[self.orders.columns[columnIndex]];
196
- if (column.hidden) {
197
- self.scrollCache.x[columnIndex] = accumulator;
198
- return accumulator;
199
- }
200
- var va =
201
- accumulator + self.getColummnWidth(self.orders.columns[columnIndex]);
202
- self.scrollCache.x[columnIndex] = va;
203
- return va;
204
- }, 0) || 0;
205
- if (self.attributes.showNewRow) {
206
- dataHeight += ch;
207
- }
208
- if (self.attributes.snapToRow) {
209
- dataHeight += self.style.cellHeight;
210
- }
211
- setCanvasSize();
212
- if (self.isChildGrid) {
213
- self.width = self.parentNode.offsetWidth;
214
- self.height = self.parentNode.offsetHeight;
215
- } else if (
216
- self.height !== self.canvas.offsetHeight ||
217
- self.width !== self.canvas.offsetWidth
218
- ) {
219
- self.height = self.canvas.offsetHeight;
220
- self.width = self.canvas.offsetWidth;
221
- self.canvasOffsetLeft = self.args.canvasOffsetLeft || 0;
222
- self.canvasOffsetTop = self.args.canvasOffsetTop || 0;
223
- }
224
- /// calculate scroll bar dimensions
225
- // non-controversial
226
- self.scrollBox.top = columnHeaderCellHeight + columnHeaderCellBorder;
227
- self.scrollBox.left = rowHeaderCellWidth;
228
- // width and height of scroll box
229
- setScrollBoxSize();
230
- // is the data larger than the scroll box
231
- checkScrollBoxVisibility();
232
- // if the scroll box is visible, make room for it by expanding the size of the element
233
- // if the other dimension is set to auto
234
- if (self.scrollBox.horizontalBarVisible) {
235
- if (self.style.height === 'auto' && !self.isChildGrid) {
236
- self.height += sbw;
237
- }
238
- dataHeight += sbw;
239
- setCanvasSize();
240
- setScrollBoxSize();
241
- checkScrollBoxVisibility();
242
- }
243
- if (self.scrollBox.verticalBarVisible) {
244
- if (self.style.width === 'auto' && !self.isChildGrid) {
245
- self.width += sbw;
246
- }
247
- dataWidth += sbw;
248
- setCanvasSize();
249
- setScrollBoxSize();
250
- checkScrollBoxVisibility();
251
- }
252
- // set again after bar visibility checks
253
- setScrollBoxSize();
254
- self.scrollBox.scrollWidth = dataWidth - self.scrollBox.width;
255
- self.scrollBox.scrollHeight = dataHeight - self.scrollBox.height;
256
- self.scrollBox.widthBoxRatio = self.scrollBox.width / dataWidth;
257
- self.scrollBox.scrollBoxWidth =
258
- self.scrollBox.width * self.scrollBox.widthBoxRatio -
259
- self.style.scrollBarWidth -
260
- b -
261
- d;
262
- // TODO: This heightBoxRatio number is terribly wrong.
263
- // They should be a result of the size of the grid/canvas?
264
- // it being off causes the scroll bar to "slide" under
265
- // the dragged mouse.
266
- // https://github.com/TonyGermaneri/canvas-datagrid/issues/97
267
- self.scrollBox.heightBoxRatio =
268
- (self.scrollBox.height - columnHeaderCellHeight) / dataHeight;
269
- self.scrollBox.scrollBoxHeight =
270
- self.scrollBox.height * self.scrollBox.heightBoxRatio -
271
- self.style.scrollBarWidth -
272
- b -
273
- d;
274
- self.scrollBox.scrollBoxWidth = Math.max(
275
- self.scrollBox.scrollBoxWidth,
276
- self.style.scrollBarBoxMinSize,
277
- );
278
- self.scrollBox.scrollBoxHeight = Math.max(
279
- self.scrollBox.scrollBoxHeight,
280
- self.style.scrollBarBoxMinSize,
281
- );
282
- // horizontal
283
- n.x += rowHeaderCellWidth;
284
- n.y += self.height - self.style.scrollBarWidth - d;
285
- n.width =
286
- self.width - self.style.scrollBarWidth - rowHeaderCellWidth - d - m;
287
- n.height = self.style.scrollBarWidth + self.style.scrollBarBorderWidth + d;
288
- // horizontal box
289
- nb.y = n.y + self.style.scrollBarBoxMargin;
290
- nb.width = self.scrollBox.scrollBoxWidth;
291
- nb.height = self.style.scrollBarBoxWidth;
292
- // vertical
293
- v.x +=
294
- self.width -
295
- self.style.scrollBarWidth -
296
- self.style.scrollBarBorderWidth -
297
- d;
298
- v.y += columnHeaderCellHeight;
299
- v.width = self.style.scrollBarWidth + self.style.scrollBarBorderWidth + d;
300
- v.height =
301
- self.height - columnHeaderCellHeight - self.style.scrollBarWidth - d - m;
302
- // vertical box
303
- vb.x = v.x + self.style.scrollBarBoxMargin;
304
- vb.width = self.style.scrollBarBoxWidth;
305
- vb.height = self.scrollBox.scrollBoxHeight;
306
- // corner
307
- co.x = n.x + n.width + m;
308
- co.y = v.y + v.height + m;
309
- co.width = self.style.scrollBarWidth + self.style.scrollBarBorderWidth;
310
- co.height = self.style.scrollBarWidth + self.style.scrollBarBorderWidth;
311
- self.scrollBox.entities = {
312
- horizontalBar: n,
313
- horizontalBox: nb,
314
- verticalBar: v,
315
- verticalBox: vb,
316
- corner: co,
317
- };
318
- self.scrollBox.bar = {
319
- v: v,
320
- h: n,
321
- };
322
- self.scrollBox.box = {
323
- v: vb,
324
- h: nb,
325
- };
326
- /// calculate page and dom elements
327
- self.page = Math.max(
328
- 1,
329
- self.visibleRows.length - 3 - self.attributes.pageUpDownOverlap,
330
- );
331
- // set canvas drawing related items
332
- if (!self.isChildGrid) {
333
- self.canvas.width = self.width * ratio;
334
- self.canvas.height = self.height * ratio;
335
- self.ctx.scale(ratio, ratio);
336
- }
337
- // resize any open dom elements (input/textarea)
338
- self.resizeEditInput();
339
- self.scroll(true);
340
- if (drawAfterResize) {
341
- self.draw(true);
342
- }
343
- self.dispatchEvent('resize', {});
344
- return true;
345
- };
346
- self.scroll = function (dontDraw) {
347
- var s = self.getSchema(),
348
- l = (self.data || []).length,
349
- ch = self.style.cellHeight;
350
- // go too far in leaps, then get focused
351
- self.scrollIndexTop = Math.floor(
352
- l * (self.scrollBox.scrollTop / self.scrollBox.scrollHeight) - 100,
353
- );
354
- self.scrollIndexTop = Math.max(self.scrollIndexTop, 0);
355
- self.scrollPixelTop = self.scrollCache.y[self.scrollIndexTop];
356
- // sometimes the grid is rendered but the height is zero
357
- if (self.scrollBox.scrollHeight === 0) {
358
- self.scrollIndexTop = 0;
359
- }
360
- self.scrollPixelTop = 0;
361
- self.scrollIndexLeft = self.frozenColumn;
362
- self.scrollPixelLeft = 0;
363
- while (
364
- self.scrollPixelTop < self.scrollBox.scrollTop &&
365
- self.scrollIndexTop < self.data.length
366
- ) {
367
- // start on index +1 since index 0 was used in "go too far" section above
368
- self.scrollIndexTop += 1;
369
- self.scrollPixelTop = self.scrollCache.y[self.scrollIndexTop];
370
- }
371
- while (
372
- self.scrollPixelLeft < self.scrollBox.scrollLeft + 1 &&
373
- self.scrollIndexLeft < s.length
374
- ) {
375
- self.scrollPixelLeft = self.scrollCache.x[self.scrollIndexLeft];
376
- self.scrollIndexLeft += 1;
377
- }
378
- if (s.length > 0) {
379
- self.scrollIndexLeft = Math.max(self.scrollIndexLeft - 1, 0);
380
- self.scrollPixelLeft -= self.getColummnWidth(
381
- self.orders.columns[self.scrollIndexLeft],
382
- );
383
- }
384
- if ((self.data || []).length > 0) {
385
- self.scrollIndexTop = Math.max(self.scrollIndexTop - 1, 0);
386
- self.scrollPixelTop = Math.max(
387
- self.scrollPixelTop -
388
- (self.data[self.scrollIndexTop]
389
- ? (self.sizes.rows[self.scrollIndexTop] || ch) +
390
- (self.sizes.trees[self.scrollIndexTop] || 0)
391
- : ch) *
392
- self.scale,
393
- 0,
394
- );
395
- }
396
- self.ellipsisCache = {};
397
- if (!dontDraw) {
398
- self.draw(true);
399
- }
400
- //TODO: figure out why this has to be delayed for child grids
401
- //BUG: wheeling event on 3rd level hierarchy fails to move input box
402
- requestAnimationFrame(self.resizeEditInput);
403
- self.dispatchEvent('scroll', {
404
- top: self.scrollBox.scrollTop,
405
- left: self.scrollBox.scrollLeft,
406
- });
407
- };
408
- self.mousemove = function (e, overridePos) {
409
- if (self.contextMenu || self.input) {
410
- return;
411
- }
412
- self.mouse = overridePos || self.getLayerPos(e);
413
- var ctrl =
414
- (e.ctrlKey || e.metaKey || self.attributes.persistantSelectionMode) &&
415
- !self.attributes.singleSelectionMode,
416
- i,
417
- s = self.getSchema(),
418
- dragBounds,
419
- sBounds,
420
- x = self.mouse.x,
421
- y = self.mouse.y,
422
- o = self.getCellAt(x, y),
423
- delta,
424
- ev = { NativeEvent: e, cell: o, x: x, y: y },
425
- previousCell = self.currentCell;
426
- clearTimeout(self.scrollTimer);
427
- if (!self.isInGrid({ x: x, y: y })) {
428
- self.hasFocus = false;
429
- }
430
- if (self.dispatchEvent('mousemove', ev)) {
431
- return;
432
- }
433
- if (o && self.currentCell) {
434
- self.rowBoundaryCrossed = self.currentCell.rowIndex !== o.rowIndex;
435
- self.columnBoundaryCrossed =
436
- self.currentCell.columnIndex !== o.columnIndex;
437
- self.cellBoundaryCrossed =
438
- self.rowBoundaryCrossed || self.columnBoundaryCrossed;
439
- ['row', 'column', 'cell'].forEach(function (prefix) {
440
- if (self[prefix + 'BoundaryCrossed']) {
441
- ev.cell = previousCell;
442
- self.dispatchEvent(prefix + 'mouseout', ev);
443
- ev.cell = o;
444
- self.dispatchEvent(prefix + 'mouseover', ev);
445
- }
446
- });
447
- }
448
- self.currentCell = o;
449
- if (!self.hasFocus) {
450
- return;
451
- }
452
- self.hovers = {};
453
- if (!self.draggingItem && o && self.scrollModes.indexOf(o.context) === -1) {
454
- self.dragItem = o;
455
- self.dragMode = o.dragContext;
456
- self.cursor = o.context;
457
- if (o.context === 'cell') {
458
- self.cursor = 'default';
459
- self.hovers = { rowIndex: o.rowIndex, columnIndex: o.columnIndex };
460
- }
461
- if ((self.selecting || self.reorderObject) && o.context === 'cell') {
462
- delta = {
463
- x: Math.abs(self.dragStart.x - x),
464
- y: Math.abs(self.dragStart.y - y),
465
- };
466
- if (self.dragStartObject.columnIndex !== -1 && e.shiftKey) {
467
- self.dragStartObject = {
468
- rowIndex: self.activeCell.rowIndex,
469
- columnIndex: self.activeCell.columnIndex,
470
- };
471
- }
472
- dragBounds = {
473
- top: Math.min(self.dragStartObject.rowIndex, o.rowIndex),
474
- left: Math.min(self.dragStartObject.columnIndex, o.columnIndex),
475
- bottom: Math.max(self.dragStartObject.rowIndex, o.rowIndex),
476
- right: Math.max(self.dragStartObject.columnIndex, o.columnIndex),
477
- };
478
- if (self.dragStartObject.columnIndex === -1) {
479
- sBounds = self.getSelectionBounds();
480
- dragBounds.left = -1;
481
- dragBounds.right = s.length - 1;
482
- dragBounds.top = Math.min(sBounds.top, o.rowIndex);
483
- dragBounds.bottom = Math.max(sBounds.bottom, o.rowIndex);
484
- }
485
- if (
486
- self.dragStartObject.rowIndex !== o.rowIndex ||
487
- self.dragStartObject.columnIndex !== o.columnIndex
488
- ) {
489
- self.ignoreNextClick = true;
490
- }
491
- if (
492
- self.cellBoundaryCrossed ||
493
- (delta.x === 0 && delta.y === 0) ||
494
- self.attributes.selectionMode === 'row'
495
- ) {
496
- if (
497
- (self.attributes.selectionMode === 'row' ||
498
- self.dragStartObject.columnIndex === -1) &&
499
- self.rowBoundaryCrossed
500
- ) {
501
- self.selectRow(o.rowIndex, ctrl, null, true);
502
- } else if (self.attributes.selectionMode !== 'row') {
503
- if (!self.dragAddToSelection && o.rowIndex !== undefined) {
504
- if (
505
- self.selections[o.rowIndex] &&
506
- self.selections[o.rowIndex].indexOf(o.columnIndex) !== -1
507
- ) {
508
- self.selections[o.rowIndex].splice(
509
- self.selections[o.rowIndex].indexOf(o.columnIndex),
510
- 1,
511
- );
512
- }
513
- } else {
514
- self.selections[o.rowIndex] = self.selections[o.rowIndex] || [];
515
- if (self.selections[o.rowIndex].indexOf(o.columnIndex) === -1) {
516
- self.selections[o.rowIndex].push(o.columnIndex);
517
- }
518
- }
519
- }
520
- }
521
- if (
522
- (!self.selectionBounds ||
523
- dragBounds.top !== self.selectionBounds.top ||
524
- dragBounds.left !== self.selectionBounds.left ||
525
- dragBounds.bottom !== self.selectionBounds.bottom ||
526
- dragBounds.right !== self.selectionBounds.right) &&
527
- !ctrl
528
- ) {
529
- self.selections = [];
530
- sBounds = dragBounds;
531
- if (self.attributes.selectionMode === 'row') {
532
- for (i = sBounds.top; i <= sBounds.bottom; i += 1) {
533
- self.selectRow(i, true, null, true);
534
- }
535
- } else if (dragBounds.top !== -1) {
536
- self.selectArea(sBounds, true);
537
- }
538
- }
539
- self.autoScrollZone(e, x, y, ctrl);
540
- }
541
- }
542
- self.cellBoundaryCrossed = false;
543
- self.rowBoundaryCrossed = false;
544
- self.columnBoundaryCrossed = false;
545
- self.draw(true);
546
- };
547
- self.click = function (e, overridePos) {
548
- var i,
549
- startingBounds = JSON.stringify(self.getSelectionBounds()),
550
- ctrl =
551
- (e.ctrlKey || e.metaKey || self.attributes.persistantSelectionMode) &&
552
- !self.attributes.singleSelectionMode,
553
- pos = overridePos || self.getLayerPos(e);
554
- self.currentCell = self.getCellAt(pos.x, pos.y);
555
- if (self.currentCell.grid !== undefined) {
556
- return;
557
- }
558
- function checkSelectionChange() {
559
- var ev,
560
- sb = self.getSelectionBounds();
561
- if (startingBounds === JSON.stringify(sb)) {
562
- return;
563
- }
564
- ev = {
565
- selections: self.selections,
566
- selectionBounds: self.getSelectionBounds(),
567
- };
568
- Object.defineProperty(ev, 'selectedData', {
569
- get: function () {
570
- return self.getSelectedData();
571
- },
572
- });
573
- self.dispatchEvent('selectionchanged', ev);
574
- }
575
- if (self.input) {
576
- self.endEdit();
577
- }
578
- if (self.ignoreNextClick) {
579
- self.ignoreNextClick = false;
580
- return;
581
- }
582
- i = self.currentCell;
583
- if (
584
- self.dispatchEvent('click', { NativeEvent: e, cell: self.currentCell })
585
- ) {
586
- return;
587
- }
588
- if (!self.hasFocus) {
589
- return;
590
- }
591
- if (
592
- ['rowHeaderCell', 'columnHeaderCell'].indexOf(self.currentCell.style) ===
593
- -1 &&
594
- !ctrl
595
- ) {
596
- self.setActiveCell(i.columnIndex, i.rowIndex);
597
- }
598
- if (self.currentCell.context === 'cell') {
599
- if (self.currentCell.style === 'cornerCell') {
600
- self.selectAll();
601
- self.draw();
602
- checkSelectionChange();
603
- return;
604
- }
605
- if (self.currentCell.style === 'columnHeaderCell') {
606
- if (self.attributes.columnHeaderClickBehavior === 'sort') {
607
- if (self.orderBy === i.header.name) {
608
- self.orderDirection =
609
- self.orderDirection === 'asc' ? 'desc' : 'asc';
610
- } else {
611
- self.orderDirection = 'asc';
612
- }
613
- self.order(i.header.name, self.orderDirection);
614
- checkSelectionChange();
615
- return;
616
- }
617
- if (self.attributes.columnHeaderClickBehavior === 'select') {
618
- self.selectColumn(i.header.index, ctrl, e.shiftKey);
619
- self.draw();
620
- return;
621
- }
622
- }
623
- self.selections[i.rowIndex] = self.selections[i.rowIndex] || [];
624
- if (
625
- self.attributes.selectionMode === 'row' ||
626
- self.currentCell.style === 'rowHeaderCell'
627
- ) {
628
- if (
629
- self.currentCell.style === 'rowHeaderCell' &&
630
- self.attributes.tree &&
631
- pos.x > 0 &&
632
- pos.x - self.currentCell.x <
633
- self.style.treeArrowWidth +
634
- self.style.treeArrowMarginLeft +
635
- self.style.treeArrowMarginRight +
636
- self.style.treeArrowClickRadius &&
637
- pos.y - self.currentCell.y <
638
- self.style.treeArrowHeight +
639
- self.style.treeArrowMarginTop +
640
- self.style.treeArrowClickRadius &&
641
- pos.y > 0
642
- ) {
643
- self.toggleTree(i.rowIndex);
644
- return;
645
- }
646
- }
647
- if (e.shiftKey && !ctrl) {
648
- self.selectionBounds = self.getSelectionBounds();
649
- self.selectArea(undefined, false);
650
- }
651
- }
652
- checkSelectionChange();
653
- self.draw(true);
654
- };
655
- self.dragResizeColumn = function (e) {
656
- var pos, x, y;
657
- pos = self.getLayerPos(e);
658
- x = self.resizingStartingWidth + pos.x - self.dragStart.x;
659
- y = self.resizingStartingHeight + pos.y - self.dragStart.y;
660
- if (x < self.style.minColumnWidth) {
661
- x = self.style.minColumnWidth;
662
- }
663
- if (y < self.style.minRowHeight) {
664
- y = self.style.minRowHeight;
665
- }
666
- if (
667
- self.dispatchEvent('resizecolumn', {
668
- x: x,
669
- y: y,
670
- draggingItem: self.draggingItem,
671
- })
672
- ) {
673
- return false;
674
- }
675
- if (
676
- self.scrollBox.scrollLeft >
677
- self.scrollBox.scrollWidth - self.attributes.resizeScrollZone &&
678
- self.dragMode === 'ew-resize'
679
- ) {
680
- self.resize(true);
681
- self.scrollBox.scrollLeft += x;
682
- }
683
- if (self.dragMode === 'ew-resize') {
684
- self.sizes.columns[
685
- self.draggingItem.header.style === 'rowHeaderCell'
686
- ? 'cornerCell'
687
- : self.draggingItem.sortColumnIndex
688
- ] = x;
689
- if (
690
- ['rowHeaderCell', 'cornerCell'].indexOf(
691
- self.draggingItem.header.style,
692
- ) !== -1
693
- ) {
694
- self.resize(true);
695
- }
696
- self.resizeChildGrids();
697
- return;
698
- }
699
- if (self.dragMode === 'ns-resize') {
700
- if (self.draggingItem.rowOpen) {
701
- self.sizes.trees[self.draggingItem.rowIndex] = y;
702
- } else if (self.attributes.globalRowResize) {
703
- self.style.cellHeight = y;
704
- } else {
705
- self.sizes.rows[self.draggingItem.rowIndex] = y;
706
- }
707
- self.dispatchEvent('resizerow', { row: y });
708
- self.resizeChildGrids();
709
- return;
710
- }
711
- self.ellipsisCache = {};
712
- };
713
- self.stopDragResize = function () {
714
- self.resize();
715
- document.body.removeEventListener(
716
- 'mousemove',
717
- self.dragResizeColumn,
718
- false,
719
- );
720
- document.body.removeEventListener('mouseup', self.stopDragResize, false);
721
- self.setStorageData();
722
- self.draw(true);
723
- self.ignoreNextClick = true;
724
- };
725
- self.scrollGrid = function (e) {
726
- var pos = self.getLayerPos(e);
727
- if (
728
- self.attributes.scrollPointerLock &&
729
- self.pointerLockPosition &&
730
- ['horizontal-scroll-box', 'vertical-scroll-box'].indexOf(
731
- self.scrollStartMode,
732
- ) !== -1
733
- ) {
734
- self.pointerLockPosition.x += e.movementX;
735
- self.pointerLockPosition.y += e.movementY;
736
- self.pointerLockPosition.x = Math.min(
737
- self.width - self.style.scrollBarWidth,
738
- Math.max(0, self.pointerLockPosition.x),
739
- );
740
- self.pointerLockPosition.y = Math.min(
741
- self.height - self.style.scrollBarWidth,
742
- Math.max(0, self.pointerLockPosition.y),
743
- );
744
- pos = self.pointerLockPosition;
745
- }
746
- self.scrollMode = self.getCellAt(pos.x, pos.y).context;
747
- if (
748
- self.scrollMode === 'horizontal-scroll-box' &&
749
- self.scrollStartMode !== 'horizontal-scroll-box'
750
- ) {
751
- self.scrollStartMode = 'horizontal-scroll-box';
752
- self.dragStart = pos;
753
- self.scrollStart.left = self.scrollBox.scrollLeft;
754
- clearTimeout(self.scrollTimer);
755
- return;
756
- }
757
- if (
758
- self.scrollMode === 'vertical-scroll-box' &&
759
- self.scrollStartMode !== 'vertical-scroll-box'
760
- ) {
761
- self.scrollStartMode = 'vertical-scroll-box';
762
- self.dragStart = pos;
763
- self.scrollStart.top = self.scrollBox.scrollTop;
764
- clearTimeout(self.scrollTimer);
765
- return;
766
- }
767
- if (
768
- self.scrollStartMode === 'vertical-scroll-box' &&
769
- self.scrollMode !== 'vertical-scroll-box'
770
- ) {
771
- self.scrollMode = 'vertical-scroll-box';
772
- }
773
- if (
774
- self.scrollStartMode === 'horizontal-scroll-box' &&
775
- self.scrollMode !== 'horizontal-scroll-box'
776
- ) {
777
- self.scrollMode = 'horizontal-scroll-box';
778
- }
779
- clearTimeout(self.scrollTimer);
780
- if (self.scrollModes.indexOf(self.scrollMode) === -1) {
781
- return;
782
- }
783
- if (self.scrollMode === 'vertical-scroll-box') {
784
- self.scrollBox.scrollTop =
785
- self.scrollStart.top +
786
- (pos.y - self.dragStart.y) / self.scrollBox.heightBoxRatio;
787
- } else if (self.scrollMode === 'vertical-scroll-top') {
788
- self.scrollBox.scrollTop -= self.page * self.style.cellHeight;
789
- self.scrollTimer = setTimeout(
790
- self.scrollGrid,
791
- self.attributes.scrollRepeatRate,
792
- e,
793
- );
794
- } else if (self.scrollMode === 'vertical-scroll-bottom') {
795
- self.scrollBox.scrollTop += self.page * self.style.cellHeight;
796
- self.scrollTimer = setTimeout(
797
- self.scrollGrid,
798
- self.attributes.scrollRepeatRate,
799
- e,
800
- );
801
- }
802
- if (self.scrollMode === 'horizontal-scroll-box') {
803
- self.scrollBox.scrollLeft =
804
- self.scrollStart.left +
805
- (pos.x - self.dragStart.x) / self.scrollBox.widthBoxRatio;
806
- } else if (self.scrollMode === 'horizontal-scroll-right') {
807
- self.scrollBox.scrollLeft += self.attributes.selectionScrollIncrement;
808
- self.scrollTimer = setTimeout(
809
- self.scrollGrid,
810
- self.attributes.scrollRepeatRate,
811
- e,
812
- );
813
- } else if (self.scrollMode === 'horizontal-scroll-left') {
814
- self.scrollBox.scrollLeft -= self.attributes.selectionScrollIncrement;
815
- self.scrollTimer = setTimeout(
816
- self.scrollGrid,
817
- self.attributes.scrollRepeatRate,
818
- e,
819
- );
820
- }
821
- };
822
- self.stopScrollGrid = function () {
823
- clearTimeout(self.scrollTimer);
824
- if (document.exitPointerLock) {
825
- document.exitPointerLock();
826
- }
827
- document.removeEventListener('mousemove', self.scrollGrid, false);
828
- };
829
- self.dragReorder = function (e) {
830
- var pos,
831
- x,
832
- y,
833
- columReorder = self.dragMode === 'column-reorder',
834
- rowReorder = self.dragMode === 'row-reorder';
835
- pos = self.getLayerPos(e);
836
- x = pos.x - self.dragStart.x;
837
- y = pos.y - self.dragStart.y;
838
- if (!self.attributes.allowColumnReordering && columReorder) {
839
- return;
840
- }
841
- if (!self.attributes.allowRowReordering && rowReorder) {
842
- return;
843
- }
844
- if (
845
- self.dispatchEvent('reordering', {
846
- NativeEvent: e,
847
- source: self.dragStartObject,
848
- target: self.currentCell,
849
- dragMode: self.dragMode,
850
- })
851
- ) {
852
- return;
853
- }
854
- if (
855
- Math.abs(x) > self.attributes.reorderDeadZone ||
856
- Math.abs(y) > self.attributes.reorderDeadZone
857
- ) {
858
- self.reorderObject = self.draggingItem;
859
- self.reorderTarget = self.currentCell;
860
- self.reorderObject.dragOffset = {
861
- x: x,
862
- y: y,
863
- };
864
- self.autoScrollZone(
865
- e,
866
- columReorder ? pos.x : -1,
867
- rowReorder ? pos.y : -1,
868
- false,
869
- );
870
- }
871
- };
872
- self.stopDragReorder = function (e) {
873
- var oIndex,
874
- tIndex,
875
- cr = {
876
- 'row-reorder': self.orders.rows,
877
- 'column-reorder': self.orders.columns,
878
- },
879
- i = {
880
- 'row-reorder': 'rowIndex',
881
- 'column-reorder': 'sortColumnIndex',
882
- }[self.dragMode];
883
- document.body.removeEventListener('mousemove', self.dragReorder, false);
884
- document.body.removeEventListener('mouseup', self.stopDragReorder, false);
885
- if (
886
- self.reorderObject &&
887
- self.reorderTarget &&
888
- ((self.dragMode === 'column-reorder' &&
889
- self.reorderTarget.sortColumnIndex > -1 &&
890
- self.reorderTarget.sortColumnIndex < self.getSchema().length) ||
891
- (self.dragMode === 'row-reorder' &&
892
- self.reorderTarget.rowIndex > -1 &&
893
- self.reorderTarget.rowIndex < self.data.length)) &&
894
- self.reorderObject[i] !== self.reorderTarget[i] &&
895
- !self.dispatchEvent('reorder', {
896
- NativeEvent: e,
897
- source: self.reorderObject,
898
- target: self.reorderTarget,
899
- dragMode: self.dragMode,
900
- })
901
- ) {
902
- self.ignoreNextClick = true;
903
- oIndex = cr[self.dragMode].indexOf(self.reorderObject[i]);
904
- tIndex = cr[self.dragMode].indexOf(self.reorderTarget[i]);
905
- cr[self.dragMode].splice(oIndex, 1);
906
- cr[self.dragMode].splice(tIndex, 0, self.reorderObject[i]);
907
- if (self.dragMode === 'column-reorder') {
908
- self.orders.columns = cr[self.dragMode];
909
- } else {
910
- self.orders.rows = cr[self.dragMode];
911
- }
912
- self.resize();
913
- self.setStorageData();
914
- }
915
- self.reorderObject = undefined;
916
- self.reorderTarget = undefined;
917
- self.draw(true);
918
- };
919
- self.dragMove = function (e) {
920
- if (
921
- self.dispatchEvent('moving', { NativeEvent: e, cell: self.currentCell })
922
- ) {
923
- return;
924
- }
925
- var pos = self.getLayerPos(e);
926
- self.moveOffset = {
927
- x: self.currentCell.columnIndex - self.dragStartObject.columnIndex,
928
- y: self.currentCell.rowIndex - self.dragStartObject.rowIndex,
929
- };
930
- if (
931
- Math.abs(pos.x) > self.attributes.reorderDeadZone ||
932
- Math.abs(pos.y) > self.attributes.reorderDeadZone
933
- ) {
934
- setTimeout(function () {
935
- self.autoScrollZone(e, pos.x, pos.y, false);
936
- }, 1);
937
- }
938
- };
939
- self.stopDragMove = function (e) {
940
- document.body.removeEventListener('mousemove', self.dragMove, false);
941
- document.body.removeEventListener('mouseup', self.stopDragMove, false);
942
- var b = self.getSelectionBounds();
943
- if (
944
- self.dispatchEvent('endmove', { NativeEvent: e, cell: self.currentCell })
945
- ) {
946
- self.movingSelection = undefined;
947
- self.moveOffset = undefined;
948
- self.draw(true);
949
- return;
950
- }
951
- if (self.moveOffset) {
952
- self.moveTo(
953
- self.movingSelection,
954
- b.left + self.moveOffset.x,
955
- b.top + self.moveOffset.y,
956
- );
957
- self.moveSelection(self.moveOffset.x, self.moveOffset.y);
958
- }
959
- self.movingSelection = undefined;
960
- self.moveOffset = undefined;
961
- self.draw(true);
962
- };
963
- self.freezeMove = function (e) {
964
- if (
965
- self.dispatchEvent('freezemoving', {
966
- NativeEvent: e,
967
- cell: self.currentCell,
968
- })
969
- ) {
970
- return;
971
- }
972
- var pos = self.getLayerPos(e);
973
- self.ignoreNextClick = true;
974
- self.freezeMarkerPosition = pos;
975
- if (
976
- self.currentCell &&
977
- self.currentCell.rowIndex !== undefined &&
978
- self.dragMode === 'frozen-row-marker'
979
- ) {
980
- self.scrollBox.scrollTop = 0;
981
- self.frozenRow = self.currentCell.rowIndex + 1;
982
- }
983
- if (
984
- self.currentCell &&
985
- self.currentCell.columnIndex !== undefined &&
986
- self.dragMode === 'frozen-column-marker'
987
- ) {
988
- self.scrollBox.scrollLeft = 0;
989
- self.frozenColumn = self.currentCell.columnIndex + 1;
990
- }
991
- if (
992
- Math.abs(pos.x) > self.attributes.reorderDeadZone ||
993
- Math.abs(pos.y) > self.attributes.reorderDeadZone
994
- ) {
995
- setTimeout(function () {
996
- self.autoScrollZone(e, pos.x, pos.y, false);
997
- }, 1);
998
- }
999
- };
1000
- self.stopFreezeMove = function (e) {
1001
- document.body.removeEventListener('mousemove', self.freezeMove, false);
1002
- document.body.removeEventListener('mouseup', self.stopFreezeMove, false);
1003
- self.freezeMarkerPosition = undefined;
1004
- if (
1005
- self.dispatchEvent('endfreezemove', {
1006
- NativeEvent: e,
1007
- cell: self.currentCell,
1008
- })
1009
- ) {
1010
- self.frozenRow = self.startFreezeMove.x;
1011
- self.frozenColumn = self.startFreezeMove.y;
1012
- self.draw(true);
1013
- return;
1014
- }
1015
- self.draw(true);
1016
- };
1017
- self.mousedown = function (e, overridePos) {
1018
- self.lastMouseDownTarget = e.target;
1019
- if (
1020
- self.dispatchEvent('mousedown', {
1021
- NativeEvent: e,
1022
- cell: self.currentCell,
1023
- })
1024
- ) {
1025
- return;
1026
- }
1027
- if (!self.hasFocus) {
1028
- return;
1029
- }
1030
- if (e.button === 2 || self.input) {
1031
- return;
1032
- }
1033
- var ctrl = e.ctrlKey || e.metaKey,
1034
- move = /-move/.test(self.dragMode),
1035
- freeze = /frozen-row-marker|frozen-column-marker/.test(self.dragMode),
1036
- resize = /-resize/.test(self.dragMode);
1037
- self.dragStart = overridePos || self.getLayerPos(e);
1038
- self.scrollStart = {
1039
- left: self.scrollBox.scrollLeft,
1040
- top: self.scrollBox.scrollTop,
1041
- };
1042
- self.dragStartObject = self.getCellAt(self.dragStart.x, self.dragStart.y);
1043
- self.dragAddToSelection = !self.dragStartObject.selected;
1044
- if (
1045
- !ctrl &&
1046
- !e.shiftKey &&
1047
- !/(vertical|horizontal)-scroll-(bar|box)/.test(
1048
- self.dragStartObject.context,
1049
- ) &&
1050
- self.currentCell &&
1051
- !self.currentCell.isColumnHeader &&
1052
- !move &&
1053
- !freeze &&
1054
- !resize
1055
- ) {
1056
- self.selections = [];
1057
- }
1058
- if (self.dragStartObject.isGrid) {
1059
- return;
1060
- }
1061
- if (self.scrollModes.indexOf(self.dragStartObject.context) !== -1) {
1062
- self.scrollMode = self.dragStartObject.context;
1063
- self.scrollStartMode = self.dragStartObject.context;
1064
- self.scrollGrid(e);
1065
- if (
1066
- self.attributes.scrollPointerLock &&
1067
- ['horizontal-scroll-box', 'vertical-scroll-box'].indexOf(
1068
- self.scrollStartMode,
1069
- ) !== -1
1070
- ) {
1071
- self.pointerLockPosition = {
1072
- x: self.dragStart.x,
1073
- y: self.dragStart.y,
1074
- };
1075
- self.canvas.requestPointerLock();
1076
- }
1077
- document.addEventListener('mousemove', self.scrollGrid, false);
1078
- document.addEventListener('mouseup', self.stopScrollGrid, false);
1079
- self.ignoreNextClick = true;
1080
- return;
1081
- }
1082
- if (self.dragMode === 'cell') {
1083
- self.selecting = true;
1084
- if (
1085
- (self.attributes.selectionMode === 'row' ||
1086
- self.dragStartObject.columnIndex === -1) &&
1087
- self.dragStartObject.rowIndex > -1
1088
- ) {
1089
- self.selectRow(self.dragStartObject.rowIndex, ctrl, null);
1090
- } else if (self.attributes.selectionMode !== 'row') {
1091
- self.mousemove(e);
1092
- }
1093
- return;
1094
- }
1095
- if (move) {
1096
- self.draggingItem = self.dragItem;
1097
- self.movingSelection = self.selections.concat([]);
1098
- self.dragging = self.dragStartObject;
1099
- if (
1100
- self.dispatchEvent('beginmove', {
1101
- NativeEvent: e,
1102
- cell: self.currentCell,
1103
- })
1104
- ) {
1105
- return;
1106
- }
1107
- document.body.addEventListener('mousemove', self.dragMove, false);
1108
- document.body.addEventListener('mouseup', self.stopDragMove, false);
1109
- return self.mousemove(e);
1110
- }
1111
- if (freeze) {
1112
- self.draggingItem = self.dragItem;
1113
- self.startFreezeMove = {
1114
- x: self.frozenRow,
1115
- y: self.frozenColumn,
1116
- };
1117
- if (self.dispatchEvent('beginfreezemove', { NativeEvent: e })) {
1118
- return;
1119
- }
1120
- document.body.addEventListener('mousemove', self.freezeMove, false);
1121
- document.body.addEventListener('mouseup', self.stopFreezeMove, false);
1122
- return self.mousemove(e);
1123
- }
1124
- if (resize) {
1125
- self.draggingItem = self.dragItem;
1126
- if (self.draggingItem.rowOpen) {
1127
- self.resizingStartingHeight =
1128
- self.sizes.trees[self.draggingItem.rowIndex];
1129
- } else {
1130
- self.resizingStartingHeight =
1131
- self.sizes.rows[self.draggingItem.rowIndex] || self.style.cellHeight;
1132
- }
1133
- self.resizingStartingWidth =
1134
- self.sizes.columns[
1135
- self.draggingItem.header.style === 'rowHeaderCell'
1136
- ? 'cornerCell'
1137
- : self.draggingItem.sortColumnIndex
1138
- ] || self.draggingItem.width;
1139
- document.body.addEventListener('mousemove', self.dragResizeColumn, false);
1140
- document.body.addEventListener('mouseup', self.stopDragResize, false);
1141
- return;
1142
- }
1143
- if (['row-reorder', 'column-reorder'].indexOf(self.dragMode) !== -1) {
1144
- self.draggingItem = self.dragStartObject;
1145
- document.body.addEventListener('mousemove', self.dragReorder, false);
1146
- document.body.addEventListener('mouseup', self.stopDragReorder, false);
1147
- return;
1148
- }
1149
- };
1150
- self.mouseup = function (e) {
1151
- clearTimeout(self.scrollTimer);
1152
- self.cellBoundaryCrossed = true;
1153
- self.rowBoundaryCrossed = true;
1154
- self.columnBoundaryCrossed = true;
1155
- self.selecting = undefined;
1156
- self.draggingItem = undefined;
1157
- self.dragStartObject = undefined;
1158
- if (
1159
- self.dispatchEvent('mouseup', { NativeEvent: e, cell: self.currentCell })
1160
- ) {
1161
- return;
1162
- }
1163
- if (!self.hasFocus && e.target !== self.canvas) {
1164
- return;
1165
- }
1166
- if (self.currentCell && self.currentCell.grid !== undefined) {
1167
- return;
1168
- }
1169
- if (self.contextMenu || self.input) {
1170
- return;
1171
- }
1172
- if (self.dragStart && self.isInGrid(self.dragStart)) {
1173
- self.controlInput.focus();
1174
- }
1175
- e.preventDefault();
1176
- };
1177
- // gets the horizontal adjacent cells as well as first/last based on column visibility
1178
- self.getAdjacentCells = function () {
1179
- var x,
1180
- i,
1181
- s = self.getSchema(),
1182
- o = {};
1183
- for (x = 0; x < s.length; x += 1) {
1184
- i = self.orders.columns[x];
1185
- if (!s[i].hidden) {
1186
- if (o.first === undefined) {
1187
- o.first = x;
1188
- o.left = x;
1189
- }
1190
- o.last = x;
1191
- if (x > self.activeCell.columnIndex && o.right === undefined) {
1192
- o.right = x;
1193
- }
1194
- if (x < self.activeCell.columnIndex) {
1195
- o.left = x;
1196
- }
1197
- }
1198
- }
1199
- if (o.right === undefined) {
1200
- o.right = o.last;
1201
- }
1202
- return o;
1203
- };
1204
- self.keydown = function (e) {
1205
- var i,
1206
- ev,
1207
- adjacentCells = self.getAdjacentCells(),
1208
- x = self.activeCell.columnIndex,
1209
- y = self.activeCell.rowIndex,
1210
- ctrl = e.ctrlKey || e.metaKey,
1211
- last = self.data.length - 1,
1212
- s = self.getSchema(),
1213
- cols = s.length - 1;
1214
-
1215
- var defaultPrevented = self.dispatchEvent('keydown', {
1216
- NativeEvent: e,
1217
- cell: self.currentCell,
1218
- });
1219
-
1220
- if (defaultPrevented) {
1221
- return;
1222
- }
1223
-
1224
- if (!self.hasFocus) {
1225
- return;
1226
- }
1227
-
1228
- // If a user starts typing content, switch to "Enter" mode
1229
- if (isPrintableKeyEvent(e) && !ctrl) {
1230
- return self.beginEditAt(x, y, e, true);
1231
- }
1232
-
1233
- if (self.attributes.showNewRow) {
1234
- last += 1;
1235
- }
1236
-
1237
- if (e.key === 'Tab') {
1238
- e.preventDefault();
1239
- }
1240
-
1241
- if (e.key === 'Escape') {
1242
- self.selections = [];
1243
- self.draw(true);
1244
- } else if (ctrl && e.key === 'a') {
1245
- self.selectAll();
1246
- } else if (e.key === 'ArrowDown') {
1247
- y += 1;
1248
- } else if (e.key === 'ArrowUp') {
1249
- y -= 1;
1250
- } else if (
1251
- (e.key === 'ArrowLeft' && !ctrl) ||
1252
- (e.shiftKey && e.key === 'Tab')
1253
- ) {
1254
- x = adjacentCells.left;
1255
- } else if (
1256
- (e.key === 'ArrowRight' && !ctrl) ||
1257
- (!e.shiftKey && e.key === 'Tab')
1258
- ) {
1259
- x = adjacentCells.right;
1260
- } else if (e.key === 'PageUp') {
1261
- y -= self.page;
1262
- e.preventDefault();
1263
- } else if (e.key === 'PageDown') {
1264
- y += self.page;
1265
- e.preventDefault();
1266
- } else if (e.key === 'Home' || (ctrl && e.key === 'ArrowUp')) {
1267
- y = 0;
1268
- } else if (e.key === 'End' || (ctrl && e.key === 'ArrowDown')) {
1269
- y = self.data.length - 1;
1270
- } else if (ctrl && e.key === 'ArrowRight') {
1271
- x = adjacentCells.last;
1272
- } else if (ctrl && e.key === 'ArrowLeft') {
1273
- x = adjacentCells.first;
1274
- }
1275
-
1276
- if (e.key === 'Enter') {
1277
- e.preventDefault();
1278
- return self.beginEditAt(x, y, e);
1279
- }
1280
-
1281
- if (x < 0 || Number.isNaN(x)) {
1282
- x = adjacentCells.first;
1283
- }
1284
- if (y > last) {
1285
- y = last;
1286
- }
1287
- if (y < 0 || Number.isNaN(y)) {
1288
- y = 0;
1289
- }
1290
- if (x > cols) {
1291
- x = adjacentCells.last;
1292
- }
1293
-
1294
- // TODO - most likely some column order related bugs in key based selection
1295
- // Arrows
1296
- var isArrowKey = [
1297
- 'ArrowLeft',
1298
- 'ArrowUp',
1299
- 'ArrowRight',
1300
- 'ArrowDown',
1301
- ].includes(e.key);
1302
-
1303
- if (e.shiftKey && isArrowKey) {
1304
- self.selections[Math.max(y, 0)] = self.selections[Math.max(y, 0)] || [];
1305
- self.selections[Math.max(y, 0)].push(x);
1306
-
1307
- self.selectionBounds = self.getSelectionBounds();
1308
- self.selectArea(undefined, ctrl);
1309
-
1310
- self.draw(true);
1311
- }
1312
-
1313
- if (x !== self.activeCell.columnIndex || y !== self.activeCell.rowIndex) {
1314
- self.scrollIntoView(
1315
- x !== self.activeCell.columnIndex ? x : undefined,
1316
- y !== self.activeCell.rowIndex && !Number.isNaN(y) ? y : undefined,
1317
- );
1318
-
1319
- self.setActiveCell(x, y);
1320
- if (!e.shiftKey && self.attributes.selectionFollowsActiveCell) {
1321
- if (!ctrl) {
1322
- self.selections = [];
1323
- }
1324
- self.selections[y] = self.selections[y] || [];
1325
- self.selections[y].push(x);
1326
- ev = {
1327
- selectedData: self.getSelectedData(),
1328
- selections: self.selections,
1329
- selectionBounds: self.getSelectionBounds(),
1330
- };
1331
- Object.defineProperty(ev, 'selectedData', {
1332
- get: function () {
1333
- return self.getSelectedData();
1334
- },
1335
- });
1336
- self.dispatchEvent('selectionchanged', ev);
1337
- }
1338
- self.draw(true);
1339
- }
1340
- };
1341
- self.keyup = function (e) {
1342
- if (
1343
- self.dispatchEvent('keyup', { NativeEvent: e, cell: self.currentCell })
1344
- ) {
1345
- return;
1346
- }
1347
- if (!self.hasFocus) {
1348
- return;
1349
- }
1350
- };
1351
- self.keypress = function (e) {
1352
- if (!self.hasFocus) {
1353
- return;
1354
- }
1355
- if (
1356
- self.dispatchEvent('keypress', { NativeEvent: e, cell: self.currentCell })
1357
- ) {
1358
- return;
1359
- }
1360
- };
1361
- self.dblclick = function (e) {
1362
- if (
1363
- self.dispatchEvent('dblclick', { NativeEvent: e, cell: self.currentCell })
1364
- ) {
1365
- return;
1366
- }
1367
- if (!self.hasFocus) {
1368
- return;
1369
- }
1370
- if (
1371
- self.currentCell.context === 'ew-resize' &&
1372
- self.currentCell.style === 'columnHeaderCell'
1373
- ) {
1374
- self.fitColumnToValues(self.currentCell.header.name);
1375
- } else if (
1376
- self.currentCell.context === 'ew-resize' &&
1377
- self.currentCell.style === 'cornerCell'
1378
- ) {
1379
- self.autosize();
1380
- } else if (['cell', 'activeCell'].indexOf(self.currentCell.style) !== -1) {
1381
- self.beginEditAt(self.currentCell.columnIndex, self.currentCell.rowIndex);
1382
- }
1383
- };
1384
- self.scrollWheel = function (e) {
1385
- var l,
1386
- t,
1387
- ev = e,
1388
- deltaX = e.deltaX === undefined ? e.NativeEvent.deltaX : e.deltaX,
1389
- deltaY = e.deltaY === undefined ? e.NativeEvent.deltaY : e.deltaY,
1390
- deltaMode =
1391
- e.deltaMode === undefined ? e.NativeEvent.deltaMode : e.deltaMode;
1392
- var e = e.NativeEvent || e;
1393
- if (wheeling) {
1394
- ev.preventDefault(e);
1395
- return;
1396
- }
1397
- if (self.dispatchEvent('wheel', { NativeEvent: e })) {
1398
- return;
1399
- }
1400
- self.touchHaltAnimation = true;
1401
- l = self.scrollBox.scrollLeft;
1402
- t = self.scrollBox.scrollTop;
1403
- if (self.hasFocus) {
1404
- //BUG Issue 42: https://github.com/TonyGermaneri/canvas-datagrid/issues/42
1405
- //https://stackoverflow.com/questions/20110224/what-is-the-height-of-a-line-in-a-wheel-event-deltamode-dom-delta-line
1406
- if (deltaMode === 1) {
1407
- // line mode = 17 pixels per line
1408
- deltaY = deltaY * 17;
1409
- }
1410
- if (
1411
- (self.scrollBox.scrollTop < self.scrollBox.scrollHeight &&
1412
- deltaY > 0) ||
1413
- (self.scrollBox.scrollLeft < self.scrollBox.scrollWidth &&
1414
- deltaX > 0) ||
1415
- (self.scrollBox.scrollTop > 0 && deltaY < 0) ||
1416
- (self.scrollBox.scrollLeft > 0 && deltaX < 0)
1417
- ) {
1418
- ev.preventDefault(e);
1419
- }
1420
- wheeling = setTimeout(function () {
1421
- wheeling = undefined;
1422
- self.scrollBox.scrollTo(deltaX + l, deltaY + t);
1423
- }, 1);
1424
- }
1425
- };
1426
-
1427
- self.pasteData = function (
1428
- pasteValue,
1429
- mimeType,
1430
- startRowIndex,
1431
- startColIndex,
1432
- ) {
1433
- var schema = self.getVisibleSchema();
1434
-
1435
- const isSupportedHtmlTable = (htmlString) =>
1436
- /(?:^(<meta[^>]+>)?<table>)|(?:<!--StartFragment-->\s*<table>)/.test(
1437
- htmlString,
1438
- );
1439
-
1440
- // TODO: support pasting tables from Excel
1441
- if (
1442
- mimeType === 'text/html' &&
1443
- isSupportedHtmlTable(pasteValue) === false
1444
- ) {
1445
- console.warn(
1446
- 'Unrecognized HTML format. HTML must be a simple table, e.g.: <table><tr><td>data</td></tr></table>.',
1447
- );
1448
- console.warn(
1449
- 'Data with the mime type text/html not in this format will not be imported as row data.',
1450
- );
1451
-
1452
- return;
1453
- }
1454
-
1455
- function parseData(data, mimeType) {
1456
- // TODO: use DOMParser
1457
- if (mimeType === 'text/html') {
1458
- // strip table beginning and ending off, then split at rows
1459
- var cleanedHtmlData = data
1460
- .substring(
1461
- data.indexOf('<table><tr>') + 11,
1462
- data.indexOf('</tr></table>'),
1463
- )
1464
- .split('</tr><tr>')
1465
- .filter(
1466
- // ditch any headers on the table
1467
- (row) => !/^<th>|^<thead>/.test(row),
1468
- )
1469
- .map(
1470
- // split row into individual cells
1471
- (row) =>
1472
- row
1473
- .match(/<td>[^<]+/g)
1474
- .map((match) => match.replace(/^<td>/, '')),
1475
- );
1476
-
1477
- return cleanedHtmlData;
1478
- }
1479
-
1480
- // Default data format is string, so split on new line,
1481
- // and then enclose in an array (a row with one cell):
1482
- return data.split('\n').map((value) => [value]);
1483
- }
1484
-
1485
- var rows = parseData(pasteValue, mimeType);
1486
-
1487
- // Special case: if rows.length = 1, we paste this value in each
1488
- // selected cell. This mimics Excel's paste functionality, and works
1489
- // as a poor-man's fill-down.
1490
- if (rows.length === 1) {
1491
- var cellData = rows[0][0];
1492
-
1493
- self.forEachSelectedCell(function (data, rowIndex, colName) {
1494
- data[rowIndex][colName] = cellData;
1495
- });
1496
- } else {
1497
- var selections = [];
1498
- for (var rowIndex = 0; rowIndex < rows.length; rowIndex++) {
1499
- // Rows may have been moved by user, so get the actual row index
1500
- // (instead of the row index at which the row is rendered):
1501
- var realRowIndex = self.orders.rows[startRowIndex + rowIndex];
1502
- var cells = rows[rowIndex];
1503
-
1504
- var existingRowData = self.data[realRowIndex];
1505
- var newRowData = Object.assign({}, existingRowData);
1506
-
1507
- selections[realRowIndex] = [];
1508
-
1509
- for (var colIndex = 0; colIndex < cells.length; colIndex++) {
1510
- var column = schema[startColIndex + colIndex];
1511
-
1512
- if (!column) {
1513
- console.warn('Paste data exceeded grid bounds. Skipping.');
1514
- continue;
1515
- }
1516
-
1517
- var columnName = column.name;
1518
- var cellData = cells[colIndex];
1519
-
1520
- if (cellData === undefined || cellData === null) {
1521
- newRowData[columnName] = existingRowData[columnName];
1522
- continue;
1523
- }
1524
-
1525
- selections[realRowIndex].push(startColIndex + colIndex);
1526
-
1527
- newRowData[columnName] = cellData;
1528
- }
1529
-
1530
- self.data[realRowIndex] = newRowData;
1531
- }
1532
- self.selections = selections;
1533
- }
1534
-
1535
- // selections is a sparse array, so we condense:
1536
- var affectedCells = [];
1537
-
1538
- self.selections.forEach(function (row, rowIndex) {
1539
- if (rowIndex === undefined) return;
1540
-
1541
- row.forEach(function (columnIndex) {
1542
- affectedCells.push([rowIndex, columnIndex]);
1543
- });
1544
- });
1545
-
1546
- self.dispatchEvent('afterpaste', {
1547
- cells: affectedCells,
1548
- });
1549
-
1550
- return rows.length;
1551
- };
1552
- self.getNextVisibleColumnIndex = function (visibleColumnIndex) {
1553
- var x,
1554
- s = self.getVisibleSchema();
1555
- for (x = 0; x < s.length; x += 1) {
1556
- if (s[x].columnIndex === visibleColumnIndex) {
1557
- return s[x + 1].columnIndex;
1558
- }
1559
- }
1560
- return -1;
1561
- };
1562
- self.getVisibleColumnIndexOf = function (columnIndex) {
1563
- var x,
1564
- s = self.getVisibleSchema();
1565
- for (x = 0; x < s.length; x += 1) {
1566
- if (s[x].columnIndex === columnIndex) {
1567
- return x;
1568
- }
1569
- }
1570
- return -1;
1571
- };
1572
- self.paste = function (event) {
1573
- if (!self.attributes.editable) {
1574
- return;
1575
- }
1576
-
1577
- var defaultPrevented = self.dispatchEvent('beforepaste', {
1578
- NativeEvent: event,
1579
- });
1580
-
1581
- if (defaultPrevented) {
1582
- return;
1583
- }
1584
-
1585
- var clipboardItems = new Map(
1586
- Array.from(event.clipboardData.items).map((item) => [item.type, item]),
1587
- );
1588
-
1589
- // Supported MIME types, in order of preference:
1590
- var supportedMimeTypes = ['text/html', 'text/csv', 'text/plain'];
1591
-
1592
- // The clipboard will often contain the same data in multiple formats,
1593
- // which can be used depending on the context in which it's pasted. Here
1594
- // we'll prefere more structured (HTML/CSV) over less structured, when
1595
- // available, so we try to find those first:
1596
- var pasteableItems = supportedMimeTypes
1597
- .map((mimeType) => clipboardItems.get(mimeType))
1598
- .filter((item) => !!item); // filter out not-found MIME types (= undefined)
1599
-
1600
- if (pasteableItems.length === 0) {
1601
- console.warn(
1602
- 'Cannot find supported clipboard data type. Supported types are:',
1603
- supportedMimeTypes.join(', '),
1604
- );
1605
- return;
1606
- }
1607
-
1608
- var itemToPaste = pasteableItems[0];
1609
-
1610
- // itemToPaste is cleared once accessed (getData or getAsString),
1611
- // so we need to store the type here, before reading its value:
1612
- var pasteType = itemToPaste.type;
1613
-
1614
- itemToPaste.getAsString(function (pasteValue) {
1615
- self.pasteData(
1616
- pasteValue,
1617
- pasteType,
1618
- self.activeCell.rowIndex,
1619
- self.getVisibleColumnIndexOf(self.activeCell.columnIndex),
1620
- );
1621
-
1622
- self.draw();
1623
- });
1624
- };
1625
- self.cut = function (e) {
1626
- self.copy(e);
1627
- self.forEachSelectedCell(function (data, index, colName) {
1628
- data[index][colName] = '';
1629
- });
1630
- };
1631
- self.copy = function (e) {
1632
- if (self.dispatchEvent('copy', { NativeEvent: e })) {
1633
- return;
1634
- }
1635
- if (!self.hasFocus || !e.clipboardData) {
1636
- return;
1637
- }
1638
- var t,
1639
- d,
1640
- data = self.data || [],
1641
- tableRows = [],
1642
- textRows = [],
1643
- outputHeaders = {},
1644
- outputHeaderKeys,
1645
- sData = self.getSelectedData(),
1646
- s = self.getSchema();
1647
- function htmlSafe(v) {
1648
- return v.replace(/</g, '&lt;').replace(/>/g, '&gt;');
1649
- }
1650
- function fCopyCell(v) {
1651
- v = v === null || v === undefined ? '' : v;
1652
- return '<td>' + (typeof v === 'string' ? htmlSafe(v) : v) + '</td>';
1653
- }
1654
- function addHeaders(headers, useHtml) {
1655
- if (!s.length || headers.length < 2) {
1656
- return '';
1657
- }
1658
- var h = [];
1659
- if (useHtml) {
1660
- h.push('<tr>');
1661
- }
1662
- s.forEach(function (column, columnIndex) {
1663
- // intentional redefinition of column
1664
- column = s[self.orders.columns[columnIndex]];
1665
- if (!column.hidden && headers.indexOf(column.name) !== -1) {
1666
- var ev = { NativeEvent: e, column: column };
1667
- if (self.dispatchEvent('copyonschema', ev)) {
1668
- column = ev.column;
1669
- }
1670
-
1671
- var hVal = column.name || column.title || '';
1672
- if (useHtml) {
1673
- h.push('<th>' + htmlSafe(hVal) + '</th>');
1674
- } else {
1675
- h.push('"' + hVal.replace(/"/g, '""') + '"');
1676
- }
1677
- }
1678
- });
1679
- h.push(useHtml ? '</tr>' : '\n');
1680
- return h.join(useHtml ? '' : ',');
1681
- }
1682
- function addCellValue(val, trRow, textRow, column) {
1683
- // escape strings
1684
- if (val !== null && val !== false && val !== undefined && val.replace) {
1685
- trRow.push(fCopyCell(val));
1686
- textRow.push('"' + val.replace(/"/g, '""') + '"');
1687
- return;
1688
- }
1689
- if (val !== undefined) {
1690
- textRow.push(val);
1691
- trRow.push(fCopyCell(val));
1692
- return;
1693
- }
1694
- // issue #66
1695
- textRow.push('');
1696
- trRow.push('<td>&nbsp;</td>');
1697
- }
1698
- if (sData.length > 0) {
1699
- sData.forEach(function (row) {
1700
- var rowKeys = Object.keys(row);
1701
- if (row) {
1702
- var trRow = [],
1703
- textRow = [],
1704
- sSorted = [];
1705
- // HACK: https://github.com/TonyGermaneri/canvas-datagrid/issues/181
1706
- // I can't use sort here or O(1), so hacks
1707
- s.forEach(function (column, columnIndex) {
1708
- sSorted.push(s[self.orders.columns[columnIndex]]);
1709
- });
1710
- sSorted.forEach(function (column, columnIndex) {
1711
- if (rowKeys.indexOf(column.name) !== -1) {
1712
- outputHeaders[column.name] = true;
1713
- // escape strings
1714
- addCellValue(row[column.name], trRow, textRow, column);
1715
- }
1716
- });
1717
- tableRows.push(trRow.join(''));
1718
- textRows.push(textRow.join(','));
1719
- }
1720
- });
1721
- outputHeaderKeys = Object.keys(outputHeaders);
1722
- t = addHeaders(outputHeaderKeys) + textRows.join('\n');
1723
- d =
1724
- '<table>' +
1725
- addHeaders(outputHeaderKeys, true) +
1726
- '<tr>' +
1727
- tableRows.join('</tr><tr>') +
1728
- '</tr></table>';
1729
- if (outputHeaderKeys.length === 1) {
1730
- // if there was only one cell selected, remove the quotes from the string
1731
- t = t.substring(1, t.length - 1);
1732
- }
1733
- e.clipboardData.setData('text/html', d);
1734
- e.clipboardData.setData('text/plain', t);
1735
- e.clipboardData.setData('text/csv', t);
1736
- e.clipboardData.setData('application/json', JSON.stringify(sData));
1737
- e.preventDefault();
1738
- }
1739
- };
1740
- return;
1741
- }