virtual-excel-table 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3391 @@
1
+ /*
2
+ * VirtualExcelTable 2.2.0
3
+ * Dependency-free spreadsheet-like paste/delete/undo behavior for HTML tables.
4
+ * ES5 syntax is used so the file can be included in legacy JSP applications.
5
+ */
6
+ (function (root, factory) {
7
+ var api = factory(root, root && root.document);
8
+
9
+ if (typeof module === 'object' && module.exports) {
10
+ module.exports = api;
11
+ }
12
+
13
+ if (root) {
14
+ root.VirtualExcelTable = api;
15
+ }
16
+ }(typeof window !== 'undefined' ? window :
17
+ (typeof global !== 'undefined' ? global : this), function (root, document) {
18
+ 'use strict';
19
+
20
+ var VERSION = '2.2.0';
21
+ var INSTANCE_KEY = '__virtualExcelTableInstance__';
22
+ var STYLE_ID = 'virtual-excel-table-style';
23
+ var TABLE_CLASS = 'vet-table';
24
+ var DRAGGING_CLASS = 'vet-dragging';
25
+ var CELL_CLASS = 'vet-cell';
26
+ var SELECTED_CLASS = 'vet-selected';
27
+ var ACTIVE_CLASS = 'vet-active';
28
+ var READONLY_CLASS = 'vet-readonly';
29
+ var HEADER_CLASS = 'vet-selection-header';
30
+ var HEADER_SELECTED_CLASS = 'vet-header-selected';
31
+
32
+ var DEFAULTS = {
33
+ cellSelector: 'tbody td',
34
+ rowSelector: 'tbody tr',
35
+ rowHeaderSelector: 'tbody th',
36
+ columnHeaderSelector: 'thead th',
37
+ minRows: 1,
38
+ historyLimit: 50,
39
+ overflow: 'clip',
40
+ autoAddRowsOnPaste: true,
41
+ pasteMode: 'grid',
42
+ interactionMode: 'spreadsheet',
43
+ pasteSingleToSelection: true,
44
+ pasteRepeatToSelection: true,
45
+ dispatchEvents: true,
46
+ injectStyles: true,
47
+ readOnly: null,
48
+ rowReadOnly: null,
49
+ getValue: null,
50
+ setValue: null,
51
+ beforeChange: null,
52
+ afterChange: null,
53
+ beforeRowsDelete: null,
54
+ beforeRowAdd: null,
55
+ rowFactory: null,
56
+ reindexRows: null,
57
+ afterRowsChange: null,
58
+ onSelectionChange: null,
59
+ onError: null
60
+ };
61
+
62
+ function own(object, key) {
63
+ return Object.prototype.hasOwnProperty.call(object, key);
64
+ }
65
+
66
+ function mergeOptions(options) {
67
+ var result = {};
68
+ var key;
69
+
70
+ for (key in DEFAULTS) {
71
+ if (own(DEFAULTS, key)) {
72
+ result[key] = DEFAULTS[key];
73
+ }
74
+ }
75
+
76
+ options = options || {};
77
+ for (key in options) {
78
+ if (own(options, key)) {
79
+ result[key] = options[key];
80
+ }
81
+ }
82
+
83
+ return result;
84
+ }
85
+
86
+ function addClass(element, name) {
87
+ if (!element || hasClass(element, name)) {
88
+ return;
89
+ }
90
+ element.className = element.className ? element.className + ' ' + name : name;
91
+ }
92
+
93
+ function removeClass(element, name) {
94
+ var value;
95
+
96
+ if (!element || !element.className) {
97
+ return;
98
+ }
99
+
100
+ value = (' ' + element.className + ' ').replace(/\s+/g, ' ');
101
+ while (value.indexOf(' ' + name + ' ') !== -1) {
102
+ value = value.replace(' ' + name + ' ', ' ');
103
+ }
104
+ element.className = value.replace(/^\s+|\s+$/g, '');
105
+ }
106
+
107
+ function hasClass(element, name) {
108
+ if (!element || !element.className) {
109
+ return false;
110
+ }
111
+ return (' ' + element.className + ' ').replace(/\s+/g, ' ')
112
+ .indexOf(' ' + name + ' ') !== -1;
113
+ }
114
+
115
+ function indexOf(array, value) {
116
+ var i;
117
+ for (i = 0; i < array.length; i += 1) {
118
+ if (array[i] === value) {
119
+ return i;
120
+ }
121
+ }
122
+ return -1;
123
+ }
124
+
125
+ function matchesSelector(element, selector) {
126
+ var matcher = element.matches || element.matchesSelector ||
127
+ element.msMatchesSelector || element.webkitMatchesSelector ||
128
+ element.mozMatchesSelector;
129
+
130
+ if (!matcher) {
131
+ throw new Error('VirtualExcelTable: 이 브라우저는 CSS selector 검사를 지원하지 않습니다.');
132
+ }
133
+
134
+ return matcher.call(element, selector);
135
+ }
136
+
137
+ function owningTable(element) {
138
+ var current = element;
139
+
140
+ while (current && current.nodeType === 1) {
141
+ if (String(current.tagName).toUpperCase() === 'TABLE') {
142
+ return current;
143
+ }
144
+ current = current.parentNode;
145
+ }
146
+
147
+ return null;
148
+ }
149
+
150
+ function contains(parent, child) {
151
+ if (!parent || !child) {
152
+ return false;
153
+ }
154
+ if (parent === child) {
155
+ return true;
156
+ }
157
+ if (parent.contains) {
158
+ return parent.contains(child);
159
+ }
160
+
161
+ while (child) {
162
+ if (child === parent) {
163
+ return true;
164
+ }
165
+ child = child.parentNode;
166
+ }
167
+ return false;
168
+ }
169
+
170
+ function setText(element, value) {
171
+ if (typeof element.textContent !== 'undefined') {
172
+ element.textContent = value;
173
+ } else {
174
+ element.innerText = value;
175
+ }
176
+ }
177
+
178
+ function getText(element) {
179
+ if (typeof element.textContent !== 'undefined') {
180
+ return element.textContent;
181
+ }
182
+ return element.innerText;
183
+ }
184
+
185
+ function isContentEditable(element) {
186
+ var attribute;
187
+
188
+ if (!element || element.nodeType !== 1) {
189
+ return false;
190
+ }
191
+
192
+ attribute = element.getAttribute('contenteditable');
193
+ return attribute === '' || String(attribute).toLowerCase() === 'true';
194
+ }
195
+
196
+ function isValueControl(element) {
197
+ var tag;
198
+ var type;
199
+
200
+ if (!element || element.nodeType !== 1) {
201
+ return false;
202
+ }
203
+
204
+ tag = String(element.tagName).toUpperCase();
205
+ if (tag === 'TEXTAREA' || tag === 'SELECT') {
206
+ return true;
207
+ }
208
+ if (tag !== 'INPUT') {
209
+ return false;
210
+ }
211
+
212
+ type = String(element.type || 'text').toLowerCase();
213
+ return type !== 'hidden' && type !== 'button' && type !== 'submit' &&
214
+ type !== 'reset' && type !== 'checkbox' && type !== 'radio' &&
215
+ type !== 'file' && type !== 'image';
216
+ }
217
+
218
+ function isInteractiveElement(element) {
219
+ var tag;
220
+
221
+ if (!element || element.nodeType !== 1) {
222
+ return false;
223
+ }
224
+ tag = String(element.tagName).toUpperCase();
225
+ return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' ||
226
+ tag === 'BUTTON' || tag === 'OPTION' || tag === 'LABEL' ||
227
+ (tag === 'A' && element.getAttribute('href') !== null) ||
228
+ isContentEditable(element);
229
+ }
230
+
231
+ function hasUnsafeCellMarkup(cell) {
232
+ var children = cell.children || cell.childNodes;
233
+ var i;
234
+ var tag;
235
+
236
+ for (i = 0; i < children.length; i += 1) {
237
+ if (children[i].nodeType !== 1) {
238
+ continue;
239
+ }
240
+ tag = String(children[i].tagName).toUpperCase();
241
+ /* A BR-only cell is safe to replace with plain text. */
242
+ if (tag !== 'BR') {
243
+ return true;
244
+ }
245
+ }
246
+ return false;
247
+ }
248
+
249
+ function prepareClonedRow(row) {
250
+ var elements = row.getElementsByTagName('*');
251
+ var element;
252
+ var tag;
253
+ var type;
254
+ var i;
255
+
256
+ row.removeAttribute('id');
257
+ removeClass(row, SELECTED_CLASS);
258
+ removeClass(row, ACTIVE_CLASS);
259
+ removeClass(row, HEADER_SELECTED_CLASS);
260
+ for (i = 0; i < elements.length; i += 1) {
261
+ element = elements[i];
262
+ tag = String(element.tagName).toUpperCase();
263
+ element.removeAttribute('id');
264
+ removeClass(element, SELECTED_CLASS);
265
+ removeClass(element, ACTIVE_CLASS);
266
+ removeClass(element, READONLY_CLASS);
267
+ removeClass(element, CELL_CLASS);
268
+ removeClass(element, HEADER_CLASS);
269
+ removeClass(element, HEADER_SELECTED_CLASS);
270
+ if (tag === 'LABEL') {
271
+ element.removeAttribute('for');
272
+ } else if (tag === 'INPUT') {
273
+ type = String(element.type || 'text').toLowerCase();
274
+ if (type === 'checkbox' || type === 'radio') {
275
+ element.checked = false;
276
+ element.defaultChecked = false;
277
+ } else if (type !== 'button' && type !== 'submit' && type !== 'reset' &&
278
+ type !== 'image') {
279
+ try {
280
+ element.value = '';
281
+ element.defaultValue = '';
282
+ } catch (ignoreFileInput) {
283
+ /* A cloned file input is already empty in supported browsers. */
284
+ }
285
+ }
286
+ } else if (tag === 'TEXTAREA') {
287
+ element.value = '';
288
+ element.defaultValue = '';
289
+ setText(element, '');
290
+ } else if (tag === 'SELECT') {
291
+ element.selectedIndex = element.options.length ? 0 : -1;
292
+ } else if (isContentEditable(element)) {
293
+ setText(element, '');
294
+ }
295
+ }
296
+
297
+ for (i = 0; i < row.cells.length; i += 1) {
298
+ if (String(row.cells[i].tagName).toUpperCase() === 'TD' &&
299
+ !row.cells[i].children.length) {
300
+ setText(row.cells[i], '');
301
+ }
302
+ }
303
+ return row;
304
+ }
305
+
306
+ function getCellTarget(cell, table) {
307
+ var markedElements = cell.querySelectorAll ?
308
+ cell.querySelectorAll('[data-excel-target]') : [];
309
+ var elements;
310
+ var i;
311
+ var element;
312
+
313
+ for (i = 0; i < markedElements.length; i += 1) {
314
+ if (owningTable(markedElements[i]) === table) {
315
+ return markedElements[i];
316
+ }
317
+ }
318
+
319
+ elements = cell.getElementsByTagName('*');
320
+ for (i = 0; i < elements.length; i += 1) {
321
+ element = elements[i];
322
+ if (owningTable(element) === table &&
323
+ (isValueControl(element) || isContentEditable(element))) {
324
+ return element;
325
+ }
326
+ }
327
+
328
+ return null;
329
+ }
330
+
331
+ function fireEvent(element, type) {
332
+ var event;
333
+
334
+ if (!document || !element || !element.dispatchEvent) {
335
+ return;
336
+ }
337
+
338
+ event = document.createEvent('HTMLEvents');
339
+ event.initEvent(type, true, false);
340
+ element.dispatchEvent(event);
341
+ }
342
+
343
+ function placeCaretAtEnd(element) {
344
+ var length;
345
+ var range;
346
+ var selection;
347
+
348
+ if (!element) {
349
+ return;
350
+ }
351
+ if (typeof element.value !== 'undefined' && element.setSelectionRange) {
352
+ try {
353
+ length = String(element.value || '').length;
354
+ element.setSelectionRange(length, length);
355
+ return;
356
+ } catch (ignoreInputType) {
357
+ /* number/date and some legacy input types reject setSelectionRange. */
358
+ }
359
+ }
360
+ if (isContentEditable(element) && document.createRange && root.getSelection) {
361
+ try {
362
+ range = document.createRange();
363
+ range.selectNodeContents(element);
364
+ range.collapse(false);
365
+ selection = root.getSelection();
366
+ selection.removeAllRanges();
367
+ selection.addRange(range);
368
+ } catch (ignoreContentEditable) {
369
+ /* Focus alone is still usable when caret APIs are unavailable. */
370
+ }
371
+ }
372
+ }
373
+
374
+ function printableKey(event) {
375
+ var key = event.key;
376
+ var code;
377
+ var character;
378
+
379
+ if (event.ctrlKey || event.metaKey || event.altKey) {
380
+ return null;
381
+ }
382
+ if (key && String(key).length === 1) {
383
+ return String(key);
384
+ }
385
+
386
+ code = event.which || event.keyCode;
387
+ if (code === 32) {
388
+ return ' ';
389
+ }
390
+ if (code >= 65 && code <= 90) {
391
+ character = String.fromCharCode(code);
392
+ return event.shiftKey ? character : character.toLowerCase();
393
+ }
394
+ if (code >= 48 && code <= 57 && !event.shiftKey) {
395
+ return String.fromCharCode(code);
396
+ }
397
+ return null;
398
+ }
399
+
400
+ function injectDefaultStyles() {
401
+ var style;
402
+ var css;
403
+ var head;
404
+
405
+ if (!document || document.getElementById(STYLE_ID)) {
406
+ return;
407
+ }
408
+
409
+ css =
410
+ '.' + TABLE_CLASS + ' .' + CELL_CLASS + '{cursor:cell;}' +
411
+ '.' + TABLE_CLASS + ' .' + SELECTED_CLASS +
412
+ '{background-color:#dbeafe!important;}' +
413
+ '.' + TABLE_CLASS + ' .' + ACTIVE_CLASS +
414
+ '{box-shadow:inset 0 0 0 2px #2563eb!important;outline:2px solid #2563eb;outline-offset:-2px;}' +
415
+ '.' + TABLE_CLASS + ' .' + SELECTED_CLASS + ' input,' +
416
+ '.' + TABLE_CLASS + ' .' + SELECTED_CLASS + ' textarea,' +
417
+ '.' + TABLE_CLASS + ' .' + SELECTED_CLASS + ' select,' +
418
+ '.' + TABLE_CLASS + ' .' + SELECTED_CLASS + ' [contenteditable]' +
419
+ '{background-color:#dbeafe!important;}' +
420
+ '.' + TABLE_CLASS + ' .' + READONLY_CLASS + '.' + SELECTED_CLASS + ' input,' +
421
+ '.' + TABLE_CLASS + ' .' + READONLY_CLASS + '.' + SELECTED_CLASS + ' textarea,' +
422
+ '.' + TABLE_CLASS + ' .' + READONLY_CLASS + '.' + SELECTED_CLASS + ' select,' +
423
+ '.' + TABLE_CLASS + ' .' + READONLY_CLASS + '.' + SELECTED_CLASS + ' [contenteditable]' +
424
+ '{background-color:#e5e7eb!important;}' +
425
+ '.' + TABLE_CLASS + ' .' + READONLY_CLASS + '.' + SELECTED_CLASS +
426
+ '{background-color:#e5e7eb!important;}' +
427
+ '.' + TABLE_CLASS + ' .' + HEADER_CLASS + '{cursor:pointer;}' +
428
+ '.' + TABLE_CLASS + ' .' + HEADER_SELECTED_CLASS +
429
+ '{background-color:#bfdbfe!important;box-shadow:inset 0 0 0 2px #2563eb!important;}' +
430
+ '.' + TABLE_CLASS + '.' + DRAGGING_CLASS + ',' +
431
+ '.' + TABLE_CLASS + '.' + DRAGGING_CLASS + ' *' +
432
+ '{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;}';
433
+
434
+ style = document.createElement('style');
435
+ style.id = STYLE_ID;
436
+ style.type = 'text/css';
437
+ if (style.styleSheet) {
438
+ style.styleSheet.cssText = css;
439
+ } else {
440
+ style.appendChild(document.createTextNode(css));
441
+ }
442
+
443
+ head = document.getElementsByTagName('head')[0] || document.documentElement;
444
+ head.appendChild(style);
445
+ }
446
+
447
+ /*
448
+ * Parses Excel/LibreOffice/Google Sheets clipboard TSV.
449
+ * Quoted tabs, quoted line breaks and doubled quotes are preserved.
450
+ */
451
+ function parseClipboardText(text) {
452
+ var rows = [];
453
+ var row = [];
454
+ var field = '';
455
+ var inQuotes = false;
456
+ var endedWithRowDelimiter = false;
457
+ var i;
458
+ var character;
459
+ var next;
460
+
461
+ text = text === null || typeof text === 'undefined' ? '' : String(text);
462
+ if (text.charCodeAt(0) === 0xFEFF) {
463
+ text = text.substring(1);
464
+ }
465
+
466
+ for (i = 0; i < text.length; i += 1) {
467
+ character = text.charAt(i);
468
+ next = i + 1 < text.length ? text.charAt(i + 1) : '';
469
+
470
+ if (inQuotes) {
471
+ if (character === '"') {
472
+ if (next === '"') {
473
+ field += '"';
474
+ i += 1;
475
+ } else {
476
+ inQuotes = false;
477
+ }
478
+ } else if (character === '\r') {
479
+ field += '\n';
480
+ if (next === '\n') {
481
+ i += 1;
482
+ }
483
+ } else {
484
+ field += character;
485
+ }
486
+ endedWithRowDelimiter = false;
487
+ continue;
488
+ }
489
+
490
+ if (character === '"' && field === '') {
491
+ inQuotes = true;
492
+ endedWithRowDelimiter = false;
493
+ } else if (character === '\t') {
494
+ row.push(field);
495
+ field = '';
496
+ endedWithRowDelimiter = false;
497
+ } else if (character === '\r' || character === '\n') {
498
+ row.push(field);
499
+ rows.push(row);
500
+ row = [];
501
+ field = '';
502
+ if (character === '\r' && next === '\n') {
503
+ i += 1;
504
+ }
505
+ endedWithRowDelimiter = true;
506
+ } else {
507
+ field += character;
508
+ endedWithRowDelimiter = false;
509
+ }
510
+ }
511
+
512
+ row.push(field);
513
+ rows.push(row);
514
+
515
+ /* Excel normally appends one CRLF after the copied range. */
516
+ if (endedWithRowDelimiter && rows.length > 1 &&
517
+ rows[rows.length - 1].length === 1 && rows[rows.length - 1][0] === '') {
518
+ rows.pop();
519
+ }
520
+
521
+ return rows;
522
+ }
523
+
524
+ function escapeClipboardField(value) {
525
+ value = value === null || typeof value === 'undefined' ? '' : String(value);
526
+ if (/[\t\r\n"]/.test(value)) {
527
+ return '"' + value.replace(/"/g, '""') + '"';
528
+ }
529
+ return value;
530
+ }
531
+
532
+ function resolveTables(target) {
533
+ var result = [];
534
+ var nodes;
535
+ var i;
536
+
537
+ if (!document) {
538
+ throw new Error('VirtualExcelTable: DOM이 있는 브라우저에서만 테이블에 연결할 수 있습니다.');
539
+ }
540
+
541
+ if (typeof target === 'string') {
542
+ nodes = document.querySelectorAll(target);
543
+ for (i = 0; i < nodes.length; i += 1) {
544
+ result.push(nodes[i]);
545
+ }
546
+ return result;
547
+ }
548
+
549
+ if (target && target.nodeType === 1) {
550
+ result.push(target);
551
+ return result;
552
+ }
553
+
554
+ if (target && typeof target.length === 'number') {
555
+ for (i = 0; i < target.length; i += 1) {
556
+ if (target[i] && target[i].nodeType === 1) {
557
+ result.push(target[i]);
558
+ }
559
+ }
560
+ }
561
+
562
+ return result;
563
+ }
564
+
565
+ function ensureTable(element) {
566
+ if (!element || String(element.tagName).toUpperCase() !== 'TABLE') {
567
+ throw new Error('VirtualExcelTable: 대상은 TABLE 요소여야 합니다.');
568
+ }
569
+ }
570
+
571
+ function bind(instance, method) {
572
+ return function (event) {
573
+ return method.call(instance, event || root.event);
574
+ };
575
+ }
576
+
577
+ function keyName(event) {
578
+ var code;
579
+ var key;
580
+
581
+ if (event.key) {
582
+ key = String(event.key).toLowerCase();
583
+ if (key === 'left' || key === 'up' || key === 'right' || key === 'down') {
584
+ return 'arrow' + key;
585
+ }
586
+ if (key === 'del') {
587
+ return 'delete';
588
+ }
589
+ if (key === 'esc') {
590
+ return 'escape';
591
+ }
592
+ return key;
593
+ }
594
+
595
+ code = event.which || event.keyCode;
596
+ if (code === 8) { return 'backspace'; }
597
+ if (code === 9) { return 'tab'; }
598
+ if (code === 13) { return 'enter'; }
599
+ if (code === 27) { return 'escape'; }
600
+ if (code === 37) { return 'arrowleft'; }
601
+ if (code === 38) { return 'arrowup'; }
602
+ if (code === 39) { return 'arrowright'; }
603
+ if (code === 40) { return 'arrowdown'; }
604
+ if (code === 46) { return 'delete'; }
605
+ if (code === 89) { return 'y'; }
606
+ if (code === 90) { return 'z'; }
607
+ if (code === 109 || code === 189) { return '-'; }
608
+ if (code === 113) { return 'f2'; }
609
+ return '';
610
+ }
611
+
612
+ function Grid(table, options) {
613
+ ensureTable(table);
614
+
615
+ this.table = table;
616
+ this.options = mergeOptions(options);
617
+ this.selectedCells = [];
618
+ this.selectedHeaders = [];
619
+ this.knownHeaders = [];
620
+ this.selectionType = 'cells';
621
+ this.anchorCell = null;
622
+ this.activeCell = null;
623
+ this.undoStack = [];
624
+ this.redoStack = [];
625
+ this.knownCells = [];
626
+ this.dragging = false;
627
+ this.dragStartCell = null;
628
+ this.dragSelectionType = null;
629
+ this.dragStartIndex = null;
630
+ this.editingCell = null;
631
+ this.editingTarget = null;
632
+ this.editingStartValue = null;
633
+ this.editingSelectionBefore = null;
634
+ this.editingMode = null;
635
+ this.endingEdit = false;
636
+ this.destroyed = false;
637
+ this.originalTabIndex = table.getAttribute('tabindex');
638
+ this.originalDataEnabled = table.getAttribute('data-virtual-excel-table');
639
+
640
+ if (this.options.overflow !== 'clip' && this.options.overflow !== 'reject') {
641
+ throw new Error('VirtualExcelTable: overflow 옵션은 "clip" 또는 "reject"여야 합니다.');
642
+ }
643
+ if (this.options.pasteMode !== 'grid' && this.options.pasteMode !== 'auto' &&
644
+ this.options.pasteMode !== 'native') {
645
+ throw new Error('VirtualExcelTable: pasteMode 옵션은 "grid", "auto", "native"여야 합니다.');
646
+ }
647
+ if (this.options.interactionMode !== 'spreadsheet' &&
648
+ this.options.interactionMode !== 'form') {
649
+ throw new Error(
650
+ 'VirtualExcelTable: interactionMode 옵션은 "spreadsheet" 또는 "form"이어야 합니다.'
651
+ );
652
+ }
653
+ if (isNaN(parseInt(this.options.minRows, 10)) ||
654
+ parseInt(this.options.minRows, 10) < 0) {
655
+ throw new Error('VirtualExcelTable: minRows 옵션은 0 이상의 숫자여야 합니다.');
656
+ }
657
+
658
+ if (this.options.injectStyles) {
659
+ injectDefaultStyles();
660
+ }
661
+
662
+ addClass(table, TABLE_CLASS);
663
+ table.setAttribute('data-virtual-excel-table', 'true');
664
+ if (this.originalTabIndex === null) {
665
+ table.setAttribute('tabindex', '0');
666
+ }
667
+
668
+ this.handlers = {
669
+ mousedown: bind(this, this._onMouseDown),
670
+ dblclick: bind(this, this._onDoubleClick),
671
+ mouseover: bind(this, this._onMouseOver),
672
+ mouseup: bind(this, this._onMouseUp),
673
+ focusin: bind(this, this._onFocusIn),
674
+ focusout: bind(this, this._onFocusOut),
675
+ keydown: bind(this, this._onKeyDown),
676
+ paste: bind(this, this._onPaste),
677
+ copy: bind(this, this._onCopy)
678
+ };
679
+
680
+ table.addEventListener('mousedown', this.handlers.mousedown, false);
681
+ table.addEventListener('dblclick', this.handlers.dblclick, false);
682
+ table.addEventListener('mouseover', this.handlers.mouseover, false);
683
+ table.addEventListener('focusin', this.handlers.focusin, false);
684
+ table.addEventListener('focusout', this.handlers.focusout, false);
685
+ table.addEventListener('keydown', this.handlers.keydown, false);
686
+ table.addEventListener('paste', this.handlers.paste, false);
687
+ table.addEventListener('copy', this.handlers.copy, false);
688
+ document.addEventListener('mouseup', this.handlers.mouseup, false);
689
+
690
+ this.refresh();
691
+ }
692
+
693
+ Grid.prototype._getGrid = function () {
694
+ var grid = [];
695
+ var rows = this.table.rows;
696
+ var currentCells = [];
697
+ var row;
698
+ var gridRow;
699
+ var cell;
700
+ var i;
701
+ var j;
702
+
703
+ for (i = 0; i < rows.length; i += 1) {
704
+ row = rows[i];
705
+ gridRow = [];
706
+ for (j = 0; j < row.cells.length; j += 1) {
707
+ cell = row.cells[j];
708
+ if (owningTable(cell) === this.table &&
709
+ matchesSelector(cell, this.options.cellSelector)) {
710
+ gridRow.push(cell);
711
+ currentCells.push(cell);
712
+ addClass(cell, CELL_CLASS);
713
+ if (this._isReadOnly(cell)) {
714
+ addClass(cell, READONLY_CLASS);
715
+ } else {
716
+ removeClass(cell, READONLY_CLASS);
717
+ }
718
+ }
719
+ }
720
+ if (gridRow.length > 0) {
721
+ grid.push(gridRow);
722
+ }
723
+ }
724
+
725
+ for (i = 0; i < this.knownCells.length; i += 1) {
726
+ if (indexOf(currentCells, this.knownCells[i]) === -1) {
727
+ removeClass(this.knownCells[i], CELL_CLASS);
728
+ removeClass(this.knownCells[i], SELECTED_CLASS);
729
+ removeClass(this.knownCells[i], ACTIVE_CLASS);
730
+ removeClass(this.knownCells[i], READONLY_CLASS);
731
+ }
732
+ }
733
+ this.knownCells = currentCells;
734
+ this._refreshSelectionHeaders(grid);
735
+
736
+ return grid;
737
+ };
738
+
739
+ Grid.prototype._getHeaderInfo = function (target, grid) {
740
+ var header = target;
741
+ var row;
742
+ var section;
743
+ var maxColumns = 0;
744
+ var offset;
745
+ var index;
746
+ var i;
747
+
748
+ if (header && header.nodeType !== 1) {
749
+ header = header.parentNode;
750
+ }
751
+ while (header && header !== this.table &&
752
+ String(header.tagName || '').toUpperCase() !== 'TH') {
753
+ header = header.parentNode;
754
+ }
755
+ if (!header || header === this.table || owningTable(header) !== this.table) {
756
+ return null;
757
+ }
758
+
759
+ row = header.parentNode;
760
+ section = row && row.parentNode;
761
+ if (!row || !section) {
762
+ return null;
763
+ }
764
+
765
+ for (i = 0; i < grid.length; i += 1) {
766
+ maxColumns = Math.max(maxColumns, grid[i].length);
767
+ }
768
+
769
+ if (String(section.tagName).toUpperCase() === 'TBODY' &&
770
+ (header.getAttribute('data-excel-row-header') !== null ||
771
+ matchesSelector(header, this.options.rowHeaderSelector))) {
772
+ for (i = 0; i < grid.length; i += 1) {
773
+ if (grid[i].length && grid[i][0].parentNode === row) {
774
+ return { type: 'rows', index: i, element: header };
775
+ }
776
+ }
777
+ }
778
+
779
+ if (String(section.tagName).toUpperCase() === 'THEAD' && maxColumns > 0 &&
780
+ header.getAttribute('data-excel-corner') !== null) {
781
+ return { type: 'all', index: 0, element: header };
782
+ }
783
+
784
+ if (String(section.tagName).toUpperCase() === 'THEAD' && maxColumns > 0 &&
785
+ (header.getAttribute('data-excel-column') !== null ||
786
+ matchesSelector(header, this.options.columnHeaderSelector))) {
787
+ if (header.getAttribute('data-excel-column') !== null) {
788
+ index = parseInt(header.getAttribute('data-excel-column'), 10);
789
+ if (!isNaN(index) && index >= 0 && index < maxColumns) {
790
+ return { type: 'columns', index: index, element: header };
791
+ }
792
+ return null;
793
+ }
794
+ offset = Math.max(0, row.cells.length - maxColumns);
795
+ index = header.cellIndex;
796
+ if (index < offset) {
797
+ return { type: 'all', index: 0, element: header };
798
+ }
799
+ index = index - offset;
800
+ if (index >= 0 && index < maxColumns) {
801
+ return { type: 'columns', index: index, element: header };
802
+ }
803
+ }
804
+ return null;
805
+ };
806
+
807
+ Grid.prototype._refreshSelectionHeaders = function (grid) {
808
+ var headers = this.table.getElementsByTagName('th');
809
+ var current = [];
810
+ var info;
811
+ var i;
812
+
813
+ for (i = 0; i < headers.length; i += 1) {
814
+ info = this._getHeaderInfo(headers[i], grid);
815
+ if (info) {
816
+ current.push(headers[i]);
817
+ addClass(headers[i], HEADER_CLASS);
818
+ }
819
+ }
820
+ for (i = 0; i < this.knownHeaders.length; i += 1) {
821
+ if (indexOf(current, this.knownHeaders[i]) === -1) {
822
+ removeClass(this.knownHeaders[i], HEADER_CLASS);
823
+ removeClass(this.knownHeaders[i], HEADER_SELECTED_CLASS);
824
+ }
825
+ }
826
+ this.knownHeaders = current;
827
+ };
828
+
829
+ Grid.prototype._clearHeaderSelectionClasses = function () {
830
+ var i;
831
+ for (i = 0; i < this.selectedHeaders.length; i += 1) {
832
+ removeClass(this.selectedHeaders[i], HEADER_SELECTED_CLASS);
833
+ }
834
+ this.selectedHeaders = [];
835
+ };
836
+
837
+ Grid.prototype._applyHeaderSelectionClasses = function (type, bounds, grid) {
838
+ var info;
839
+ var selected;
840
+ var i;
841
+
842
+ this._clearHeaderSelectionClasses();
843
+ if (type === 'cells' || !bounds) {
844
+ return;
845
+ }
846
+ for (i = 0; i < this.knownHeaders.length; i += 1) {
847
+ info = this._getHeaderInfo(this.knownHeaders[i], grid);
848
+ selected = type === 'all' ||
849
+ (type === 'rows' && info && info.type === 'rows' &&
850
+ info.index >= bounds.startRow && info.index <= bounds.endRow) ||
851
+ (type === 'columns' && info && info.type === 'columns' &&
852
+ info.index >= bounds.startColumn && info.index <= bounds.endColumn);
853
+ if (selected) {
854
+ this.selectedHeaders.push(this.knownHeaders[i]);
855
+ addClass(this.knownHeaders[i], HEADER_SELECTED_CLASS);
856
+ }
857
+ }
858
+ };
859
+
860
+ Grid.prototype._selectHeaderRange = function (type, startIndex, endIndex, focusTable) {
861
+ var grid = this._getGrid();
862
+ var start;
863
+ var end;
864
+ var firstRow;
865
+ var lastRow;
866
+ var row;
867
+
868
+ if (!grid.length) {
869
+ return false;
870
+ }
871
+ if (type === 'all') {
872
+ lastRow = grid.length - 1;
873
+ return this._setSelection(
874
+ grid[0][0],
875
+ grid[lastRow][grid[lastRow].length - 1],
876
+ focusTable,
877
+ 'all'
878
+ );
879
+ }
880
+
881
+ if (type === 'rows') {
882
+ start = Math.max(0, Math.min(grid.length - 1, startIndex));
883
+ end = Math.max(0, Math.min(grid.length - 1, endIndex));
884
+ return this._setSelection(
885
+ grid[start][0],
886
+ grid[end][grid[end].length - 1],
887
+ focusTable,
888
+ 'rows'
889
+ );
890
+ }
891
+
892
+ if (type === 'columns') {
893
+ start = Math.min(startIndex, endIndex);
894
+ end = Math.max(startIndex, endIndex);
895
+ firstRow = -1;
896
+ lastRow = -1;
897
+ for (row = 0; row < grid.length; row += 1) {
898
+ if (grid[row][start] && grid[row][end]) {
899
+ if (firstRow === -1) {
900
+ firstRow = row;
901
+ }
902
+ lastRow = row;
903
+ }
904
+ }
905
+ if (firstRow === -1) {
906
+ return false;
907
+ }
908
+ return this._setSelection(
909
+ grid[firstRow][start],
910
+ grid[lastRow][end],
911
+ focusTable,
912
+ 'columns'
913
+ );
914
+ }
915
+ return false;
916
+ };
917
+
918
+ Grid.prototype._findCell = function (target) {
919
+ var nearestTable;
920
+ var current;
921
+ var tag;
922
+
923
+ if (!target) {
924
+ return null;
925
+ }
926
+ if (target.nodeType !== 1) {
927
+ target = target.parentNode;
928
+ }
929
+
930
+ nearestTable = owningTable(target);
931
+ if (nearestTable !== this.table) {
932
+ return null;
933
+ }
934
+
935
+ current = target;
936
+ while (current && current !== this.table) {
937
+ tag = String(current.tagName || '').toUpperCase();
938
+ if ((tag === 'TD' || tag === 'TH') &&
939
+ matchesSelector(current, this.options.cellSelector)) {
940
+ return current;
941
+ }
942
+ current = current.parentNode;
943
+ }
944
+
945
+ return null;
946
+ };
947
+
948
+ Grid.prototype._isEditorTarget = function (target, cell) {
949
+ var current = target;
950
+
951
+ if (current && current.nodeType !== 1) {
952
+ current = current.parentNode;
953
+ }
954
+
955
+ while (current && current !== cell.parentNode) {
956
+ if (isInteractiveElement(current)) {
957
+ return true;
958
+ }
959
+ if (current === cell) {
960
+ break;
961
+ }
962
+ current = current.parentNode;
963
+ }
964
+ return false;
965
+ };
966
+
967
+ Grid.prototype._isGridEditorTarget = function (target, cell) {
968
+ var current = target;
969
+
970
+ if (current && current.nodeType !== 1) {
971
+ current = current.parentNode;
972
+ }
973
+
974
+ while (current && current !== cell.parentNode) {
975
+ if ((isValueControl(current) &&
976
+ String(current.tagName).toUpperCase() !== 'SELECT') ||
977
+ isContentEditable(current)) {
978
+ return true;
979
+ }
980
+ if (current === cell) {
981
+ break;
982
+ }
983
+ current = current.parentNode;
984
+ }
985
+ return false;
986
+ };
987
+
988
+ Grid.prototype._usesNativePointerEditor = function (target, cell) {
989
+ var current = target;
990
+
991
+ if (current && current.nodeType !== 1) {
992
+ current = current.parentNode;
993
+ }
994
+
995
+ while (current && current !== cell.parentNode) {
996
+ if (current.getAttribute &&
997
+ current.getAttribute('data-excel-native-editor') !== null) {
998
+ return true;
999
+ }
1000
+ if (current === cell) {
1001
+ break;
1002
+ }
1003
+ current = current.parentNode;
1004
+ }
1005
+ return false;
1006
+ };
1007
+
1008
+ Grid.prototype._clearEditState = function () {
1009
+ this.editingCell = null;
1010
+ this.editingTarget = null;
1011
+ this.editingStartValue = null;
1012
+ this.editingSelectionBefore = null;
1013
+ this.editingMode = null;
1014
+ };
1015
+
1016
+ Grid.prototype._enterEditMode = function (cell, replacementValue, replaceValue) {
1017
+ var target;
1018
+ var tag;
1019
+ var startValue;
1020
+ var selectionBefore;
1021
+
1022
+ if (!cell || this._isReadOnly(cell)) {
1023
+ return false;
1024
+ }
1025
+ target = this._getTarget(cell);
1026
+ if (!target || (!isValueControl(target) && !isContentEditable(target))) {
1027
+ return false;
1028
+ }
1029
+
1030
+ tag = String(target.tagName).toUpperCase();
1031
+ if (replaceValue === true && tag === 'SELECT') {
1032
+ return false;
1033
+ }
1034
+
1035
+ if (this.editingCell && this.editingCell !== cell) {
1036
+ this._finishEditMode(false, false);
1037
+ }
1038
+ selectionBefore = this._snapshotSelection();
1039
+ startValue = this._readValue(cell);
1040
+ this._setSelection(cell, cell, false);
1041
+ this.editingCell = cell;
1042
+ this.editingTarget = target;
1043
+ this.editingStartValue = startValue;
1044
+ this.editingSelectionBefore = selectionBefore;
1045
+ this.editingMode = replaceValue === true ? 'replace' : 'manual';
1046
+ try {
1047
+ target.focus();
1048
+ if (replaceValue === true) {
1049
+ this._writeEditValue(cell, replacementValue);
1050
+ }
1051
+ placeCaretAtEnd(target);
1052
+ } catch (error) {
1053
+ if (replaceValue === true) {
1054
+ try {
1055
+ this._writeValue(cell, startValue, false);
1056
+ } catch (ignoreRollback) {
1057
+ /* Preserve the first error. */
1058
+ }
1059
+ }
1060
+ this._clearEditState();
1061
+ this._reportError(error);
1062
+ return false;
1063
+ }
1064
+ return true;
1065
+ };
1066
+
1067
+ Grid.prototype._writeEditValue = function (cell, value) {
1068
+ var target = this._getTarget(cell) || cell;
1069
+
1070
+ this._writeValue(cell, value, false);
1071
+ if (this.options.dispatchEvents) {
1072
+ fireEvent(target, 'input');
1073
+ }
1074
+ };
1075
+
1076
+ Grid.prototype._finishEditMode = function (cancel, focusTable) {
1077
+ var cell = this.editingCell;
1078
+ var startValue = this.editingStartValue;
1079
+ var selectionBefore = this.editingSelectionBefore;
1080
+ var selectionAfter;
1081
+ var currentValue;
1082
+ var changes;
1083
+ var beforeResult;
1084
+
1085
+ if (!cell || this.endingEdit) {
1086
+ if (focusTable) {
1087
+ this._focusTable();
1088
+ }
1089
+ return false;
1090
+ }
1091
+
1092
+ this.endingEdit = true;
1093
+ selectionAfter = { type: 'cells', anchor: cell, active: cell };
1094
+ currentValue = contains(this.table, cell) ? this._readValue(cell) : startValue;
1095
+
1096
+ if (cancel === true && startValue !== null && contains(this.table, cell)) {
1097
+ try {
1098
+ if (currentValue !== startValue) {
1099
+ this._writeEditValue(cell, startValue);
1100
+ }
1101
+ } catch (error) {
1102
+ this._reportError(error);
1103
+ }
1104
+ } else if (contains(this.table, cell) && currentValue !== startValue) {
1105
+ changes = [{
1106
+ cell: cell,
1107
+ oldValue: startValue,
1108
+ newValue: currentValue
1109
+ }];
1110
+ if (typeof this.options.beforeChange === 'function') {
1111
+ try {
1112
+ beforeResult = this.options.beforeChange(
1113
+ this._copyChanges(changes),
1114
+ { type: 'edit' }
1115
+ );
1116
+ } catch (beforeError) {
1117
+ beforeResult = false;
1118
+ this._reportError(beforeError);
1119
+ }
1120
+ }
1121
+ if (beforeResult === false) {
1122
+ try {
1123
+ this._writeEditValue(cell, startValue);
1124
+ } catch (restoreError) {
1125
+ this._reportError(restoreError);
1126
+ }
1127
+ } else {
1128
+ this._record('edit', changes, selectionBefore, selectionAfter);
1129
+ if (typeof this.options.afterChange === 'function') {
1130
+ try {
1131
+ this.options.afterChange(
1132
+ this._copyChanges(changes),
1133
+ { type: 'edit' }
1134
+ );
1135
+ } catch (afterError) {
1136
+ this._reportError(afterError);
1137
+ }
1138
+ }
1139
+ }
1140
+ }
1141
+
1142
+ this._clearEditState();
1143
+ if (cancel === true && selectionBefore) {
1144
+ this._restoreSelection(selectionBefore, focusTable === true);
1145
+ } else if (focusTable) {
1146
+ this._focusTable();
1147
+ }
1148
+ this.endingEdit = false;
1149
+ return true;
1150
+ };
1151
+
1152
+ Grid.prototype._getTarget = function (cell) {
1153
+ return getCellTarget(cell, this.table);
1154
+ };
1155
+
1156
+ Grid.prototype._isReadOnly = function (cell) {
1157
+ var target = this._getTarget(cell);
1158
+ var cellAttribute = cell.getAttribute('data-excel-readonly');
1159
+ var targetAttribute = target && target.getAttribute ?
1160
+ target.getAttribute('data-excel-readonly') : null;
1161
+
1162
+ if (typeof this.options.readOnly === 'function' &&
1163
+ this.options.readOnly(cell, target) === true) {
1164
+ return true;
1165
+ }
1166
+ if ((cellAttribute !== null && String(cellAttribute).toLowerCase() !== 'false') ||
1167
+ cell.getAttribute('aria-readonly') === 'true') {
1168
+ return true;
1169
+ }
1170
+ if (target && ((targetAttribute !== null &&
1171
+ String(targetAttribute).toLowerCase() !== 'false') ||
1172
+ target.getAttribute('aria-readonly') === 'true' ||
1173
+ target.disabled === true || target.readOnly === true)) {
1174
+ return true;
1175
+ }
1176
+ if (target && isInteractiveElement(target) && !isValueControl(target) &&
1177
+ !isContentEditable(target) && typeof this.options.setValue !== 'function') {
1178
+ return true;
1179
+ }
1180
+ /* Do not destroy links, buttons, icons or unsupported controls in a complex cell. */
1181
+ if (!target && typeof this.options.setValue !== 'function' &&
1182
+ hasUnsafeCellMarkup(cell)) {
1183
+ return true;
1184
+ }
1185
+ return false;
1186
+ };
1187
+
1188
+ Grid.prototype._readValue = function (cell) {
1189
+ var target = this._getTarget(cell);
1190
+ var customValue;
1191
+
1192
+ if (typeof this.options.getValue === 'function') {
1193
+ customValue = this.options.getValue(cell, target);
1194
+ return String(customValue === null || typeof customValue === 'undefined' ? '' : customValue);
1195
+ }
1196
+ if (target && (isValueControl(target) || typeof target.value !== 'undefined')) {
1197
+ return String(target.value === null || typeof target.value === 'undefined' ? '' : target.value);
1198
+ }
1199
+ if (target) {
1200
+ return String(getText(target));
1201
+ }
1202
+ return String(getText(cell));
1203
+ };
1204
+
1205
+ Grid.prototype._readDisplayValue = function (cell) {
1206
+ var target = this._getTarget(cell);
1207
+
1208
+ if (target && String(target.tagName).toUpperCase() === 'SELECT') {
1209
+ if (target.selectedIndex >= 0 && target.options[target.selectedIndex]) {
1210
+ return String(target.options[target.selectedIndex].text);
1211
+ }
1212
+ return '';
1213
+ }
1214
+ return this._readValue(cell);
1215
+ };
1216
+
1217
+ Grid.prototype._normalizeIncomingValue = function (cell, value) {
1218
+ var target = this._getTarget(cell);
1219
+ var text = value === null || typeof value === 'undefined' ? '' : String(value);
1220
+ var i;
1221
+
1222
+ if (typeof this.options.setValue === 'function' || !target ||
1223
+ String(target.tagName).toUpperCase() !== 'SELECT') {
1224
+ return text;
1225
+ }
1226
+ for (i = 0; i < target.options.length; i += 1) {
1227
+ if (String(target.options[i].value) === text) {
1228
+ return text;
1229
+ }
1230
+ }
1231
+ for (i = 0; i < target.options.length; i += 1) {
1232
+ if (String(target.options[i].text) === text) {
1233
+ return String(target.options[i].value);
1234
+ }
1235
+ }
1236
+ return text;
1237
+ };
1238
+
1239
+ Grid.prototype._writeValue = function (cell, value, dispatchEvents) {
1240
+ var target = this._getTarget(cell);
1241
+ var eventTarget = target || cell;
1242
+
1243
+ value = value === null || typeof value === 'undefined' ? '' : String(value);
1244
+
1245
+ if (typeof this.options.setValue === 'function') {
1246
+ this.options.setValue(cell, value, target);
1247
+ } else if (target && String(target.tagName).toUpperCase() === 'SELECT') {
1248
+ target.value = this._normalizeIncomingValue(cell, value);
1249
+ } else if (target && (isValueControl(target) || typeof target.value !== 'undefined')) {
1250
+ target.value = value;
1251
+ } else if (target) {
1252
+ setText(target, value);
1253
+ } else {
1254
+ setText(cell, value);
1255
+ }
1256
+
1257
+ if (dispatchEvents && this.options.dispatchEvents) {
1258
+ fireEvent(eventTarget, 'input');
1259
+ fireEvent(eventTarget, 'change');
1260
+ }
1261
+ };
1262
+
1263
+ Grid.prototype._positionOf = function (grid, cell) {
1264
+ var row;
1265
+ var column;
1266
+
1267
+ for (row = 0; row < grid.length; row += 1) {
1268
+ for (column = 0; column < grid[row].length; column += 1) {
1269
+ if (grid[row][column] === cell) {
1270
+ return { row: row, column: column };
1271
+ }
1272
+ }
1273
+ }
1274
+ return null;
1275
+ };
1276
+
1277
+ Grid.prototype._clearSelectionClasses = function () {
1278
+ var i;
1279
+ for (i = 0; i < this.selectedCells.length; i += 1) {
1280
+ removeClass(this.selectedCells[i], SELECTED_CLASS);
1281
+ removeClass(this.selectedCells[i], ACTIVE_CLASS);
1282
+ }
1283
+ };
1284
+
1285
+ Grid.prototype._setSelection = function (anchorCell, activeCell, focusTable, type) {
1286
+ var grid = this._getGrid();
1287
+ var anchorPosition = this._positionOf(grid, anchorCell);
1288
+ var activePosition = this._positionOf(grid, activeCell);
1289
+ var startRow;
1290
+ var endRow;
1291
+ var startColumn;
1292
+ var endColumn;
1293
+ var row;
1294
+ var column;
1295
+ var cell;
1296
+
1297
+ if (!anchorPosition || !activePosition) {
1298
+ return false;
1299
+ }
1300
+
1301
+ this._clearSelectionClasses();
1302
+ this.selectedCells = [];
1303
+ this.anchorCell = anchorCell;
1304
+ this.activeCell = activeCell;
1305
+ this.selectionType = type || 'cells';
1306
+
1307
+ startRow = Math.min(anchorPosition.row, activePosition.row);
1308
+ endRow = Math.max(anchorPosition.row, activePosition.row);
1309
+ startColumn = Math.min(anchorPosition.column, activePosition.column);
1310
+ endColumn = Math.max(anchorPosition.column, activePosition.column);
1311
+
1312
+ for (row = startRow; row <= endRow; row += 1) {
1313
+ for (column = startColumn; column <= endColumn; column += 1) {
1314
+ cell = grid[row] && grid[row][column];
1315
+ if (cell) {
1316
+ this.selectedCells.push(cell);
1317
+ addClass(cell, SELECTED_CLASS);
1318
+ }
1319
+ }
1320
+ }
1321
+ addClass(activeCell, ACTIVE_CLASS);
1322
+ this._applyHeaderSelectionClasses(this.selectionType, {
1323
+ startRow: startRow,
1324
+ endRow: endRow,
1325
+ startColumn: startColumn,
1326
+ endColumn: endColumn
1327
+ }, grid);
1328
+
1329
+ if (focusTable) {
1330
+ this._focusTable();
1331
+ }
1332
+ this._notifySelectionChange();
1333
+ return true;
1334
+ };
1335
+
1336
+ Grid.prototype._focusTable = function () {
1337
+ if (this.table && this.table.focus) {
1338
+ try {
1339
+ this.table.focus();
1340
+ } catch (ignore) {
1341
+ /* Some embedded browsers can reject programmatic focus. */
1342
+ }
1343
+ }
1344
+ };
1345
+
1346
+ Grid.prototype._notifySelectionChange = function () {
1347
+ if (typeof this.options.onSelectionChange === 'function') {
1348
+ try {
1349
+ this.options.onSelectionChange(this.getSelection());
1350
+ } catch (error) {
1351
+ this._reportError(error);
1352
+ }
1353
+ }
1354
+ };
1355
+
1356
+ Grid.prototype._snapshotSelection = function () {
1357
+ return {
1358
+ type: this.selectionType || 'cells',
1359
+ anchor: this.anchorCell,
1360
+ active: this.activeCell
1361
+ };
1362
+ };
1363
+
1364
+ Grid.prototype._restoreSelection = function (snapshot, focusTable) {
1365
+ if (snapshot && snapshot.anchor && snapshot.active &&
1366
+ contains(this.table, snapshot.anchor) && contains(this.table, snapshot.active)) {
1367
+ this._setSelection(
1368
+ snapshot.anchor,
1369
+ snapshot.active,
1370
+ focusTable === true,
1371
+ snapshot.type || 'cells'
1372
+ );
1373
+ }
1374
+ };
1375
+
1376
+ Grid.prototype._selectionBounds = function (grid) {
1377
+ var anchorPosition;
1378
+ var activePosition;
1379
+
1380
+ if (!this.anchorCell || !this.activeCell) {
1381
+ return null;
1382
+ }
1383
+ anchorPosition = this._positionOf(grid, this.anchorCell);
1384
+ activePosition = this._positionOf(grid, this.activeCell);
1385
+ if (!anchorPosition || !activePosition) {
1386
+ return null;
1387
+ }
1388
+
1389
+ return {
1390
+ startRow: Math.min(anchorPosition.row, activePosition.row),
1391
+ endRow: Math.max(anchorPosition.row, activePosition.row),
1392
+ startColumn: Math.min(anchorPosition.column, activePosition.column),
1393
+ endColumn: Math.max(anchorPosition.column, activePosition.column)
1394
+ };
1395
+ };
1396
+
1397
+ Grid.prototype._ensureSelection = function () {
1398
+ var grid;
1399
+ if (this.selectedCells.length > 0 && this.anchorCell && this.activeCell) {
1400
+ return true;
1401
+ }
1402
+ grid = this._getGrid();
1403
+ if (grid.length && grid[0].length) {
1404
+ return this._setSelection(grid[0][0], grid[0][0], false);
1405
+ }
1406
+ return false;
1407
+ };
1408
+
1409
+ Grid.prototype._onMouseDown = function (event) {
1410
+ var cell;
1411
+ var grid;
1412
+ var headerInfo;
1413
+ var anchorPosition;
1414
+ var headerStart;
1415
+ var editorTarget;
1416
+ var gridEditorTarget;
1417
+ var useNativeEditor;
1418
+ var nativePointerEditor;
1419
+
1420
+ if (this.destroyed || (typeof event.button !== 'undefined' && event.button !== 0)) {
1421
+ return;
1422
+ }
1423
+
1424
+ grid = this._getGrid();
1425
+ headerInfo = this._getHeaderInfo(event.target || event.srcElement, grid);
1426
+ if (headerInfo) {
1427
+ if (this.editingCell) {
1428
+ this._finishEditMode(false, false);
1429
+ }
1430
+ headerStart = headerInfo.index;
1431
+ if (event.shiftKey && this.selectionType === headerInfo.type && this.anchorCell) {
1432
+ anchorPosition = this._positionOf(grid, this.anchorCell);
1433
+ if (anchorPosition) {
1434
+ headerStart = headerInfo.type === 'rows' ?
1435
+ anchorPosition.row : anchorPosition.column;
1436
+ }
1437
+ }
1438
+ this._selectHeaderRange(
1439
+ headerInfo.type,
1440
+ headerStart,
1441
+ headerInfo.index,
1442
+ true
1443
+ );
1444
+ if (event.preventDefault) {
1445
+ event.preventDefault();
1446
+ }
1447
+ this._focusTable();
1448
+ this.dragStartCell = null;
1449
+ this.dragSelectionType = null;
1450
+ this.dragStartIndex = null;
1451
+ if (headerInfo.type !== 'all') {
1452
+ this.dragging = true;
1453
+ this.dragSelectionType = headerInfo.type;
1454
+ this.dragStartIndex = headerStart;
1455
+ addClass(this.table, DRAGGING_CLASS);
1456
+ } else {
1457
+ this.dragging = false;
1458
+ removeClass(this.table, DRAGGING_CLASS);
1459
+ }
1460
+ return;
1461
+ }
1462
+
1463
+ cell = this._findCell(event.target || event.srcElement);
1464
+ if (!cell) {
1465
+ return;
1466
+ }
1467
+
1468
+ editorTarget = this._isEditorTarget(event.target || event.srcElement, cell);
1469
+ gridEditorTarget = this._isGridEditorTarget(event.target || event.srcElement, cell);
1470
+ nativePointerEditor = this._usesNativePointerEditor(
1471
+ event.target || event.srcElement,
1472
+ cell
1473
+ );
1474
+ useNativeEditor = editorTarget;
1475
+ if (this.options.interactionMode === 'spreadsheet' && gridEditorTarget &&
1476
+ this.editingCell !== cell && !nativePointerEditor) {
1477
+ useNativeEditor = false;
1478
+ }
1479
+
1480
+ if (this.editingCell && (this.editingCell !== cell ||
1481
+ !useNativeEditor || event.shiftKey)) {
1482
+ this._finishEditMode(false, false);
1483
+ }
1484
+
1485
+ if (event.shiftKey && this.anchorCell) {
1486
+ this._setSelection(this.anchorCell, cell, true);
1487
+ } else {
1488
+ this._setSelection(cell, cell, !useNativeEditor);
1489
+ }
1490
+
1491
+ if (useNativeEditor && !event.shiftKey) {
1492
+ this.dragging = false;
1493
+ return;
1494
+ }
1495
+
1496
+ if (event.preventDefault) {
1497
+ event.preventDefault();
1498
+ }
1499
+ this._focusTable();
1500
+ this.dragging = true;
1501
+ this.dragSelectionType = null;
1502
+ this.dragStartIndex = null;
1503
+ this.dragStartCell = event.shiftKey && this.anchorCell ? this.anchorCell : cell;
1504
+ addClass(this.table, DRAGGING_CLASS);
1505
+ };
1506
+
1507
+ Grid.prototype._onDoubleClick = function (event) {
1508
+ var cell;
1509
+
1510
+ if (this.destroyed || this.options.interactionMode !== 'spreadsheet') {
1511
+ return;
1512
+ }
1513
+ cell = this._findCell(event.target || event.srcElement);
1514
+ if (!cell || !this._enterEditMode(cell)) {
1515
+ return;
1516
+ }
1517
+
1518
+ this._setSelection(cell, cell, false);
1519
+ this.dragging = false;
1520
+ this.dragStartCell = null;
1521
+ removeClass(this.table, DRAGGING_CLASS);
1522
+ if (event.preventDefault) {
1523
+ event.preventDefault();
1524
+ }
1525
+ };
1526
+
1527
+ Grid.prototype._onMouseOver = function (event) {
1528
+ var cell;
1529
+ var info;
1530
+ var grid;
1531
+ if (!this.dragging || !this.dragStartCell) {
1532
+ if (!this.dragging || !this.dragSelectionType) {
1533
+ return;
1534
+ }
1535
+ grid = this._getGrid();
1536
+ info = this._getHeaderInfo(event.target || event.srcElement, grid);
1537
+ if (info && info.type === this.dragSelectionType) {
1538
+ this._selectHeaderRange(
1539
+ this.dragSelectionType,
1540
+ this.dragStartIndex,
1541
+ info.index,
1542
+ false
1543
+ );
1544
+ }
1545
+ return;
1546
+ }
1547
+ cell = this._findCell(event.target || event.srcElement);
1548
+ if (cell && cell !== this.activeCell) {
1549
+ this._setSelection(this.dragStartCell, cell, false);
1550
+ }
1551
+ };
1552
+
1553
+ Grid.prototype._onMouseUp = function () {
1554
+ this.dragging = false;
1555
+ this.dragStartCell = null;
1556
+ this.dragSelectionType = null;
1557
+ this.dragStartIndex = null;
1558
+ removeClass(this.table, DRAGGING_CLASS);
1559
+ };
1560
+
1561
+ Grid.prototype._onFocusIn = function (event) {
1562
+ var cell;
1563
+
1564
+ if (this.destroyed) {
1565
+ return;
1566
+ }
1567
+ cell = this._findCell(event.target || event.srcElement);
1568
+ if (cell && this._isGridEditorTarget(event.target || event.srcElement, cell)) {
1569
+ if (this.editingCell !== cell) {
1570
+ this.editingSelectionBefore = this._snapshotSelection();
1571
+ this.editingCell = cell;
1572
+ this.editingTarget = this._getTarget(cell);
1573
+ this.editingStartValue = this._readValue(cell);
1574
+ this.editingMode = this.options.interactionMode === 'form' ? 'form' : 'manual';
1575
+ }
1576
+ if (this.activeCell !== cell) {
1577
+ this._setSelection(cell, cell, false);
1578
+ }
1579
+ } else if ((event.target || event.srcElement) === this.table) {
1580
+ this._clearEditState();
1581
+ }
1582
+ };
1583
+
1584
+ Grid.prototype._onFocusOut = function (event) {
1585
+ var nextTarget = event.relatedTarget || null;
1586
+
1587
+ if (!this.endingEdit && this.editingCell &&
1588
+ (!nextTarget || !contains(this.editingCell, nextTarget))) {
1589
+ this._finishEditMode(false, false);
1590
+ }
1591
+ };
1592
+
1593
+ Grid.prototype._moveActiveCell = function (rowDelta, columnDelta, extendSelection) {
1594
+ var grid = this._getGrid();
1595
+ var current = this._positionOf(grid, this.activeCell);
1596
+ var nextRow;
1597
+ var nextColumn;
1598
+ var nextCell;
1599
+
1600
+ if (!current) {
1601
+ return;
1602
+ }
1603
+
1604
+ nextRow = Math.max(0, Math.min(grid.length - 1, current.row + rowDelta));
1605
+ nextColumn = Math.max(0, current.column + columnDelta);
1606
+ if (!grid[nextRow] || !grid[nextRow].length) {
1607
+ return;
1608
+ }
1609
+ nextColumn = Math.min(grid[nextRow].length - 1, nextColumn);
1610
+ nextCell = grid[nextRow][nextColumn];
1611
+
1612
+ if (extendSelection && this.anchorCell) {
1613
+ this._setSelection(this.anchorCell, nextCell, true);
1614
+ } else {
1615
+ this._setSelection(nextCell, nextCell, true);
1616
+ }
1617
+ };
1618
+
1619
+ Grid.prototype._moveTab = function (direction) {
1620
+ var grid = this._getGrid();
1621
+ var current = this._positionOf(grid, this.activeCell);
1622
+ var nextRow;
1623
+ var nextColumn;
1624
+ var nextCell;
1625
+
1626
+ if (!current || !grid.length) {
1627
+ return;
1628
+ }
1629
+
1630
+ nextRow = current.row;
1631
+ nextColumn = current.column + direction;
1632
+ if (direction > 0 && nextColumn >= grid[nextRow].length) {
1633
+ nextRow = nextRow + 1;
1634
+ if (nextRow >= grid.length) {
1635
+ nextRow = 0;
1636
+ }
1637
+ nextColumn = 0;
1638
+ } else if (direction < 0 && nextColumn < 0) {
1639
+ nextRow = nextRow - 1;
1640
+ if (nextRow < 0) {
1641
+ nextRow = grid.length - 1;
1642
+ }
1643
+ nextColumn = grid[nextRow].length - 1;
1644
+ }
1645
+
1646
+ if (!grid[nextRow] || !grid[nextRow].length) {
1647
+ return;
1648
+ }
1649
+ nextColumn = Math.max(0, Math.min(grid[nextRow].length - 1, nextColumn));
1650
+ nextCell = grid[nextRow][nextColumn];
1651
+ this._setSelection(nextCell, nextCell, true);
1652
+ };
1653
+
1654
+ Grid.prototype._onKeyDown = function (event) {
1655
+ var key;
1656
+ var command;
1657
+ var cell;
1658
+ var target;
1659
+ var editorTarget;
1660
+ var gridEditorTarget;
1661
+ var nativeEditor;
1662
+ var character;
1663
+ var code;
1664
+
1665
+ if (this.destroyed) {
1666
+ return;
1667
+ }
1668
+
1669
+ target = event.target || event.srcElement;
1670
+ key = keyName(event);
1671
+ command = event.ctrlKey || event.metaKey;
1672
+ code = event.which || event.keyCode;
1673
+ cell = this._findCell(target) || this.activeCell;
1674
+ editorTarget = cell ? this._isEditorTarget(target, cell) : false;
1675
+ gridEditorTarget = cell ?
1676
+ this._isGridEditorTarget(target, cell) : false;
1677
+ nativeEditor = editorTarget && (!gridEditorTarget ||
1678
+ this.options.interactionMode === 'form' || this.editingCell === cell);
1679
+
1680
+ if (event.isComposing || code === 229) {
1681
+ if (this.options.interactionMode === 'spreadsheet' && !nativeEditor &&
1682
+ this.activeCell) {
1683
+ /* Best effort: hand the active input to the IME without cancelling the key. */
1684
+ this._enterEditMode(this.activeCell, '', true);
1685
+ }
1686
+ return;
1687
+ }
1688
+
1689
+ if (this.options.interactionMode === 'spreadsheet' && command &&
1690
+ key === 'enter' && !event.altKey && !event.shiftKey) {
1691
+ if (event.preventDefault) { event.preventDefault(); }
1692
+ if (!event.repeat) {
1693
+ this.insertRowBelow();
1694
+ }
1695
+ return;
1696
+ }
1697
+
1698
+ if (nativeEditor) {
1699
+ if (this.options.interactionMode === 'spreadsheet' && gridEditorTarget &&
1700
+ this.editingCell === cell && key === 'escape') {
1701
+ if (event.preventDefault) { event.preventDefault(); }
1702
+ this._finishEditMode(true, true);
1703
+ return;
1704
+ }
1705
+ if (this.options.interactionMode === 'spreadsheet' && gridEditorTarget &&
1706
+ this.editingCell === cell && key === 'tab') {
1707
+ if (event.preventDefault) { event.preventDefault(); }
1708
+ this._finishEditMode(false, true);
1709
+ this._moveTab(event.shiftKey ? -1 : 1);
1710
+ return;
1711
+ }
1712
+ if (this.options.interactionMode === 'spreadsheet' && gridEditorTarget &&
1713
+ this.editingCell === cell && key === 'enter' && !event.altKey) {
1714
+ if (event.preventDefault) { event.preventDefault(); }
1715
+ this._finishEditMode(false, true);
1716
+ this._moveActiveCell(event.shiftKey ? -1 : 1, 0, false);
1717
+ return;
1718
+ }
1719
+ if (this.options.interactionMode === 'spreadsheet' && gridEditorTarget &&
1720
+ this.editingCell === cell && this.editingMode === 'replace' &&
1721
+ !command && !event.altKey && !event.shiftKey &&
1722
+ (key === 'arrowleft' || key === 'arrowright' ||
1723
+ key === 'arrowup' || key === 'arrowdown')) {
1724
+ if (event.preventDefault) { event.preventDefault(); }
1725
+ this._finishEditMode(false, true);
1726
+ this._moveActiveCell(
1727
+ key === 'arrowup' ? -1 : (key === 'arrowdown' ? 1 : 0),
1728
+ key === 'arrowleft' ? -1 : (key === 'arrowright' ? 1 : 0),
1729
+ false
1730
+ );
1731
+ return;
1732
+ }
1733
+ return;
1734
+ }
1735
+
1736
+ if (this.options.interactionMode === 'spreadsheet') {
1737
+ if (key === 'tab') {
1738
+ if (event.preventDefault) { event.preventDefault(); }
1739
+ this._moveTab(event.shiftKey ? -1 : 1);
1740
+ return;
1741
+ }
1742
+ if (!command && key === 'enter') {
1743
+ if (event.preventDefault) { event.preventDefault(); }
1744
+ this._moveActiveCell(event.shiftKey ? -1 : 1, 0, false);
1745
+ return;
1746
+ }
1747
+ if (!command && key === 'f2') {
1748
+ if (this.selectionType === 'cells' &&
1749
+ this._enterEditMode(this.activeCell) && event.preventDefault) {
1750
+ event.preventDefault();
1751
+ }
1752
+ return;
1753
+ }
1754
+ character = printableKey(event);
1755
+ if (character !== null) {
1756
+ if (this.selectionType === 'cells' &&
1757
+ this._enterEditMode(this.activeCell, character, true) &&
1758
+ event.preventDefault) {
1759
+ event.preventDefault();
1760
+ }
1761
+ return;
1762
+ }
1763
+ }
1764
+
1765
+ if (command && (key === '-' || key === 'subtract')) {
1766
+ if (event.preventDefault) { event.preventDefault(); }
1767
+ if (this.selectionType === 'rows' || this.selectionType === 'cells') {
1768
+ this.deleteRows();
1769
+ }
1770
+ return;
1771
+ }
1772
+
1773
+ if (command && key === 'z') {
1774
+ if (event.preventDefault) { event.preventDefault(); }
1775
+ if (event.shiftKey) {
1776
+ this.redo();
1777
+ } else {
1778
+ this.undo();
1779
+ }
1780
+ return;
1781
+ }
1782
+ if (command && key === 'y') {
1783
+ if (event.preventDefault) { event.preventDefault(); }
1784
+ this.redo();
1785
+ return;
1786
+ }
1787
+ if (key === 'delete' || key === 'backspace') {
1788
+ if (event.preventDefault) { event.preventDefault(); }
1789
+ if (key === 'delete' && this.selectionType === 'rows') {
1790
+ this.deleteRows();
1791
+ } else {
1792
+ this.clear();
1793
+ }
1794
+ return;
1795
+ }
1796
+ if (key === 'arrowleft' || key === 'arrowright' ||
1797
+ key === 'arrowup' || key === 'arrowdown') {
1798
+ if (event.preventDefault) { event.preventDefault(); }
1799
+ this._moveActiveCell(
1800
+ key === 'arrowup' ? -1 : (key === 'arrowdown' ? 1 : 0),
1801
+ key === 'arrowleft' ? -1 : (key === 'arrowright' ? 1 : 0),
1802
+ event.shiftKey === true
1803
+ );
1804
+ }
1805
+ };
1806
+
1807
+ Grid.prototype._clipboardText = function (event) {
1808
+ var clipboard = event.clipboardData || (root && root.clipboardData);
1809
+ var value = '';
1810
+ var available = false;
1811
+ var legacyClipboard = root && root.clipboardData === clipboard;
1812
+ var firstError = null;
1813
+
1814
+ if (!clipboard || !clipboard.getData) {
1815
+ return { available: false, value: '' };
1816
+ }
1817
+
1818
+ try {
1819
+ value = clipboard.getData(legacyClipboard ? 'Text' : 'text/plain');
1820
+ available = true;
1821
+ } catch (primaryError) {
1822
+ firstError = primaryError;
1823
+ }
1824
+
1825
+ if (!available || value === null || typeof value === 'undefined') {
1826
+ try {
1827
+ value = clipboard.getData(legacyClipboard ? 'text/plain' : 'Text');
1828
+ available = true;
1829
+ } catch (fallbackError) {
1830
+ this._reportError(firstError || fallbackError);
1831
+ return { available: false, value: '' };
1832
+ }
1833
+ }
1834
+ return {
1835
+ available: available,
1836
+ value: value === null || typeof value === 'undefined' ? '' : value
1837
+ };
1838
+ };
1839
+
1840
+ Grid.prototype._onPaste = function (event) {
1841
+ var clipboard;
1842
+ var matrix;
1843
+ var cell;
1844
+ var editorTarget;
1845
+ var shouldUseNative;
1846
+
1847
+ if (this.destroyed || this.options.pasteMode === 'native') {
1848
+ return;
1849
+ }
1850
+
1851
+ clipboard = this._clipboardText(event);
1852
+ if (!clipboard.available) {
1853
+ return;
1854
+ }
1855
+
1856
+ matrix = parseClipboardText(clipboard.value);
1857
+ cell = this._findCell(event.target || event.srcElement);
1858
+ editorTarget = cell ? this._isEditorTarget(event.target || event.srcElement, cell) : false;
1859
+
1860
+ if (this.options.interactionMode === 'spreadsheet' && cell &&
1861
+ this.editingCell === cell &&
1862
+ this._isGridEditorTarget(event.target || event.srcElement, cell)) {
1863
+ return;
1864
+ }
1865
+ if (this.editingCell) {
1866
+ this._finishEditMode(false, false);
1867
+ }
1868
+
1869
+ /*
1870
+ * Keyboard/Tab focus can move without a mouse event; paste into the focused cell.
1871
+ * A legacy browser may, however, leave focus on an old input after a row/column
1872
+ * header drag. Do not let that stale paste target collapse a header selection.
1873
+ */
1874
+ if (this.selectionType === 'cells' && cell && editorTarget &&
1875
+ cell !== this.activeCell) {
1876
+ this._setSelection(cell, cell, false);
1877
+ }
1878
+ shouldUseNative = this.options.pasteMode === 'auto' && editorTarget &&
1879
+ matrix.length === 1 && matrix[0].length === 1 && this.selectedCells.length <= 1;
1880
+
1881
+ if (shouldUseNative) {
1882
+ return;
1883
+ }
1884
+ if (!this._ensureSelection()) {
1885
+ return;
1886
+ }
1887
+
1888
+ if (event.preventDefault) {
1889
+ event.preventDefault();
1890
+ }
1891
+ this._pasteMatrix(matrix);
1892
+ this._focusTable();
1893
+ };
1894
+
1895
+ Grid.prototype._onCopy = function (event) {
1896
+ var target = event.target || event.srcElement;
1897
+ var cell;
1898
+ var editorTarget;
1899
+ var gridEditorTarget;
1900
+ var nativeEditor;
1901
+ var clipboard;
1902
+ var legacyClipboard;
1903
+ var text;
1904
+ var copied = false;
1905
+
1906
+ if (this.destroyed || !this.selectedCells.length) {
1907
+ return;
1908
+ }
1909
+
1910
+ if (target === this.table && document.activeElement &&
1911
+ contains(this.table, document.activeElement)) {
1912
+ target = document.activeElement;
1913
+ }
1914
+ cell = this._findCell(target);
1915
+ editorTarget = cell ? this._isEditorTarget(target, cell) : false;
1916
+ gridEditorTarget = cell ?
1917
+ this._isGridEditorTarget(target, cell) : false;
1918
+ nativeEditor = editorTarget && (!gridEditorTarget ||
1919
+ this.options.interactionMode === 'form' || this.editingCell === cell);
1920
+ if (nativeEditor) {
1921
+ return;
1922
+ }
1923
+
1924
+ clipboard = event.clipboardData || (root && root.clipboardData);
1925
+ if (!clipboard || !clipboard.setData) {
1926
+ return;
1927
+ }
1928
+
1929
+ try {
1930
+ text = this.getCopyText();
1931
+ } catch (readError) {
1932
+ this._reportError(readError);
1933
+ return;
1934
+ }
1935
+
1936
+ legacyClipboard = root && root.clipboardData === clipboard;
1937
+ try {
1938
+ clipboard.setData(legacyClipboard ? 'Text' : 'text/plain', text);
1939
+ copied = true;
1940
+ } catch (primaryError) {
1941
+ try {
1942
+ clipboard.setData(legacyClipboard ? 'text/plain' : 'Text', text);
1943
+ copied = true;
1944
+ } catch (fallbackError) {
1945
+ this._reportError(primaryError || fallbackError);
1946
+ }
1947
+ }
1948
+
1949
+ if (copied && event.preventDefault) {
1950
+ event.preventDefault();
1951
+ }
1952
+ if (copied) {
1953
+ event.returnValue = false;
1954
+ }
1955
+ };
1956
+
1957
+ Grid.prototype._makeChanges = function (cellsAndValues) {
1958
+ var changes = [];
1959
+ var item;
1960
+ var oldValue;
1961
+ var newValue;
1962
+ var i;
1963
+
1964
+ for (i = 0; i < cellsAndValues.length; i += 1) {
1965
+ item = cellsAndValues[i];
1966
+ if (!item.cell || this._isReadOnly(item.cell)) {
1967
+ continue;
1968
+ }
1969
+ oldValue = this._readValue(item.cell);
1970
+ newValue = this._normalizeIncomingValue(item.cell, item.value);
1971
+ if (oldValue !== newValue) {
1972
+ changes.push({
1973
+ cell: item.cell,
1974
+ oldValue: oldValue,
1975
+ newValue: newValue
1976
+ });
1977
+ }
1978
+ }
1979
+
1980
+ return changes;
1981
+ };
1982
+
1983
+ Grid.prototype._copyChanges = function (changes) {
1984
+ var copy = [];
1985
+ var i;
1986
+ for (i = 0; i < changes.length; i += 1) {
1987
+ copy.push({
1988
+ cell: changes[i].cell,
1989
+ oldValue: changes[i].oldValue,
1990
+ newValue: changes[i].newValue
1991
+ });
1992
+ }
1993
+ return copy;
1994
+ };
1995
+
1996
+ Grid.prototype._applyChanges = function (changes, meta) {
1997
+ var applied = [];
1998
+ var i;
1999
+ var beforeResult;
2000
+ var error;
2001
+
2002
+ if (changes.length === 0) {
2003
+ return true;
2004
+ }
2005
+
2006
+ if (typeof this.options.beforeChange === 'function') {
2007
+ try {
2008
+ beforeResult = this.options.beforeChange(this._copyChanges(changes), meta);
2009
+ } catch (beforeError) {
2010
+ this._reportError(beforeError);
2011
+ return false;
2012
+ }
2013
+ if (beforeResult === false) {
2014
+ return false;
2015
+ }
2016
+ }
2017
+
2018
+ try {
2019
+ for (i = 0; i < changes.length; i += 1) {
2020
+ this._writeValue(changes[i].cell, changes[i].newValue, true);
2021
+ applied.push(changes[i]);
2022
+ }
2023
+ } catch (writeError) {
2024
+ error = writeError;
2025
+ for (i = applied.length - 1; i >= 0; i -= 1) {
2026
+ try {
2027
+ this._writeValue(applied[i].cell, applied[i].oldValue, false);
2028
+ } catch (ignore) {
2029
+ /* Preserve the first error; best-effort rollback. */
2030
+ }
2031
+ }
2032
+ this._reportError(error);
2033
+ return false;
2034
+ }
2035
+
2036
+ if (typeof this.options.afterChange === 'function') {
2037
+ try {
2038
+ this.options.afterChange(this._copyChanges(changes), meta);
2039
+ } catch (afterError) {
2040
+ this._reportError(afterError);
2041
+ }
2042
+ }
2043
+ return true;
2044
+ };
2045
+
2046
+ Grid.prototype._pushHistory = function (entry) {
2047
+ var limit = parseInt(this.options.historyLimit, 10);
2048
+
2049
+ if (isNaN(limit) || limit <= 0) {
2050
+ this.redoStack = [];
2051
+ return;
2052
+ }
2053
+
2054
+ this.undoStack.push(entry);
2055
+ while (this.undoStack.length > limit) {
2056
+ this.undoStack.shift();
2057
+ }
2058
+ this.redoStack = [];
2059
+ };
2060
+
2061
+ Grid.prototype._record = function (type, changes, selectionBefore, selectionAfter, addedRows) {
2062
+ if (!changes.length && (!addedRows || !addedRows.length)) {
2063
+ return;
2064
+ }
2065
+ this._pushHistory({
2066
+ kind: 'cells',
2067
+ type: type,
2068
+ changes: this._copyChanges(changes),
2069
+ addedRows: addedRows && addedRows.length ? addedRows.slice(0) : null,
2070
+ selectionBefore: selectionBefore,
2071
+ selectionAfter: selectionAfter
2072
+ });
2073
+ };
2074
+
2075
+ Grid.prototype._recordRows = function (records, selectionBefore, selectionAfter) {
2076
+ if (!records.length) {
2077
+ return;
2078
+ }
2079
+ this._pushHistory({
2080
+ kind: 'rows',
2081
+ type: 'deleteRows',
2082
+ rows: records.slice(0),
2083
+ selectionBefore: selectionBefore,
2084
+ selectionAfter: selectionAfter
2085
+ });
2086
+ };
2087
+
2088
+ Grid.prototype._isRowReadOnly = function (row) {
2089
+ var attribute = row.getAttribute('data-excel-row-readonly');
2090
+
2091
+ if (typeof this.options.rowReadOnly === 'function' &&
2092
+ this.options.rowReadOnly(row) === true) {
2093
+ return true;
2094
+ }
2095
+ return (attribute !== null && String(attribute).toLowerCase() !== 'false') ||
2096
+ row.getAttribute('aria-readonly') === 'true';
2097
+ };
2098
+
2099
+ Grid.prototype._collectSelectedRowRecords = function () {
2100
+ var records = [];
2101
+ var rows = this.table.rows;
2102
+ var row;
2103
+ var selected;
2104
+ var i;
2105
+ var j;
2106
+
2107
+ for (i = 0; i < rows.length; i += 1) {
2108
+ row = rows[i];
2109
+ selected = false;
2110
+ for (j = 0; j < this.selectedCells.length; j += 1) {
2111
+ if (this.selectedCells[j].parentNode === row) {
2112
+ selected = true;
2113
+ break;
2114
+ }
2115
+ }
2116
+ if (selected && owningTable(row) === this.table &&
2117
+ matchesSelector(row, this.options.rowSelector) &&
2118
+ row.parentNode && String(row.parentNode.tagName).toUpperCase() === 'TBODY') {
2119
+ records.push({
2120
+ row: row,
2121
+ parent: row.parentNode,
2122
+ nextSibling: row.nextSibling,
2123
+ sectionIndex: row.sectionRowIndex
2124
+ });
2125
+ }
2126
+ }
2127
+ return records;
2128
+ };
2129
+
2130
+ Grid.prototype._clearSelectionState = function () {
2131
+ this._clearSelectionClasses();
2132
+ this._clearHeaderSelectionClasses();
2133
+ this.selectedCells = [];
2134
+ this.anchorCell = null;
2135
+ this.activeCell = null;
2136
+ this.selectionType = 'cells';
2137
+ this._clearEditState();
2138
+ };
2139
+
2140
+ Grid.prototype._notifyRowsChange = function (records, meta) {
2141
+ var rows = [];
2142
+ var i;
2143
+
2144
+ for (i = 0; i < records.length; i += 1) {
2145
+ rows.push(records[i].row);
2146
+ }
2147
+
2148
+ if (typeof this.options.reindexRows === 'function') {
2149
+ try {
2150
+ this.options.reindexRows(this.table, meta);
2151
+ } catch (reindexError) {
2152
+ this._reportError(reindexError);
2153
+ }
2154
+ }
2155
+ if (typeof this.options.afterRowsChange === 'function') {
2156
+ try {
2157
+ this.options.afterRowsChange(rows, meta);
2158
+ } catch (afterError) {
2159
+ this._reportError(afterError);
2160
+ }
2161
+ }
2162
+ };
2163
+
2164
+ Grid.prototype._pasteMatrix = function (matrix) {
2165
+ var grid = this._getGrid();
2166
+ var bounds = this._selectionBounds(grid);
2167
+ var selectionBefore = this._snapshotSelection();
2168
+ var selectionAfter;
2169
+ var assignments = [];
2170
+ var changes;
2171
+ var startCell;
2172
+ var lastCell;
2173
+ var targetCell;
2174
+ var row;
2175
+ var column;
2176
+ var overflowed = false;
2177
+ var fillSelection;
2178
+ var repeatSelection;
2179
+ var rectangularSource = true;
2180
+ var rectangularSelection;
2181
+ var sourceColumns;
2182
+ var selectionRows;
2183
+ var selectionColumns;
2184
+ var addedRows = [];
2185
+ var addedRecord;
2186
+ var requiredRows;
2187
+ var maxSourceColumns = 0;
2188
+ var maxGridColumns = 0;
2189
+ var pasteMeta;
2190
+ var addMeta = {
2191
+ type: 'pasteRows',
2192
+ originalType: 'paste',
2193
+ action: 'add'
2194
+ };
2195
+
2196
+ if (!bounds || !matrix || !matrix.length) {
2197
+ return 0;
2198
+ }
2199
+
2200
+ startCell = grid[bounds.startRow][bounds.startColumn];
2201
+ lastCell = startCell;
2202
+ fillSelection = this.options.pasteSingleToSelection && matrix.length === 1 &&
2203
+ matrix[0].length === 1 && this.selectedCells.length > 1;
2204
+ sourceColumns = matrix[0].length;
2205
+ selectionRows = bounds.endRow - bounds.startRow + 1;
2206
+ selectionColumns = bounds.endColumn - bounds.startColumn + 1;
2207
+ rectangularSelection = this.selectedCells.length ===
2208
+ selectionRows * selectionColumns;
2209
+
2210
+ for (row = 0; row < matrix.length; row += 1) {
2211
+ maxSourceColumns = Math.max(maxSourceColumns, matrix[row].length);
2212
+ if (matrix[row].length !== sourceColumns) {
2213
+ rectangularSource = false;
2214
+ }
2215
+ }
2216
+ if (rectangularSelection) {
2217
+ for (row = bounds.startRow; row <= bounds.endRow; row += 1) {
2218
+ for (column = bounds.startColumn;
2219
+ column <= bounds.endColumn; column += 1) {
2220
+ if (!grid[row] || !grid[row][column]) {
2221
+ rectangularSelection = false;
2222
+ break;
2223
+ }
2224
+ }
2225
+ if (!rectangularSelection) {
2226
+ break;
2227
+ }
2228
+ }
2229
+ }
2230
+ repeatSelection = !fillSelection &&
2231
+ this.options.pasteRepeatToSelection !== false &&
2232
+ this.selectedCells.length > 1 && rectangularSource &&
2233
+ rectangularSelection && sourceColumns > 0 &&
2234
+ (matrix.length > 1 || sourceColumns > 1) &&
2235
+ selectionRows >= matrix.length && selectionColumns >= sourceColumns &&
2236
+ (selectionRows > matrix.length || selectionColumns > sourceColumns) &&
2237
+ selectionRows % matrix.length === 0 &&
2238
+ selectionColumns % sourceColumns === 0;
2239
+ for (row = 0; row < grid.length; row += 1) {
2240
+ maxGridColumns = Math.max(maxGridColumns, grid[row].length);
2241
+ }
2242
+ if (!fillSelection && !repeatSelection && this.options.overflow === 'reject' &&
2243
+ bounds.startColumn + maxSourceColumns > maxGridColumns) {
2244
+ this._reportError(new Error('VirtualExcelTable: 붙여넣을 범위가 테이블 열 크기를 초과했습니다.'));
2245
+ return 0;
2246
+ }
2247
+
2248
+ if (!fillSelection && !repeatSelection &&
2249
+ this.options.autoAddRowsOnPaste !== false) {
2250
+ requiredRows = bounds.startRow + matrix.length;
2251
+ while (grid.length < requiredRows) {
2252
+ addedRecord = this._appendRow(null, addMeta, false);
2253
+ if (!addedRecord) {
2254
+ break;
2255
+ }
2256
+ addedRows.push(addedRecord);
2257
+ grid = this._getGrid();
2258
+ }
2259
+ if (addedRows.length) {
2260
+ this._notifyRowsChange(addedRows, addMeta);
2261
+ grid = this._getGrid();
2262
+ bounds = this._selectionBounds(grid);
2263
+ }
2264
+ }
2265
+
2266
+ if (fillSelection) {
2267
+ for (row = 0; row < this.selectedCells.length; row += 1) {
2268
+ assignments.push({ cell: this.selectedCells[row], value: matrix[0][0] });
2269
+ }
2270
+ selectionAfter = selectionBefore;
2271
+ } else if (repeatSelection) {
2272
+ for (row = bounds.startRow; row <= bounds.endRow; row += 1) {
2273
+ for (column = bounds.startColumn;
2274
+ column <= bounds.endColumn; column += 1) {
2275
+ targetCell = grid[row] && grid[row][column];
2276
+ if (targetCell) {
2277
+ assignments.push({
2278
+ cell: targetCell,
2279
+ value: matrix[(row - bounds.startRow) % matrix.length]
2280
+ [(column - bounds.startColumn) % sourceColumns]
2281
+ });
2282
+ }
2283
+ }
2284
+ }
2285
+ selectionAfter = selectionBefore;
2286
+ } else {
2287
+ for (row = 0; row < matrix.length; row += 1) {
2288
+ for (column = 0; column < matrix[row].length; column += 1) {
2289
+ targetCell = grid[bounds.startRow + row] &&
2290
+ grid[bounds.startRow + row][bounds.startColumn + column];
2291
+ if (!targetCell) {
2292
+ overflowed = true;
2293
+ } else {
2294
+ assignments.push({ cell: targetCell, value: matrix[row][column] });
2295
+ lastCell = targetCell;
2296
+ }
2297
+ }
2298
+ }
2299
+ selectionAfter = { type: 'cells', anchor: startCell, active: lastCell };
2300
+ }
2301
+
2302
+ if (overflowed && this.options.overflow === 'reject') {
2303
+ this._reportError(new Error('VirtualExcelTable: 붙여넣을 범위가 테이블 크기를 초과했습니다.'));
2304
+ return 0;
2305
+ }
2306
+
2307
+ pasteMeta = {
2308
+ type: 'paste',
2309
+ matrix: matrix,
2310
+ repeated: fillSelection || repeatSelection,
2311
+ sourceRows: matrix.length,
2312
+ sourceColumns: sourceColumns,
2313
+ targetRows: fillSelection || repeatSelection ? selectionRows : matrix.length,
2314
+ targetColumns: fillSelection || repeatSelection ?
2315
+ selectionColumns : maxSourceColumns
2316
+ };
2317
+ changes = this._makeChanges(assignments);
2318
+ if (!this._applyChanges(changes, pasteMeta)) {
2319
+ return 0;
2320
+ }
2321
+ this._record('paste', changes, selectionBefore, selectionAfter, addedRows);
2322
+ this._restoreSelection(selectionAfter, false);
2323
+ return changes.length;
2324
+ };
2325
+
2326
+ Grid.prototype._reportError = function (error) {
2327
+ if (typeof this.options.onError === 'function') {
2328
+ try {
2329
+ this.options.onError(error);
2330
+ return;
2331
+ } catch (ignore) {
2332
+ /* Fall through to console for an error inside onError itself. */
2333
+ }
2334
+ }
2335
+ if (root && root.console && typeof root.console.error === 'function') {
2336
+ root.console.error(error);
2337
+ }
2338
+ };
2339
+
2340
+ Grid.prototype._assertActive = function () {
2341
+ if (this.destroyed) {
2342
+ throw new Error('VirtualExcelTable: destroy()된 인스턴스는 다시 사용할 수 없습니다.');
2343
+ }
2344
+ };
2345
+
2346
+ Grid.prototype.refresh = function () {
2347
+ var grid;
2348
+ var selected = [];
2349
+ var i;
2350
+
2351
+ this._assertActive();
2352
+ grid = this._getGrid();
2353
+
2354
+ for (i = 0; i < this.selectedCells.length; i += 1) {
2355
+ if (indexOf(this.knownCells, this.selectedCells[i]) !== -1) {
2356
+ selected.push(this.selectedCells[i]);
2357
+ }
2358
+ }
2359
+ this.selectedCells = selected;
2360
+
2361
+ if (this.anchorCell && indexOf(this.knownCells, this.anchorCell) === -1) {
2362
+ this.anchorCell = null;
2363
+ }
2364
+ if (this.activeCell && indexOf(this.knownCells, this.activeCell) === -1) {
2365
+ this.activeCell = null;
2366
+ }
2367
+ if (!this.anchorCell || !this.activeCell) {
2368
+ this._clearSelectionClasses();
2369
+ this._clearHeaderSelectionClasses();
2370
+ this.selectedCells = [];
2371
+ this.selectionType = 'cells';
2372
+ }
2373
+
2374
+ return grid.length;
2375
+ };
2376
+
2377
+ Grid.prototype.select = function (startRow, startColumn, endRow, endColumn) {
2378
+ var grid;
2379
+ var anchor;
2380
+ var active;
2381
+
2382
+ this._assertActive();
2383
+ grid = this._getGrid();
2384
+
2385
+ endRow = typeof endRow === 'number' ? endRow : startRow;
2386
+ endColumn = typeof endColumn === 'number' ? endColumn : startColumn;
2387
+ anchor = grid[startRow] && grid[startRow][startColumn];
2388
+ active = grid[endRow] && grid[endRow][endColumn];
2389
+
2390
+ if (!anchor || !active) {
2391
+ throw new Error('VirtualExcelTable: 선택 좌표가 테이블 범위를 벗어났습니다.');
2392
+ }
2393
+ this._setSelection(anchor, active, true);
2394
+ return this;
2395
+ };
2396
+
2397
+ Grid.prototype.selectAll = function () {
2398
+ var grid;
2399
+ var lastRow;
2400
+
2401
+ this._assertActive();
2402
+ grid = this._getGrid();
2403
+
2404
+ if (!grid.length || !grid[0].length) {
2405
+ return this;
2406
+ }
2407
+ lastRow = grid.length - 1;
2408
+ this._setSelection(
2409
+ grid[0][0],
2410
+ grid[lastRow][grid[lastRow].length - 1],
2411
+ true,
2412
+ 'all'
2413
+ );
2414
+ return this;
2415
+ };
2416
+
2417
+ Grid.prototype.getSelection = function () {
2418
+ var grid;
2419
+ var bounds;
2420
+
2421
+ this._assertActive();
2422
+ grid = this._getGrid();
2423
+ bounds = this._selectionBounds(grid);
2424
+
2425
+ if (!bounds) {
2426
+ return null;
2427
+ }
2428
+ return {
2429
+ type: this.selectionType || 'cells',
2430
+ start: { row: bounds.startRow, column: bounds.startColumn },
2431
+ end: { row: bounds.endRow, column: bounds.endColumn },
2432
+ cells: this.selectedCells.slice(0)
2433
+ };
2434
+ };
2435
+
2436
+ Grid.prototype.getCopyText = function () {
2437
+ var grid;
2438
+ var bounds;
2439
+ var lines = [];
2440
+ var fields;
2441
+ var cell;
2442
+ var row;
2443
+ var column;
2444
+
2445
+ this._assertActive();
2446
+ grid = this._getGrid();
2447
+ bounds = this._selectionBounds(grid);
2448
+ if (!bounds) {
2449
+ return '';
2450
+ }
2451
+
2452
+ for (row = bounds.startRow; row <= bounds.endRow; row += 1) {
2453
+ fields = [];
2454
+ for (column = bounds.startColumn; column <= bounds.endColumn; column += 1) {
2455
+ cell = grid[row] && grid[row][column];
2456
+ if (cell && indexOf(this.selectedCells, cell) !== -1) {
2457
+ fields.push(escapeClipboardField(this._readDisplayValue(cell)));
2458
+ } else {
2459
+ fields.push('');
2460
+ }
2461
+ }
2462
+ lines.push(fields.join('\t'));
2463
+ }
2464
+ return lines.join('\r\n');
2465
+ };
2466
+
2467
+ Grid.prototype.copy = function () {
2468
+ var result;
2469
+
2470
+ this._assertActive();
2471
+ if (!this.selectedCells.length || !document.execCommand) {
2472
+ return false;
2473
+ }
2474
+ this._focusTable();
2475
+ try {
2476
+ result = document.execCommand('copy');
2477
+ return result !== false;
2478
+ } catch (error) {
2479
+ this._reportError(error);
2480
+ return false;
2481
+ }
2482
+ };
2483
+
2484
+ Grid.prototype._inferJsonKeys = function (grid, providedKeys) {
2485
+ var keys = [];
2486
+ var used = {};
2487
+ var maxColumns = 0;
2488
+ var headerInfo;
2489
+ var target;
2490
+ var name;
2491
+ var match;
2492
+ var key;
2493
+ var base;
2494
+ var suffix;
2495
+ var row;
2496
+ var column;
2497
+ var i;
2498
+
2499
+ for (row = 0; row < grid.length; row += 1) {
2500
+ maxColumns = Math.max(maxColumns, grid[row].length);
2501
+ }
2502
+
2503
+ for (column = 0; column < maxColumns; column += 1) {
2504
+ key = providedKeys && providedKeys[column] !== null &&
2505
+ typeof providedKeys[column] !== 'undefined' ?
2506
+ String(providedKeys[column]) : '';
2507
+
2508
+ if (!key) {
2509
+ for (i = 0; i < this.knownHeaders.length; i += 1) {
2510
+ headerInfo = this._getHeaderInfo(this.knownHeaders[i], grid);
2511
+ if (headerInfo && headerInfo.type === 'columns' &&
2512
+ headerInfo.index === column &&
2513
+ this.knownHeaders[i].getAttribute('data-excel-key')) {
2514
+ key = this.knownHeaders[i].getAttribute('data-excel-key');
2515
+ break;
2516
+ }
2517
+ }
2518
+ }
2519
+
2520
+ if (!key) {
2521
+ for (row = 0; row < grid.length; row += 1) {
2522
+ if (!grid[row][column]) {
2523
+ continue;
2524
+ }
2525
+ target = this._getTarget(grid[row][column]);
2526
+ name = target && target.getAttribute ? target.getAttribute('name') : '';
2527
+ if (name) {
2528
+ match = name.match(/(?:^|\.)([^.\[\]]+)$/) ||
2529
+ name.match(/\[([^\]]+)\]$/);
2530
+ if (match) {
2531
+ key = match[1];
2532
+ break;
2533
+ }
2534
+ }
2535
+ }
2536
+ }
2537
+
2538
+ base = key || ('key' + (column + 1));
2539
+ if (base === '__proto__') {
2540
+ base = 'key' + (column + 1);
2541
+ }
2542
+ key = base;
2543
+ suffix = 2;
2544
+ while (used[key]) {
2545
+ key = base + '_' + suffix;
2546
+ suffix += 1;
2547
+ }
2548
+ used[key] = true;
2549
+ keys.push(key);
2550
+ }
2551
+ return keys;
2552
+ };
2553
+
2554
+ Grid.prototype.getJsonData = function (columnKeys, options) {
2555
+ var grid;
2556
+ var keys;
2557
+ var result = [];
2558
+ var item;
2559
+ var value;
2560
+ var empty;
2561
+ var row;
2562
+ var column;
2563
+
2564
+ this._assertActive();
2565
+ if (columnKeys && Object.prototype.toString.call(columnKeys) !== '[object Array]') {
2566
+ throw new Error('VirtualExcelTable: JSON 열 키는 문자열 배열이어야 합니다.');
2567
+ }
2568
+ options = options || {};
2569
+ grid = this._getGrid();
2570
+ keys = this._inferJsonKeys(grid, columnKeys || null);
2571
+
2572
+ for (row = 0; row < grid.length; row += 1) {
2573
+ item = {};
2574
+ empty = true;
2575
+ for (column = 0; column < keys.length; column += 1) {
2576
+ value = grid[row][column] ? this._readValue(grid[row][column]) : '';
2577
+ item[keys[column]] = value;
2578
+ if (value !== '') {
2579
+ empty = false;
2580
+ }
2581
+ }
2582
+ if (!(options.skipEmptyRows === true && empty)) {
2583
+ result.push(item);
2584
+ }
2585
+ }
2586
+ return result;
2587
+ };
2588
+
2589
+ Grid.prototype.toJSON = function (columnKeys, options) {
2590
+ if (Object.prototype.toString.call(columnKeys) !== '[object Array]') {
2591
+ columnKeys = null;
2592
+ }
2593
+ return this.getJsonData(columnKeys, options);
2594
+ };
2595
+
2596
+ Grid.prototype.paste = function (text) {
2597
+ this._assertActive();
2598
+ if (this.editingCell) {
2599
+ this._finishEditMode(false, false);
2600
+ }
2601
+ if (!this._ensureSelection()) {
2602
+ return 0;
2603
+ }
2604
+ return this._pasteMatrix(parseClipboardText(text));
2605
+ };
2606
+
2607
+ Grid.prototype.clear = function () {
2608
+ var selectionBefore;
2609
+ var assignments = [];
2610
+ var changes;
2611
+ var i;
2612
+
2613
+ this._assertActive();
2614
+ if (this.editingCell) {
2615
+ this._finishEditMode(false, false);
2616
+ }
2617
+
2618
+ if (!this.selectedCells.length) {
2619
+ return 0;
2620
+ }
2621
+
2622
+ selectionBefore = this._snapshotSelection();
2623
+ for (i = 0; i < this.selectedCells.length; i += 1) {
2624
+ assignments.push({ cell: this.selectedCells[i], value: '' });
2625
+ }
2626
+ changes = this._makeChanges(assignments);
2627
+ if (!this._applyChanges(changes, { type: 'delete' })) {
2628
+ return 0;
2629
+ }
2630
+ this._record('delete', changes, selectionBefore, selectionBefore);
2631
+ return changes.length;
2632
+ };
2633
+
2634
+ Grid.prototype.deleteSelection = Grid.prototype.clear;
2635
+
2636
+ Grid.prototype._appendRow = function (values, meta, notifyChange, placement) {
2637
+ var grid = this._getGrid();
2638
+ var referenceRow = placement && placement.referenceRow ?
2639
+ placement.referenceRow : null;
2640
+ var sourceRow = null;
2641
+ var insertIndex = grid.length;
2642
+ var referenceGridIndex = -1;
2643
+ var referenceNode;
2644
+ var parent;
2645
+ var gridIndex;
2646
+ var row;
2647
+ var newGrid;
2648
+ var rowCells = [];
2649
+ var keys;
2650
+ var value;
2651
+ var beforeResult;
2652
+ var record;
2653
+ var i;
2654
+ var j;
2655
+
2656
+ if (referenceRow) {
2657
+ for (gridIndex = 0; gridIndex < grid.length; gridIndex += 1) {
2658
+ if (grid[gridIndex].length &&
2659
+ grid[gridIndex][0].parentNode === referenceRow) {
2660
+ referenceGridIndex = gridIndex;
2661
+ break;
2662
+ }
2663
+ }
2664
+ if (referenceGridIndex === -1 || owningTable(referenceRow) !== this.table ||
2665
+ !matchesSelector(referenceRow, this.options.rowSelector) ||
2666
+ !referenceRow.parentNode ||
2667
+ String(referenceRow.parentNode.tagName).toUpperCase() !== 'TBODY') {
2668
+ this._reportError(new Error(
2669
+ 'VirtualExcelTable: 새 행을 삽입할 기준 행을 찾을 수 없습니다.'
2670
+ ));
2671
+ return null;
2672
+ }
2673
+ sourceRow = referenceRow;
2674
+ insertIndex = referenceGridIndex + 1;
2675
+ } else {
2676
+ sourceRow = grid.length && grid[grid.length - 1].length ?
2677
+ grid[grid.length - 1][0].parentNode : null;
2678
+ }
2679
+ parent = sourceRow ? sourceRow.parentNode :
2680
+ (this.table.tBodies.length ? this.table.tBodies[0] : null);
2681
+
2682
+ try {
2683
+ if (typeof this.options.rowFactory === 'function') {
2684
+ row = this.options.rowFactory(
2685
+ this.table,
2686
+ insertIndex,
2687
+ values,
2688
+ referenceRow
2689
+ );
2690
+ } else if (sourceRow) {
2691
+ row = prepareClonedRow(sourceRow.cloneNode(true));
2692
+ }
2693
+ } catch (factoryError) {
2694
+ this._reportError(factoryError);
2695
+ return null;
2696
+ }
2697
+
2698
+ if (!row || String(row.tagName).toUpperCase() !== 'TR' || row.parentNode) {
2699
+ this._reportError(new Error(
2700
+ 'VirtualExcelTable: 행을 추가하려면 rowFactory가 분리된 TR을 반환하거나 기존 행이 있어야 합니다.'
2701
+ ));
2702
+ return null;
2703
+ }
2704
+ if (!parent) {
2705
+ parent = document.createElement('tbody');
2706
+ this.table.appendChild(parent);
2707
+ }
2708
+
2709
+ if (typeof this.options.beforeRowAdd === 'function') {
2710
+ try {
2711
+ beforeResult = this.options.beforeRowAdd(row, meta);
2712
+ } catch (beforeError) {
2713
+ this._reportError(beforeError);
2714
+ return null;
2715
+ }
2716
+ if (beforeResult === false) {
2717
+ return null;
2718
+ }
2719
+ }
2720
+
2721
+ if (referenceRow && (referenceRow.parentNode !== parent ||
2722
+ !contains(this.table, referenceRow))) {
2723
+ this._reportError(new Error(
2724
+ 'VirtualExcelTable: 행 추가 준비 중 기준 행의 위치가 변경되었습니다.'
2725
+ ));
2726
+ return null;
2727
+ }
2728
+
2729
+ try {
2730
+ referenceNode = referenceRow ? referenceRow.nextSibling : null;
2731
+ parent.insertBefore(row, referenceNode);
2732
+ } catch (appendError) {
2733
+ this._reportError(appendError);
2734
+ return null;
2735
+ }
2736
+
2737
+ this.refresh();
2738
+ newGrid = this._getGrid();
2739
+ for (i = 0; i < newGrid.length; i += 1) {
2740
+ if (newGrid[i].length && newGrid[i][0].parentNode === row) {
2741
+ rowCells = newGrid[i];
2742
+ break;
2743
+ }
2744
+ }
2745
+
2746
+ if (Object.prototype.toString.call(values) === '[object Array]') {
2747
+ for (j = 0; j < rowCells.length && j < values.length; j += 1) {
2748
+ if (!this._isReadOnly(rowCells[j])) {
2749
+ this._writeValue(rowCells[j], values[j], true);
2750
+ }
2751
+ }
2752
+ } else if (values && typeof values === 'object') {
2753
+ keys = this._inferJsonKeys(newGrid, null);
2754
+ for (j = 0; j < rowCells.length && j < keys.length; j += 1) {
2755
+ if (own(values, keys[j]) && !this._isReadOnly(rowCells[j])) {
2756
+ value = values[keys[j]];
2757
+ this._writeValue(rowCells[j], value, true);
2758
+ }
2759
+ }
2760
+ }
2761
+
2762
+ record = {
2763
+ row: row,
2764
+ parent: parent,
2765
+ nextSibling: row.nextSibling,
2766
+ sectionIndex: row.sectionRowIndex
2767
+ };
2768
+ if (notifyChange !== false) {
2769
+ this._notifyRowsChange([record], meta);
2770
+ }
2771
+ return record;
2772
+ };
2773
+
2774
+ Grid.prototype.addRow = function (values) {
2775
+ var meta = {
2776
+ type: 'addRow',
2777
+ originalType: 'addRow',
2778
+ action: 'add'
2779
+ };
2780
+ var record;
2781
+ var grid;
2782
+ var row;
2783
+ var selectionBefore;
2784
+ var selectionAfter;
2785
+
2786
+ this._assertActive();
2787
+ if (this.editingCell) {
2788
+ this._finishEditMode(false, false);
2789
+ }
2790
+ selectionBefore = this._snapshotSelection();
2791
+ record = this._appendRow(values, meta, true);
2792
+ if (!record) {
2793
+ return null;
2794
+ }
2795
+
2796
+ grid = this._getGrid();
2797
+ for (row = 0; row < grid.length; row += 1) {
2798
+ if (grid[row].length && grid[row][0].parentNode === record.row) {
2799
+ this._setSelection(grid[row][0], grid[row][0], true);
2800
+ break;
2801
+ }
2802
+ }
2803
+ selectionAfter = this._snapshotSelection();
2804
+ this._pushHistory({
2805
+ kind: 'addedRows',
2806
+ type: 'addRow',
2807
+ addedRows: [record],
2808
+ selectionBefore: selectionBefore,
2809
+ selectionAfter: selectionAfter
2810
+ });
2811
+ return record.row;
2812
+ };
2813
+
2814
+ Grid.prototype.insertRowBelow = function (values) {
2815
+ var grid;
2816
+ var activePosition;
2817
+ var referenceRow;
2818
+ var preferredColumn;
2819
+ var meta;
2820
+ var record;
2821
+ var selectionBefore;
2822
+ var selectionAfter;
2823
+ var row;
2824
+ var column;
2825
+
2826
+ this._assertActive();
2827
+ if (this.editingCell) {
2828
+ this._finishEditMode(false, false);
2829
+ }
2830
+ if ((this.selectionType !== 'cells' && this.selectionType !== 'rows') ||
2831
+ !this.activeCell) {
2832
+ return null;
2833
+ }
2834
+
2835
+ grid = this._getGrid();
2836
+ activePosition = this._positionOf(grid, this.activeCell);
2837
+ if (!activePosition) {
2838
+ return null;
2839
+ }
2840
+ referenceRow = this.activeCell.parentNode;
2841
+ preferredColumn = this.selectionType === 'cells' ? activePosition.column : 0;
2842
+ meta = {
2843
+ type: 'insertRow',
2844
+ originalType: 'insertRow',
2845
+ action: 'add',
2846
+ position: 'after',
2847
+ insertIndex: activePosition.row + 1
2848
+ };
2849
+ selectionBefore = this._snapshotSelection();
2850
+ record = this._appendRow(values, meta, true, {
2851
+ referenceRow: referenceRow
2852
+ });
2853
+ if (!record) {
2854
+ return null;
2855
+ }
2856
+
2857
+ grid = this._getGrid();
2858
+ for (row = 0; row < grid.length; row += 1) {
2859
+ if (grid[row].length && grid[row][0].parentNode === record.row) {
2860
+ column = Math.max(0, Math.min(grid[row].length - 1, preferredColumn));
2861
+ this._setSelection(grid[row][column], grid[row][column], true);
2862
+ break;
2863
+ }
2864
+ }
2865
+ selectionAfter = this._snapshotSelection();
2866
+ this._pushHistory({
2867
+ kind: 'addedRows',
2868
+ type: 'insertRow',
2869
+ addedRows: [record],
2870
+ selectionBefore: selectionBefore,
2871
+ selectionAfter: selectionAfter
2872
+ });
2873
+ return record.row;
2874
+ };
2875
+
2876
+ Grid.prototype.addRowBelow = Grid.prototype.insertRowBelow;
2877
+
2878
+ Grid.prototype.deleteRows = function () {
2879
+ var grid;
2880
+ var bounds;
2881
+ var records;
2882
+ var rows = [];
2883
+ var selectionBefore;
2884
+ var selectionAfter;
2885
+ var targetRow;
2886
+ var targetColumn;
2887
+ var beforeResult;
2888
+ var removed = [];
2889
+ var meta = {
2890
+ type: 'deleteRows',
2891
+ originalType: 'deleteRows',
2892
+ action: 'remove'
2893
+ };
2894
+ var i;
2895
+
2896
+ this._assertActive();
2897
+ if (this.editingCell) {
2898
+ this._finishEditMode(false, false);
2899
+ }
2900
+ this.refresh();
2901
+ if (!this.selectedCells.length) {
2902
+ return 0;
2903
+ }
2904
+
2905
+ grid = this._getGrid();
2906
+ bounds = this._selectionBounds(grid);
2907
+ records = this._collectSelectedRowRecords();
2908
+ if (!bounds || !records.length) {
2909
+ return 0;
2910
+ }
2911
+ if (grid.length - records.length < parseInt(this.options.minRows, 10)) {
2912
+ this._reportError(new Error(
2913
+ 'VirtualExcelTable: 최소 ' + this.options.minRows + '개의 행은 남아 있어야 합니다.'
2914
+ ));
2915
+ return 0;
2916
+ }
2917
+
2918
+ for (i = 0; i < records.length; i += 1) {
2919
+ if (this._isRowReadOnly(records[i].row)) {
2920
+ this._reportError(new Error(
2921
+ 'VirtualExcelTable: 삭제할 수 없는 읽기 전용 행이 선택되어 있습니다.'
2922
+ ));
2923
+ return 0;
2924
+ }
2925
+ rows.push(records[i].row);
2926
+ }
2927
+
2928
+ if (typeof this.options.beforeRowsDelete === 'function') {
2929
+ try {
2930
+ beforeResult = this.options.beforeRowsDelete(rows.slice(0), meta);
2931
+ } catch (beforeError) {
2932
+ this._reportError(beforeError);
2933
+ return 0;
2934
+ }
2935
+ if (beforeResult === false) {
2936
+ return 0;
2937
+ }
2938
+ }
2939
+
2940
+ /* A callback may have changed the DOM, so validate the entire batch again. */
2941
+ for (i = 0; i < records.length; i += 1) {
2942
+ if (records[i].row.parentNode !== records[i].parent ||
2943
+ !contains(this.table, records[i].row)) {
2944
+ this._reportError(new Error(
2945
+ 'VirtualExcelTable: 행 삭제 전에 테이블 구조가 변경되었습니다.'
2946
+ ));
2947
+ return 0;
2948
+ }
2949
+ }
2950
+
2951
+ selectionBefore = this._snapshotSelection();
2952
+ this._clearSelectionState();
2953
+ try {
2954
+ for (i = records.length - 1; i >= 0; i -= 1) {
2955
+ records[i].parent.removeChild(records[i].row);
2956
+ removed.push(records[i]);
2957
+ }
2958
+ } catch (removeError) {
2959
+ for (i = 0; i < removed.length; i += 1) {
2960
+ try {
2961
+ removed[i].parent.insertBefore(
2962
+ removed[i].row,
2963
+ removed[i].nextSibling &&
2964
+ removed[i].nextSibling.parentNode === removed[i].parent ?
2965
+ removed[i].nextSibling : null
2966
+ );
2967
+ } catch (ignore) {
2968
+ /* Best-effort rollback; preserve the original error. */
2969
+ }
2970
+ }
2971
+ this.refresh();
2972
+ this._restoreSelection(selectionBefore, false);
2973
+ this._reportError(removeError);
2974
+ return 0;
2975
+ }
2976
+
2977
+ this.refresh();
2978
+ grid = this._getGrid();
2979
+ if (grid.length) {
2980
+ targetRow = Math.min(bounds.startRow, grid.length - 1);
2981
+ targetColumn = Math.min(bounds.startColumn, grid[targetRow].length - 1);
2982
+ if (grid[targetRow][targetColumn]) {
2983
+ this._setSelection(
2984
+ grid[targetRow][targetColumn],
2985
+ grid[targetRow][targetColumn],
2986
+ true
2987
+ );
2988
+ }
2989
+ } else {
2990
+ this._notifySelectionChange();
2991
+ }
2992
+ selectionAfter = this._snapshotSelection();
2993
+ this._recordRows(records, selectionBefore, selectionAfter);
2994
+ this._notifyRowsChange(records, meta);
2995
+ return records.length;
2996
+ };
2997
+
2998
+ Grid.prototype.removeSelectedRows = Grid.prototype.deleteRows;
2999
+
3000
+ Grid.prototype._undoDeletedRows = function (entry) {
3001
+ var inserted = [];
3002
+ var record;
3003
+ var reference;
3004
+ var meta = {
3005
+ type: 'undo',
3006
+ originalType: 'deleteRows',
3007
+ action: 'restore'
3008
+ };
3009
+ var i;
3010
+
3011
+ for (i = 0; i < entry.rows.length; i += 1) {
3012
+ record = entry.rows[i];
3013
+ if (!contains(this.table, record.parent) || record.row.parentNode) {
3014
+ this._reportError(new Error(
3015
+ 'VirtualExcelTable: 삭제된 행의 원래 위치가 변경되어 복원할 수 없습니다.'
3016
+ ));
3017
+ return false;
3018
+ }
3019
+ }
3020
+
3021
+ try {
3022
+ for (i = entry.rows.length - 1; i >= 0; i -= 1) {
3023
+ record = entry.rows[i];
3024
+ reference = record.nextSibling &&
3025
+ record.nextSibling.parentNode === record.parent ?
3026
+ record.nextSibling : (record.parent.rows[record.sectionIndex] || null);
3027
+ record.parent.insertBefore(record.row, reference);
3028
+ inserted.push(record);
3029
+ }
3030
+ } catch (insertError) {
3031
+ for (i = 0; i < inserted.length; i += 1) {
3032
+ if (inserted[i].row.parentNode === inserted[i].parent) {
3033
+ inserted[i].parent.removeChild(inserted[i].row);
3034
+ }
3035
+ }
3036
+ this.refresh();
3037
+ this._reportError(insertError);
3038
+ return false;
3039
+ }
3040
+
3041
+ this.refresh();
3042
+ this._restoreSelection(entry.selectionBefore, true);
3043
+ this._notifyRowsChange(entry.rows, meta);
3044
+ return true;
3045
+ };
3046
+
3047
+ Grid.prototype._redoDeletedRows = function (entry) {
3048
+ var removed = [];
3049
+ var record;
3050
+ var grid = this._getGrid();
3051
+ var meta = {
3052
+ type: 'redo',
3053
+ originalType: 'deleteRows',
3054
+ action: 'remove'
3055
+ };
3056
+ var i;
3057
+
3058
+ if (grid.length - entry.rows.length < parseInt(this.options.minRows, 10)) {
3059
+ this._reportError(new Error(
3060
+ 'VirtualExcelTable: 최소 ' + this.options.minRows + '개의 행은 남아 있어야 합니다.'
3061
+ ));
3062
+ return false;
3063
+ }
3064
+
3065
+ for (i = 0; i < entry.rows.length; i += 1) {
3066
+ record = entry.rows[i];
3067
+ if (record.row.parentNode !== record.parent ||
3068
+ !contains(this.table, record.row) || this._isRowReadOnly(record.row)) {
3069
+ this._reportError(new Error(
3070
+ 'VirtualExcelTable: 행 구조가 변경되었거나 읽기 전용이어서 다시 삭제할 수 없습니다.'
3071
+ ));
3072
+ return false;
3073
+ }
3074
+ }
3075
+
3076
+ this._clearSelectionState();
3077
+ try {
3078
+ for (i = entry.rows.length - 1; i >= 0; i -= 1) {
3079
+ entry.rows[i].parent.removeChild(entry.rows[i].row);
3080
+ removed.push(entry.rows[i]);
3081
+ }
3082
+ } catch (removeError) {
3083
+ for (i = 0; i < removed.length; i += 1) {
3084
+ record = removed[i];
3085
+ record.parent.insertBefore(
3086
+ record.row,
3087
+ record.nextSibling && record.nextSibling.parentNode === record.parent ?
3088
+ record.nextSibling : (record.parent.rows[record.sectionIndex] || null)
3089
+ );
3090
+ }
3091
+ this.refresh();
3092
+ this._restoreSelection(entry.selectionBefore, false);
3093
+ this._reportError(removeError);
3094
+ return false;
3095
+ }
3096
+
3097
+ this.refresh();
3098
+ this._restoreSelection(entry.selectionAfter, true);
3099
+ if (!this.activeCell) {
3100
+ this._notifySelectionChange();
3101
+ }
3102
+ this._notifyRowsChange(entry.rows, meta);
3103
+ return true;
3104
+ };
3105
+
3106
+ Grid.prototype._removePasteAddedRows = function (entry) {
3107
+ var records = entry.addedRows || [];
3108
+ var meta = {
3109
+ type: 'undo',
3110
+ originalType: entry.type,
3111
+ action: 'removeAddedRows'
3112
+ };
3113
+ var i;
3114
+
3115
+ for (i = 0; i < records.length; i += 1) {
3116
+ if (records[i].row.parentNode !== records[i].parent) {
3117
+ this._reportError(new Error(
3118
+ 'VirtualExcelTable: 추가된 행 구조가 변경되어 되돌릴 수 없습니다.'
3119
+ ));
3120
+ return false;
3121
+ }
3122
+ }
3123
+ this._clearSelectionState();
3124
+ for (i = records.length - 1; i >= 0; i -= 1) {
3125
+ records[i].parent.removeChild(records[i].row);
3126
+ }
3127
+ this.refresh();
3128
+ this._notifyRowsChange(records, meta);
3129
+ return true;
3130
+ };
3131
+
3132
+ Grid.prototype._restorePasteAddedRows = function (entry) {
3133
+ var records = entry.addedRows || [];
3134
+ var record;
3135
+ var reference;
3136
+ var meta = {
3137
+ type: 'redo',
3138
+ originalType: entry.type,
3139
+ action: 'restoreAddedRows'
3140
+ };
3141
+ var i;
3142
+
3143
+ for (i = 0; i < records.length; i += 1) {
3144
+ if (!contains(this.table, records[i].parent) || records[i].row.parentNode) {
3145
+ this._reportError(new Error(
3146
+ 'VirtualExcelTable: 추가된 행의 위치가 변경되어 다시 실행할 수 없습니다.'
3147
+ ));
3148
+ return false;
3149
+ }
3150
+ }
3151
+ for (i = 0; i < records.length; i += 1) {
3152
+ record = records[i];
3153
+ reference = record.nextSibling && record.nextSibling.parentNode === record.parent ?
3154
+ record.nextSibling : (record.parent.rows[record.sectionIndex] || null);
3155
+ record.parent.insertBefore(record.row, reference);
3156
+ }
3157
+ this.refresh();
3158
+ this._notifyRowsChange(records, meta);
3159
+ return true;
3160
+ };
3161
+
3162
+ Grid.prototype.undo = function () {
3163
+ var entry;
3164
+ var changes = [];
3165
+ var original;
3166
+ var i;
3167
+
3168
+ this._assertActive();
3169
+
3170
+ if (!this.undoStack.length) {
3171
+ return false;
3172
+ }
3173
+ entry = this.undoStack[this.undoStack.length - 1];
3174
+ if (entry.kind === 'rows') {
3175
+ if (!this._undoDeletedRows(entry)) {
3176
+ return false;
3177
+ }
3178
+ this.undoStack.pop();
3179
+ this.redoStack.push(entry);
3180
+ return true;
3181
+ }
3182
+ if (entry.kind === 'addedRows') {
3183
+ if (!this._removePasteAddedRows(entry)) {
3184
+ return false;
3185
+ }
3186
+ this.undoStack.pop();
3187
+ this.redoStack.push(entry);
3188
+ this._restoreSelection(entry.selectionBefore, true);
3189
+ return true;
3190
+ }
3191
+ if (entry.addedRows) {
3192
+ for (i = 0; i < entry.addedRows.length; i += 1) {
3193
+ if (entry.addedRows[i].row.parentNode !== entry.addedRows[i].parent) {
3194
+ this._reportError(new Error(
3195
+ 'VirtualExcelTable: 자동 추가된 행 구조가 변경되어 되돌릴 수 없습니다.'
3196
+ ));
3197
+ return false;
3198
+ }
3199
+ }
3200
+ }
3201
+ for (i = entry.changes.length - 1; i >= 0; i -= 1) {
3202
+ original = entry.changes[i];
3203
+ if (contains(this.table, original.cell)) {
3204
+ if (this._isReadOnly(original.cell)) {
3205
+ this._reportError(new Error(
3206
+ 'VirtualExcelTable: 읽기 전용으로 변경된 셀이 있어 되돌릴 수 없습니다.'
3207
+ ));
3208
+ return false;
3209
+ }
3210
+ changes.push({
3211
+ cell: original.cell,
3212
+ oldValue: this._readValue(original.cell),
3213
+ newValue: original.oldValue
3214
+ });
3215
+ }
3216
+ }
3217
+ if (!this._applyChanges(changes, { type: 'undo', originalType: entry.type })) {
3218
+ return false;
3219
+ }
3220
+ if (entry.addedRows && !this._removePasteAddedRows(entry)) {
3221
+ return false;
3222
+ }
3223
+ this.undoStack.pop();
3224
+ this.redoStack.push(entry);
3225
+ this._restoreSelection(entry.selectionBefore, true);
3226
+ return true;
3227
+ };
3228
+
3229
+ Grid.prototype.redo = function () {
3230
+ var entry;
3231
+ var changes = [];
3232
+ var original;
3233
+ var i;
3234
+
3235
+ this._assertActive();
3236
+
3237
+ if (!this.redoStack.length) {
3238
+ return false;
3239
+ }
3240
+ entry = this.redoStack[this.redoStack.length - 1];
3241
+ if (entry.kind === 'rows') {
3242
+ if (!this._redoDeletedRows(entry)) {
3243
+ return false;
3244
+ }
3245
+ this.redoStack.pop();
3246
+ this.undoStack.push(entry);
3247
+ return true;
3248
+ }
3249
+ if (entry.kind === 'addedRows') {
3250
+ if (!this._restorePasteAddedRows(entry)) {
3251
+ return false;
3252
+ }
3253
+ this.redoStack.pop();
3254
+ this.undoStack.push(entry);
3255
+ this._restoreSelection(entry.selectionAfter, true);
3256
+ return true;
3257
+ }
3258
+ if (entry.addedRows && !this._restorePasteAddedRows(entry)) {
3259
+ return false;
3260
+ }
3261
+ for (i = 0; i < entry.changes.length; i += 1) {
3262
+ original = entry.changes[i];
3263
+ if (contains(this.table, original.cell)) {
3264
+ if (this._isReadOnly(original.cell)) {
3265
+ this._reportError(new Error(
3266
+ 'VirtualExcelTable: 읽기 전용으로 변경된 셀이 있어 다시 실행할 수 없습니다.'
3267
+ ));
3268
+ return false;
3269
+ }
3270
+ changes.push({
3271
+ cell: original.cell,
3272
+ oldValue: this._readValue(original.cell),
3273
+ newValue: original.newValue
3274
+ });
3275
+ }
3276
+ }
3277
+ if (!this._applyChanges(changes, { type: 'redo', originalType: entry.type })) {
3278
+ if (entry.addedRows) {
3279
+ this._removePasteAddedRows(entry);
3280
+ }
3281
+ return false;
3282
+ }
3283
+ this.redoStack.pop();
3284
+ this.undoStack.push(entry);
3285
+ this._restoreSelection(entry.selectionAfter, true);
3286
+ return true;
3287
+ };
3288
+
3289
+ Grid.prototype.canUndo = function () {
3290
+ return this.undoStack.length > 0;
3291
+ };
3292
+
3293
+ Grid.prototype.canRedo = function () {
3294
+ return this.redoStack.length > 0;
3295
+ };
3296
+
3297
+ Grid.prototype.destroy = function () {
3298
+ var i;
3299
+
3300
+ if (this.destroyed) {
3301
+ return;
3302
+ }
3303
+ this.destroyed = true;
3304
+ this.table.removeEventListener('mousedown', this.handlers.mousedown, false);
3305
+ this.table.removeEventListener('dblclick', this.handlers.dblclick, false);
3306
+ this.table.removeEventListener('mouseover', this.handlers.mouseover, false);
3307
+ this.table.removeEventListener('focusin', this.handlers.focusin, false);
3308
+ this.table.removeEventListener('focusout', this.handlers.focusout, false);
3309
+ this.table.removeEventListener('keydown', this.handlers.keydown, false);
3310
+ this.table.removeEventListener('paste', this.handlers.paste, false);
3311
+ this.table.removeEventListener('copy', this.handlers.copy, false);
3312
+ document.removeEventListener('mouseup', this.handlers.mouseup, false);
3313
+
3314
+ for (i = 0; i < this.knownCells.length; i += 1) {
3315
+ removeClass(this.knownCells[i], CELL_CLASS);
3316
+ removeClass(this.knownCells[i], SELECTED_CLASS);
3317
+ removeClass(this.knownCells[i], ACTIVE_CLASS);
3318
+ removeClass(this.knownCells[i], READONLY_CLASS);
3319
+ }
3320
+ for (i = 0; i < this.knownHeaders.length; i += 1) {
3321
+ removeClass(this.knownHeaders[i], HEADER_CLASS);
3322
+ removeClass(this.knownHeaders[i], HEADER_SELECTED_CLASS);
3323
+ }
3324
+ removeClass(this.table, TABLE_CLASS);
3325
+ removeClass(this.table, DRAGGING_CLASS);
3326
+
3327
+ if (this.originalTabIndex === null) {
3328
+ this.table.removeAttribute('tabindex');
3329
+ } else {
3330
+ this.table.setAttribute('tabindex', this.originalTabIndex);
3331
+ }
3332
+ if (this.originalDataEnabled === null) {
3333
+ this.table.removeAttribute('data-virtual-excel-table');
3334
+ } else {
3335
+ this.table.setAttribute('data-virtual-excel-table', this.originalDataEnabled);
3336
+ }
3337
+
3338
+ this.selectedCells = [];
3339
+ this.selectedHeaders = [];
3340
+ this.selectionType = 'cells';
3341
+ this._clearEditState();
3342
+ this.undoStack = [];
3343
+ this.redoStack = [];
3344
+ this.knownCells = [];
3345
+ this.knownHeaders = [];
3346
+ if (this.table[INSTANCE_KEY] === this) {
3347
+ try {
3348
+ delete this.table[INSTANCE_KEY];
3349
+ } catch (ignore) {
3350
+ this.table[INSTANCE_KEY] = null;
3351
+ }
3352
+ }
3353
+ };
3354
+
3355
+ function attachOne(table, options) {
3356
+ ensureTable(table);
3357
+ if (table[INSTANCE_KEY] && !table[INSTANCE_KEY].destroyed) {
3358
+ return table[INSTANCE_KEY];
3359
+ }
3360
+ table[INSTANCE_KEY] = new Grid(table, options);
3361
+ return table[INSTANCE_KEY];
3362
+ }
3363
+
3364
+ function VirtualExcelTable(target, options) {
3365
+ var tables = resolveTables(target);
3366
+
3367
+ if (tables.length === 0) {
3368
+ throw new Error('VirtualExcelTable: 셀렉터에 해당하는 테이블을 찾지 못했습니다.');
3369
+ }
3370
+ if (tables.length > 1) {
3371
+ throw new Error('VirtualExcelTable: 여러 테이블에는 VirtualExcelTable.attachAll()을 사용하세요.');
3372
+ }
3373
+ return attachOne(tables[0], options);
3374
+ }
3375
+
3376
+ VirtualExcelTable.attach = VirtualExcelTable;
3377
+ VirtualExcelTable.attachAll = function (target, options) {
3378
+ var tables = resolveTables(target);
3379
+ var instances = [];
3380
+ var i;
3381
+
3382
+ for (i = 0; i < tables.length; i += 1) {
3383
+ instances.push(attachOne(tables[i], options));
3384
+ }
3385
+ return instances;
3386
+ };
3387
+ VirtualExcelTable.parseClipboardText = parseClipboardText;
3388
+ VirtualExcelTable.version = VERSION;
3389
+
3390
+ return VirtualExcelTable;
3391
+ }));