@refinitiv-ui/efx-grid 6.0.35 → 6.0.36

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.
@@ -3,6 +3,7 @@ import { GridPlugin } from "../../tr-grid-util/es6/GridPlugin.js";
3
3
  import { Conflator } from "../../tr-grid-util/es6/Conflator.js";
4
4
  import { Icon } from "../../tr-grid-util/es6/Icon.js";
5
5
  import { ElfUtil } from "../../tr-grid-util/es6/ElfUtil.js";
6
+ import { GroupDefinitions } from "../../tr-grid-util/es6/GroupDefinitions.js";
6
7
  import { Dom } from "../../tr-grid-util/es6/Dom.js";
7
8
  import { injectCss, prettifyCss } from "../../tr-grid-util/es6/Util.js";
8
9
  import { preventDefault } from "../../tr-grid-util/es6/EventDispatcher.js";
@@ -58,6 +59,51 @@ import { preventDefault } from "../../tr-grid-util/es6/EventDispatcher.js";
58
59
  * @property {Event} event Native event argument from click event
59
60
  */
60
61
 
62
+ /** @private
63
+ * @function
64
+ * @param {Array} refA
65
+ * @param {Array} refB
66
+ * @return {number}
67
+ */
68
+ var compareRef = function(refA, refB) {
69
+ var valA = refA[0];
70
+ var valB = refB[0];
71
+ var invalidA = (valA == null || valA < 0);
72
+ var invalidB = (valB == null || valB < 0);
73
+
74
+ if(invalidA) {
75
+ return invalidB ? 0 : 1;
76
+ } else if(invalidB) {
77
+ return -1;
78
+ }
79
+ if(valA < valB) {
80
+ return -1;
81
+ }
82
+ if(valB < valA) {
83
+ return 1;
84
+ }
85
+
86
+ return 0;
87
+ };
88
+ /** @private
89
+ * @description Resolve active column from the given stack object, if activeColumn is invalid
90
+ * @param {Object} stackOpt
91
+ * @return {boolean} Return true if there is any change
92
+ */
93
+ var _resolveActiveColumn = function(stackOpt) {
94
+ if(stackOpt) {
95
+ var children = stackOpt.children;
96
+ if(children && children.length) {
97
+ var activeColumn = stackOpt.activeColumn;
98
+ if(!activeColumn || children.indexOf(activeColumn) < 0) {
99
+ stackOpt.activeColumn = stackOpt.spreading ? children[children.length - 1] : children[0];
100
+ return true;
101
+ }
102
+ }
103
+ }
104
+ return false;
105
+ };
106
+
61
107
  /** @constructor
62
108
  * @extends {GridPlugin}
63
109
  */
@@ -66,22 +112,27 @@ var ColumnStackPlugin = function () {
66
112
  this._onColumnRemoved = this._onColumnRemoved.bind(this);
67
113
  this._onColumnMoved = this._onColumnMoved.bind(this);
68
114
  this._onColumnAdded = this._onColumnAdded.bind(this);
115
+ this._onBeforeBatchOperation = this._onBeforeBatchOperation.bind(this);
116
+ this._onAfterBatchOperation = this._onAfterBatchOperation.bind(this);
117
+
69
118
  this._onStackButtonClicked = this._onStackButtonClicked.bind(this);
70
119
  this._updateUI = this._updateUI.bind(this);
71
120
  this._requestStackingByFields = this._requestStackingByFields.bind(this);
121
+ this._toIdOrField = this._toIdOrField.bind(this);
122
+
72
123
  this._hosts = [];
73
- this._stacks = {};
124
+ this._groupDefs = new GroupDefinitions();
74
125
 
75
- this._conflator = new Conflator(100, this._updateUI);
126
+ this._conflator = new Conflator(50, this._updateUI);
76
127
  this._stackConflator = new Conflator(100, this._requestStackingByFields);
77
128
  };
78
129
 
79
130
  Ext.inherits(ColumnStackPlugin, GridPlugin);
80
131
 
81
- /** @type {!Object.<string, Object>}
132
+ /** @type {!GroupDefinitions}
82
133
  * @private
83
134
  */
84
- ColumnStackPlugin.prototype._stacks;
135
+ ColumnStackPlugin.prototype._groupDefs;
85
136
  /** @type {Object.<string, string>}
86
137
  * @private
87
138
  */
@@ -102,7 +153,11 @@ ColumnStackPlugin.prototype._autoStacking = false;
102
153
  /** @type {boolean}
103
154
  * @private
104
155
  */
105
- ColumnStackPlugin.prototype._stacking = false;
156
+ ColumnStackPlugin.prototype._inReordering = false;
157
+ /** @type {boolean}
158
+ * @private
159
+ */
160
+ ColumnStackPlugin.prototype._inResetting = false;
106
161
 
107
162
 
108
163
  /** @type {number}
@@ -114,7 +169,7 @@ ColumnStackPlugin._runningId = 0;
114
169
  */
115
170
  ColumnStackPlugin.prototype._generateStackId = function() {
116
171
  var sid = "Stack" + ColumnStackPlugin._runningId++;
117
- while(this._stacks[sid]) {
172
+ while(this._groupDefs.getGroup(sid)) {
118
173
  sid = "Stack" + ColumnStackPlugin._runningId++;
119
174
  }
120
175
  return sid;
@@ -156,6 +211,8 @@ ColumnStackPlugin.prototype.initialize = function (host, options) {
156
211
  host.listen("columnRemoved", this._onColumnRemoved);
157
212
  host.listen("columnMoved", this._onColumnMoved);
158
213
  host.listen("columnAdded", this._onColumnAdded);
214
+ host.listen("beforeBatchOperation", this._onBeforeBatchOperation);
215
+ host.listen("afterBatchOperation", this._onAfterBatchOperation);
159
216
  }
160
217
  host.listen("preSectionRender", this._onPreSectionRender);
161
218
 
@@ -173,12 +230,14 @@ ColumnStackPlugin.prototype.unload = function (host) {
173
230
  host.unlisten("columnRemoved", this._onColumnRemoved);
174
231
  host.unlisten("columnMoved", this._onColumnMoved);
175
232
  host.unlisten("columnAdded", this._onColumnAdded);
233
+ host.unlisten("beforeBatchOperation", this._onBeforeBatchOperation);
234
+ host.unlisten("afterBatchOperation", this._onAfterBatchOperation);
176
235
  host.unlisten("preSectionRender", this._onPreSectionRender);
177
236
 
178
237
  if(this._hosts.length <= 0) {
179
238
  this._conflator.reset();
180
239
  this._stackConflator.reset();
181
- this._stacks = {};
240
+ this._groupDefs.removeAllGroups();
182
241
  }
183
242
  this._dispose();
184
243
  };
@@ -233,9 +292,6 @@ ColumnStackPlugin.prototype.config = function (options) {
233
292
 
234
293
  var i;
235
294
  var colCount = columns.length;
236
- for(i = 0; i < colCount; ++i) {
237
- this._setUniqueRef(i, {}); // used for further column index getting
238
- }
239
295
 
240
296
  var columnStack = options.columnStack;
241
297
  var stackId = "";
@@ -257,9 +313,10 @@ ColumnStackPlugin.prototype.config = function (options) {
257
313
  }
258
314
  this._idToFields[stackId] = columnStack.fields;
259
315
  stacks[stackId] = {
260
- colRefs: columnStack.fields,
316
+ id: stackId,
261
317
  spreading: columnStack.spreading === true,
262
- collapsed: columnStack.collapsed !== false
318
+ collapsed: columnStack.collapsed !== false,
319
+ children: columnStack.fields
263
320
  };
264
321
  } else if (columnStack.stacks && columnStack.stacks.length) {
265
322
  hasStack = true;
@@ -289,6 +346,7 @@ ColumnStackPlugin.prototype.config = function (options) {
289
346
  stackId = "";
290
347
  var spreading = false;
291
348
  var collapsed = true;
349
+ var activeColumn = "";
292
350
  if(typeof stackOpt === "string") {
293
351
  stackId = stackOpt;
294
352
  } else if(typeof stackOpt === "object") {
@@ -296,34 +354,31 @@ ColumnStackPlugin.prototype.config = function (options) {
296
354
  stackId = stackConfig["id"] || "";
297
355
  spreading = stackConfig["spreading"] === true; // WARNING: Only the first column in the stack has an effect
298
356
  collapsed = stackConfig["collapsed"] !== false;
357
+ activeColumn = stackConfig["activeColumn"] || "";
299
358
  }
300
359
  if(stackId) {
301
- if(!stacks[stackId]) {
302
- stacks[stackId] = {
303
- colRefs: [],
360
+ stackOpt = stacks[stackId];
361
+ if(!stackOpt ) {
362
+ stackOpt = stacks[stackId] = {
363
+ id: stackId,
304
364
  spreading: spreading, // Default is false (stacking mode)
305
365
  collapsed: collapsed // Default is true (columns are hidden)
306
366
  };
367
+ if(activeColumn) {
368
+ stackOpt.activeColumn = activeColumn;
369
+ }
307
370
  hasStack = true;
308
371
  }
309
- stacks[stackId].colRefs.push(i);
372
+ if(!stackOpt.children) {
373
+ stackOpt.children = [];
374
+ }
375
+ stackOpt.children.push(i);
310
376
  }
311
377
  }
312
378
  }
313
379
 
314
380
  if(this._initializedGrid) {
315
- if(hasStack){
316
- this.removeAllStacks(false); // No UI update
317
- for(stackId in stacks) {
318
- var config = stacks[stackId];
319
- if(!config.colRefs){
320
- this._transformStackConfig(config);
321
- }
322
- this.stackColumns(config.colRefs, stackId, config);
323
- }
324
- } else {
325
- this.removeAllStacks();
326
- }
381
+ this._applyUserConfigs(hasStack ? stacks : null);
327
382
  } else {
328
383
  this._pendingStacks = this._pendingStacks || stacks;
329
384
  }
@@ -340,15 +395,15 @@ ColumnStackPlugin.prototype.getConfigObject = function (gridOptions) {
340
395
 
341
396
  var columnOptions = obj["columns"];
342
397
 
343
- var stacks = this._stacks;
398
+ var stacks = this._groupDefs.getGroupMap();
344
399
  var stackOptions = [];
345
400
 
346
401
  for (var stackKey in stacks) {
347
402
  var stackOption = stacks[stackKey];
348
- var activeColIndex = this._getColumnIndex(stackOption.activeColumn);
403
+ var activeColIndex = this.getColumnIndex(stackOption.activeColumn);
349
404
 
350
405
  if(columnOptions && columnOptions.length){
351
- var memberIndices = this.getStackMemberIndices(stackOption.stackId);
406
+ var memberIndices = this.getStackMemberIndices(stackOption.id);
352
407
  for(var i = 0; i < memberIndices.length; i++){
353
408
  var colIndex = memberIndices[i];
354
409
  var colOption = columnOptions[colIndex];
@@ -359,7 +414,7 @@ ColumnStackPlugin.prototype.getConfigObject = function (gridOptions) {
359
414
  }
360
415
 
361
416
  var stackConfigObj = {
362
- id: stackOption.stackId
417
+ id: stackOption.id
363
418
  };
364
419
  var name = stackOption.name;
365
420
  var collapsed = stackOption.collapsed;
@@ -376,18 +431,17 @@ ColumnStackPlugin.prototype.getConfigObject = function (gridOptions) {
376
431
  }
377
432
 
378
433
  if (this._autoStacking) {
379
- var fields = this._idToFields[stackOption.stackId];
434
+ var fields = this._idToFields[stackOption.id];
380
435
  var activeColumnField = this._getField(activeColIndex);
381
436
 
382
437
  stackConfigObj.fields = fields;
383
438
  stackConfigObj.activeColumn = activeColumnField;
384
439
 
385
440
  } else {
386
- var children = this.getStackMemberIds(stackOption.stackId);
387
- var activeColumn = this.getColumnId(activeColIndex);
388
-
389
- stackConfigObj.children = children;
390
- stackConfigObj.activeColumn = activeColumn;
441
+ stackConfigObj.children = this.getStackMemberIds(stackOption.id);
442
+ if(stackOption.activeColumn) {
443
+ stackConfigObj.activeColumn = stackOption.activeColumn; // WARNING: Column Id or field
444
+ }
391
445
  }
392
446
  stackOptions.push(stackConfigObj);
393
447
  }
@@ -404,144 +458,87 @@ ColumnStackPlugin.prototype.getConfigObject = function (gridOptions) {
404
458
  */
405
459
  ColumnStackPlugin.prototype._afterInit = function () {
406
460
  if(this._pendingStacks) {
407
- this.removeAllStacks(false);
408
- for(var sid in this._pendingStacks) {
409
- var stackOpt = this._pendingStacks[sid];
410
- if(!stackOpt.colRefs){
411
- this._transformStackConfig(stackOpt);
412
- }
413
- this.stackColumns(stackOpt.colRefs, sid, stackOpt);
414
- }
461
+ this._applyUserConfigs(this._pendingStacks);
415
462
  this._pendingStacks = null;
416
463
  }
417
464
  // In case of lazy loading
418
465
  // DO something
419
466
  };
420
- /** WARNING: This method is slow
421
- * @private
422
- * @param {Object} stackRef
423
- * @param {number=} colCount
424
- * @return {number}
425
- */
426
- ColumnStackPlugin.prototype._getColumnIndex = function(stackRef, colCount) {
427
- if(stackRef) {
428
- if(colCount == null) {
429
- colCount = this.getColumnCount();
430
- }
431
- for(var i = 0; i < colCount; i++) {
432
- if(stackRef === this._getUniqueRef(i)) {
433
- return i;
434
- }
435
- }
436
- }
437
- return -1;
438
- };
439
- /** WARNING: This method is really slow
440
- * @private
441
- * @param {!Array.<Object>} stackRefs
442
- * @return {!Array.<number>}
443
- */
444
- ColumnStackPlugin.prototype._getColumnIndices = function(stackRefs) {
445
- var refCount = stackRefs ? stackRefs.length : 0;
446
- var ary = new Array(refCount);
447
- if(refCount) {
448
- var colCount = this.getColumnCount();
449
- for(var i = 0; i < refCount; i++) {
450
- ary[i] = this._getColumnIndex(stackRefs[i], colCount);
451
- }
452
- }
453
- return ary;
454
- };
455
- /** @private
456
- * @param {number} colIndex
457
- * @param {!Object} refObj
458
- */
459
- ColumnStackPlugin.prototype._setUniqueRef = function(colIndex, refObj) {
460
- this._newColumnData(colIndex)["stack"] = refObj;
461
- };
462
- /** @private
463
- * @param {number} colIndex
464
- * @return {Object}
465
- */
466
- ColumnStackPlugin.prototype._getUniqueRef = function(colIndex) {
467
- return this._getColumnOption(colIndex, "stack") || null;
468
- };
469
- /** @private
470
- * @return {!Array.<Object>}
471
- */
472
- ColumnStackPlugin.prototype._getUniqueRefs = function() {
473
- var colCount = this.getColumnCount();
474
- var ary = new Array(colCount);
475
- for(var c = 0; c < colCount; ++c) {
476
- ary[c] = this._getUniqueRef(c);
477
- }
478
- return ary;
479
- };
480
467
 
481
468
  /** @private
482
- * @param {number} colIndex
483
- * @param {Object} stackOptions
484
- */
485
- ColumnStackPlugin.prototype._setColumnStackOptions = function(colIndex, stackOptions) {
486
- var ref = this._getUniqueRef(colIndex);
487
- if(ref) {
488
- ref["stackOpt"] = stackOptions;
469
+ * @param {*} colRef
470
+ * @return {string} column id or field
471
+ */
472
+ ColumnStackPlugin.prototype._toIdOrField = function(colRef) {
473
+ if(typeof colRef !== "string") {
474
+ if(typeof colRef === "number") {
475
+ return this.getColumnId(colRef);
476
+ // return this._getField(colRef);
477
+ }
478
+ return "";
489
479
  }
480
+ return colRef || "";
490
481
  };
491
482
  /** @private
492
483
  * @param {Object} stackConfig
493
- * @return {Object} stack config object
494
484
  */
495
485
  ColumnStackPlugin.prototype._transformStackConfig = function(stackConfig) {
496
- stackConfig.colRefs = [];
497
486
  var children = stackConfig.children;
498
- var fields = stackConfig.fields;
499
- var field;
500
- if(children){
501
- var childLen = stackConfig.children.length;
502
- for(var j = 0; j < childLen; j++){
503
- var colIndex = this.getColumnIndex(children[j]);
504
- if(colIndex !== -1){
505
- field = this._getField(colIndex);
506
- if(field) {
507
- stackConfig.colRefs.push(field);
508
- }
487
+ var childCount = children ? children.length : 0;
488
+ if(childCount) { // Fields or ids
489
+ for(var i = 0; i < childCount; i++){
490
+ var childRef = this._toIdOrField(children[i]);
491
+ if(childRef) {
492
+ children[i] = childRef;
509
493
  }
510
494
  }
511
- } else if(fields) {
512
- stackConfig.colRefs = fields;
513
- }
514
- var activeColumn = stackConfig.activeColumn;
515
- if(activeColumn != null && !this._autoStacking){
516
- var activeColIndex = this.getColumnIndex(activeColumn);
517
- if(activeColIndex !== -1){
518
- field = this._getField(activeColIndex);
519
- if(field){
520
- stackConfig.activeColumn = field;
521
- }
495
+ } else if(stackConfig.fields) {
496
+ stackConfig.children = stackConfig.fields;
497
+ } else if(!stackConfig.children) {
498
+ stackConfig.children = [];
499
+ }
500
+ if(!this._autoStacking) { // TODO: This may not be necessary
501
+ var activeColumn = this._toIdOrField(stackConfig.activeColumn);
502
+ if(activeColumn) {
503
+ stackConfig.activeColumn = activeColumn;
522
504
  }
523
505
  }
524
- return stackConfig;
525
506
  };
526
507
  /** @private
527
- * @param {number} colIndex
528
- * @return {*}
529
- */
530
- ColumnStackPlugin.prototype._getColumnStackOptions = function(colIndex) {
531
- var refObj = this._getUniqueRef(colIndex);
532
- if(refObj){
533
- return refObj["stackOpt"] || null;
508
+ * @param {Object=} stacks
509
+ */
510
+ ColumnStackPlugin.prototype._applyUserConfigs = function(stacks) {
511
+ if(stacks) {
512
+ this.removeAllStacks(false); // No UI update
513
+ for(var stackId in stacks) {
514
+ var stack = stacks[stackId];
515
+ this._transformStackConfig(stack);
516
+ this.stackColumns(stack.children, stackId, stack);
517
+ }
518
+ } else {
519
+ this.removeAllStacks(); // with UI update
534
520
  }
535
- return null;
536
521
  };
522
+
537
523
  /** @private
538
524
  * @param {number} colIndex
525
+ * @return {Object}
539
526
  */
540
- ColumnStackPlugin.prototype._removeColumnStackOptions = function(colIndex) {
541
- var refObj = this._getUniqueRef(colIndex);
542
- if(refObj) {
543
- refObj["stackOpt"] = null;
527
+ ColumnStackPlugin.prototype._getColumnStackOptions = function(colIndex) {
528
+ if(colIndex >= 0) {
529
+ var colId = this.getColumnId(colIndex);
530
+ var stack = this._groupDefs.getParentGroup(colId);
531
+ if(stack) {
532
+ return stack;
533
+ }
534
+
535
+ var field = this._getField(colIndex);
536
+ stack = this._groupDefs.getParentGroup(field);
537
+ if(stack) {
538
+ return stack;
539
+ }
544
540
  }
541
+ return null;
545
542
  };
546
543
  /** @private
547
544
  * @return {!Array.<number>} Sorted column indices
@@ -562,12 +559,12 @@ ColumnStackPlugin.prototype._setColumnVisibility = function(colIndex, shown) {
562
559
  }
563
560
  };
564
561
  /** @private
565
- * @param {Array.<Object>} stackRefs
562
+ * @param {Array} colRefs Array of column index, id, or field
566
563
  */
567
- ColumnStackPlugin.prototype._moveStackedColumns = function (stackRefs) {
568
- var ary = this._getColumnIndices(stackRefs);
569
- if(ary.length > 1) {
570
- this.reorderColumns(ary, ary[0]);
564
+ ColumnStackPlugin.prototype._moveStackedColumns = function (colRefs) {
565
+ var colIndices = this.getColumnIndices(colRefs);
566
+ if(colIndices.length > 1) {
567
+ this.reorderColumns(colIndices, colIndices[0]);
571
568
  }
572
569
  };
573
570
  /** @private
@@ -587,12 +584,12 @@ ColumnStackPlugin.prototype._isIconAvailable = function() {
587
584
  };
588
585
  /** @private
589
586
  * @param {number} colIndex
590
- * @param {Object} colData
587
+ * @param {Object} stackOpt
591
588
  */
592
- ColumnStackPlugin.prototype._updateIcon = function(colIndex, colData) {
593
- var stackRefs = (colData) ? colData.stackRefs : null;
594
- var spreading = (colData) ? colData.spreading : false;
589
+ ColumnStackPlugin.prototype._updateIcon = function(colIndex, stackOpt) {
590
+ var spreading = (stackOpt) ? stackOpt.spreading : false;
595
591
  var gridCount = this._hosts.length;
592
+
596
593
  for(var g = 0; g < gridCount; ++g) {
597
594
  var host = this._hosts[g];
598
595
  var tSect = host.getSection("title");
@@ -601,18 +598,19 @@ ColumnStackPlugin.prototype._updateIcon = function(colIndex, colData) {
601
598
  if(!cell) { continue; }
602
599
 
603
600
  var stackIcon = cell._stackIcon;
604
- if(stackRefs) {
605
- if(!stackIcon && this.isActiveStackedColumn(colIndex)) {
601
+ if(stackOpt) {
602
+ var activeStackedColumn = this.isActiveStackedColumn(colIndex);
603
+ if(!stackIcon && activeStackedColumn) {
606
604
  if(spreading) {
607
605
  cell.addClass("grouping");
608
606
  if(this._expandIconName) {
609
- if(colData.collapsed) {
607
+ if(stackOpt.collapsed) {
610
608
  stackIcon = Icon.create(this._expandIconName);
611
609
  } else {
612
610
  stackIcon = Icon.create(this._collapseIconName);
613
611
  }
614
612
  } else {
615
- if(colData.collapsed) {
613
+ if(stackOpt.collapsed) {
616
614
  stackIcon = Dom.text(">");
617
615
  } else {
618
616
  stackIcon = Dom.text("<");
@@ -637,7 +635,7 @@ ColumnStackPlugin.prototype._updateIcon = function(colIndex, colData) {
637
635
  cell._stackIcon = stackIcon;
638
636
 
639
637
  this._dispatch("iconCreated", {"icon": stackIcon, "colIndex": colIndex, "grid": host, "cell": cell});
640
- } else if(stackIcon && this.isInactiveStackedColumn(colIndex)) {
638
+ } else if(stackIcon && !activeStackedColumn) {
641
639
  cell.removeFloatingIcon(cell._stackIcon);
642
640
  cell._stackIcon = null;
643
641
  }
@@ -663,21 +661,22 @@ ColumnStackPlugin.prototype._updateUI = function() {
663
661
  this._updating = true;
664
662
  var colCount = this._getColumnCount();
665
663
  var gridCount = this._hosts.length;
666
- var colData, spreading;
664
+ var stackOpt, spreading;
667
665
  for(var c = 0; c < colCount; ++c) {
668
- colData = this._getColumnStackOptions(c);
669
- spreading = (colData) ? colData.spreading : false;
666
+ stackOpt = this._getColumnStackOptions(c);
667
+ spreading = (stackOpt) ? stackOpt.spreading : false;
668
+ spreading = spreading === true;
670
669
  for(var g = 0; g < gridCount; ++g) {
671
670
  var host = this._hosts[g];
672
- host.enableColumnClass(c, "grouped", spreading === true);
671
+ host.enableColumnClass(c, "grouped", spreading);
673
672
  }
674
673
 
675
- this._updateIcon(c, colData);
676
- if(colData) {
677
- if(colData.spreading) {
678
- this._collapseMember(c, colData.collapsed);
674
+ this._updateIcon(c, stackOpt);
675
+ if(stackOpt) {
676
+ if(stackOpt.spreading) {
677
+ this._collapseMember(c, stackOpt.collapsed);
679
678
  } else {
680
- this._setColumnVisibility(c, this.isActiveStackedColumn(c)); // a little slow
679
+ this._setColumnVisibility(c, this._isActiveStackedColumn(c, stackOpt)); // a little slow
681
680
  }
682
681
  }
683
682
  }
@@ -688,8 +687,8 @@ ColumnStackPlugin.prototype._updateUI = function() {
688
687
  * @param {boolean} collapsed
689
688
  */
690
689
  ColumnStackPlugin.prototype._collapseMember = function(colIndex, collapsed) {
691
- var colData = this._getColumnStackOptions(colIndex);
692
- if(!colData) {
690
+ var stackOpt = this._getColumnStackOptions(colIndex);
691
+ if(!stackOpt) {
693
692
  return;
694
693
  }
695
694
 
@@ -703,9 +702,9 @@ ColumnStackPlugin.prototype._collapseMember = function(colIndex, collapsed) {
703
702
  var rowCount = tSect.getRowCount();
704
703
  var cell = tSect.getCell(colIndex, rowCount - 1, true);
705
704
  if(collapsed) {
706
- if(colData.collapsed !== collapsed || !colData._origWidth) { // Prevent from getting width while column is collapsed
707
- colData._origWidth = this._getColumnWidth(host, colIndex);
708
- colData._scalable = host.getColumnScalability(colIndex);
705
+ if(stackOpt.collapsed !== collapsed || !stackOpt._origWidth) { // Prevent from getting width while column is collapsed
706
+ stackOpt._origWidth = this._getColumnWidth(host, colIndex);
707
+ stackOpt._scalable = host.getColumnScalability(colIndex);
709
708
  }
710
709
 
711
710
  host.setColumnWidth(colIndex, tSect.getRowHeight(0), false);
@@ -718,8 +717,8 @@ ColumnStackPlugin.prototype._collapseMember = function(colIndex, collapsed) {
718
717
  }
719
718
  }
720
719
  } else {
721
- if(colData._origWidth != null) {
722
- host.setColumnWidth(colIndex, colData._origWidth, colData._scalable);
720
+ if(stackOpt._origWidth != null) {
721
+ host.setColumnWidth(colIndex, stackOpt._origWidth, stackOpt._scalable);
723
722
  }
724
723
 
725
724
  if(cell._stackIcon) {
@@ -734,8 +733,6 @@ ColumnStackPlugin.prototype._collapseMember = function(colIndex, collapsed) {
734
733
  host.setColumnVisibility(colIndex, !collapsed, 2);
735
734
  }
736
735
  }
737
-
738
- colData.collapsed = collapsed;
739
736
  };
740
737
  /** Get column width that is currently defined in the layout inside grid <br>
741
738
  * (not included the effect by this extension). <br>
@@ -758,12 +755,11 @@ ColumnStackPlugin.prototype._getColumnWidth = function(host, colIndex) {
758
755
  * @return {Array.<number>} Null if there is no column data
759
756
  */
760
757
  ColumnStackPlugin.prototype.getMemberIndices = function(colIndex) {
761
- var colData = this._getColumnStackOptions(colIndex);
762
- if(!colData) {
763
- return null;
758
+ var stackOpt = this._getColumnStackOptions(colIndex);
759
+ if(stackOpt) {
760
+ return this.getColumnIndices(stackOpt.children);
764
761
  }
765
-
766
- return this._getColumnIndices(colData.stackRefs);
762
+ return null;
767
763
  };
768
764
  /** @public
769
765
  * @param {number} colIndex
@@ -771,31 +767,34 @@ ColumnStackPlugin.prototype.getMemberIndices = function(colIndex) {
771
767
  * @return {Array.<number>} Null if it is non-grouped column
772
768
  */
773
769
  ColumnStackPlugin.prototype.collapseGroup = function(colIndex, collapsed) {
774
- var groupMembers = this.getMemberIndices(colIndex);
775
- if(!groupMembers) {
770
+ var stackOpt = this._getColumnStackOptions(colIndex);
771
+ if(!stackOpt) {
772
+ return null;
773
+ }
774
+ if(!stackOpt.spreading) {
776
775
  return null;
777
776
  }
778
777
 
779
778
  collapsed = collapsed !== false;
780
- for(var j = groupMembers.length; --j >= 0;) {
781
- this._collapseMember(groupMembers[j], collapsed);
779
+ if(stackOpt.collapsed !== collapsed) {
780
+ var groupMembers = this.getColumnIndices(stackOpt.children);
781
+ if(groupMembers) {
782
+ for(var j = groupMembers.length; --j >= 0;) {
783
+ this._collapseMember(groupMembers[j], collapsed);
784
+ }
785
+ stackOpt.collapsed = collapsed;
786
+
787
+ return groupMembers;
788
+ }
782
789
  }
783
- return groupMembers;
790
+ return null;
784
791
  };
785
792
  /** @public
786
793
  * @param {number} colIndex
787
794
  * @return {Array.<number>} Null if it is non-grouped column
788
795
  */
789
796
  ColumnStackPlugin.prototype.expandGroup = function(colIndex) {
790
- var groupMembers = this.getMemberIndices(colIndex);
791
- if(!groupMembers) {
792
- return null;
793
- }
794
-
795
- for(var j = groupMembers.length; --j >= 0;) {
796
- this._collapseMember(groupMembers[j], false);
797
- }
798
- return groupMembers;
797
+ return this.collapseGroup(colIndex, false);
799
798
  };
800
799
  /** Intended for determining if the selected columns can be stacked from the context menu
801
800
  * @public
@@ -806,11 +805,9 @@ ColumnStackPlugin.prototype.isColumnStackable = function(colIndices) {
806
805
  if(!colIndices) {
807
806
  return false;
808
807
  }
809
-
810
808
  var len = colIndices.length;
811
809
  for(var i = 0; i < len; ++i) {
812
- var colIndex = colIndices[i];
813
- if(this.isColumnInCollection(colIndex)) {
810
+ if(this._getColumnStackOptions(colIndices[i])) {
814
811
  return false;
815
812
  }
816
813
  }
@@ -827,36 +824,46 @@ ColumnStackPlugin.prototype.isColumnStacked = function(colIndices) {
827
824
  }
828
825
  var len = colIndices.length;
829
826
  for(var i = 0; i < len; ++i) {
830
- var colData = this._getColumnStackOptions(colIndices[i]);
831
- if(colData && colData.stackRefs) {
827
+ if(this._getColumnStackOptions(colIndices[i])) {
832
828
  return true;
833
829
  }
834
830
  }
835
831
  return false;
836
832
  };
837
- /** This is used for removing some column state while the column is inactive
833
+ /** This method is deprecated in favor of isColumnActive method. This method checks if the given column is inactive or doesn't have the icon.
838
834
  * @public
839
835
  * @param {number} colIndex
840
836
  * @return {boolean} Return true if the columns is grouped and inactive
837
+ * @see {@link ColumnStackPlugin#isColumnActive}
841
838
  */
842
839
  ColumnStackPlugin.prototype.isInactiveStackedColumn = function(colIndex) {
843
840
  var stackOpt = this._getColumnStackOptions(colIndex);
844
- if(stackOpt && stackOpt.stackRefs) {
845
- return colIndex !== this._getColumnIndex(stackOpt.activeColumn);
841
+ if(stackOpt) {
842
+ return !this._isActiveStackedColumn(colIndex, stackOpt);
846
843
  }
847
844
  return false;
848
845
  };
849
- /** Check if the given column is active (visible) in the stack
846
+ /** This method is deprecated in favor of isColumnActive method. This method checks if the given column is active and has the icon.
850
847
  * @public
851
848
  * @param {number} colIndex
852
849
  * @return {boolean}
850
+ * @see {@link ColumnStackPlugin#isColumnActive}
853
851
  */
854
852
  ColumnStackPlugin.prototype.isActiveStackedColumn = function(colIndex) {
855
- var stackOpt = this._getColumnStackOptions(colIndex);
856
- if(stackOpt && stackOpt.stackRefs) {
857
- return colIndex === this._getColumnIndex(stackOpt.activeColumn);
853
+ return this._isActiveStackedColumn(colIndex, this._getColumnStackOptions(colIndex));
854
+ };
855
+ /** @private
856
+ * @param {number} colIndex
857
+ * @param {Object} stackOpt
858
+ * @return {boolean}
859
+ */
860
+ ColumnStackPlugin.prototype._isActiveStackedColumn = function(colIndex, stackOpt) {
861
+ if(stackOpt) {
862
+ _resolveActiveColumn(stackOpt);
863
+
864
+ return colIndex === this.getColumnIndex(stackOpt.activeColumn);
858
865
  }
859
- return true;
866
+ return false;
860
867
  };
861
868
 
862
869
  /** @public
@@ -886,11 +893,7 @@ ColumnStackPlugin.prototype.isCollapsingMode = function(colIndex) {
886
893
  * @return {boolean}
887
894
  */
888
895
  ColumnStackPlugin.prototype.isColumnInCollection = function(colIndex) {
889
- var stackOpt = this._getColumnStackOptions(colIndex);
890
- if(stackOpt) {
891
- return true;
892
- }
893
- return false;
896
+ return this._getColumnStackOptions(colIndex) ? true : false;
894
897
  };
895
898
  /** Alias of isColumnInCollection
896
899
  * @public
@@ -925,7 +928,7 @@ ColumnStackPlugin.prototype.isColumnActive = function(colIndex) {
925
928
  return false;
926
929
  }
927
930
  } else {
928
- return colIndex === this._getColumnIndex(stackOpt.activeColumn);
931
+ return colIndex === this.getColumnIndex(stackOpt.activeColumn);
929
932
  }
930
933
  }
931
934
  return false;
@@ -936,8 +939,8 @@ ColumnStackPlugin.prototype.isColumnActive = function(colIndex) {
936
939
  * @return {string}
937
940
  */
938
941
  ColumnStackPlugin.prototype.getStackId = function(colIndex) {
939
- var colData = this._getColumnStackOptions(colIndex);
940
- return colData ? colData.stackId : "";
942
+ var stackOpt = this._getColumnStackOptions(colIndex);
943
+ return stackOpt ? stackOpt.id : "";
941
944
  };
942
945
  /** @public
943
946
  * @param {Array.<number|string>=} colRefs Names of fields or column indices. If not specified, selected columns will be used.
@@ -946,109 +949,90 @@ ColumnStackPlugin.prototype.getStackId = function(colIndex) {
946
949
  * @return {boolean} Return true if all of the given columns is stacked together
947
950
  */
948
951
  ColumnStackPlugin.prototype.stackColumns = function(colRefs, stackId, options) {
949
- var fields = [];
950
- var i, colIndex, sid;
952
+ var i, sid;
951
953
  options = options || {};
952
954
 
953
955
  if(stackId) {
954
- if(this._stacks[stackId]) {
956
+ if(this._groupDefs.getGroup(stackId)) {
955
957
  return false; // Cannot store the same stack Id
956
958
  }
957
959
  sid = stackId;
958
960
  } else {
959
- sid = this._generateStackId();
960
- }
961
-
962
- // If grid is not initialize, add setting to pending stacks
963
- if(!this._initializedGrid) {
964
- var pendingStacks = this._pendingStacks || {};
965
- pendingStacks[sid] = {
966
- colRefs: colRefs,
967
- spreading: false,
968
- collapsed: false,
969
- activeColumn: options.activeColumn || colRefs[0]
970
- };
971
- this._pendingStacks = pendingStacks;
972
- return false;
961
+ sid = stackId = this._generateStackId();
973
962
  }
974
963
 
975
964
  if(!colRefs) {
976
965
  colRefs = this._getSelectedColumns();
977
966
  }
978
967
 
979
- if(colRefs.length) {
980
- if(typeof colRefs[0] === "string") {
981
- fields = colRefs.slice();
982
- colRefs = this.getColumnIndices(colRefs);
983
- } else {
984
- colRefs.sort(function(a, b) { return a - b; }); // Only sort in the case of column index stack
985
- for(i = 0; i < colRefs.length; i++){
986
- var field = this._getField(colRefs[i]);
987
- fields.push(field);
988
- }
968
+ var refCount = colRefs ? colRefs.length : 0;
969
+ if(refCount < 2) {
970
+ return false; // Only two or more columns can be stacked
971
+ }
972
+
973
+ // Clone user data
974
+ var isSpreading = options.spreading === true;
975
+ var isCollapsed = options.collapsed !== false;
976
+ var activeColumn = options.activeColumn || ""; // field or id
977
+ var stack = {};
978
+ stack.id = sid;
979
+ stack.name = options.name || "";
980
+ stack.spreading = isSpreading;
981
+ stack.collapsed = isCollapsed;
982
+ stack.activeColumn = activeColumn; // field or id
983
+
984
+ // If grid is not initialize, add setting to pending stacks
985
+ if(!this._initializedGrid) {
986
+ var pendingStacks = this._pendingStacks;
987
+ if(!pendingStacks) {
988
+ pendingStacks = this._pendingStacks = {};
989
989
  }
990
+ stack.children = colRefs;
991
+ pendingStacks[sid] = stack;
992
+ return false;
990
993
  }
991
994
 
992
- // Save stack fields for
995
+ var children = colRefs.map(this._toIdOrField);
996
+ var colIndices = this.getColumnIndices(colRefs); // WARNING: Invalid columns are filtered out
997
+ var validCount = colIndices.length;
998
+
999
+ // Save stack fields for autoStacking
993
1000
  if(this._autoStacking){
994
1001
  if(!this._idToFields) {
995
1002
  this._idToFields = {};
996
1003
  }
997
- this._idToFields[sid] = fields;
1004
+ this._idToFields[sid] = children;
998
1005
  }
999
1006
 
1000
- var len = colRefs.length;
1001
- if(len <= 1) {
1002
- return false; // Only two or more columns can be stacked
1003
- }
1004
- if(!this.isColumnStackable(colRefs)) {
1007
+ // Prevent columns already in a stack from moving out to another stack
1008
+ if(!this.isColumnStackable(colIndices)) {
1005
1009
  return false;
1006
1010
  }
1007
1011
 
1008
- var activeIndex = colRefs[0];
1009
- if(options.activeColumn != null) {
1010
- var idx = this.getColumnIndex(options.activeColumn);
1011
- if(idx > -1 && colRefs.indexOf(idx) > -1) {
1012
- activeIndex = idx;
1013
- }
1014
- }
1015
-
1016
- // Collecting data
1017
- var stack = {};
1018
- stack.stackId = sid;
1019
- stack.name = options.name || "";
1020
- stack.spreading = options.spreading === true;
1021
- if(stack.spreading) {
1022
- activeIndex = colRefs[len - 1];
1023
- }
1024
- stack.collapsed = options.collapsed !== false;
1025
- stack.stackRefs = new Array(len);
1026
- for(i = 0; i < len; ++i) {
1027
- colIndex = colRefs[i];
1028
- this._setColumnStackOptions(colIndex, stack);
1029
- stack.stackRefs[i] = this._getUniqueRef(colIndex);
1030
-
1031
- if(colIndex == activeIndex){
1032
- stack.activeColumn = stack.stackRefs[i];
1033
- }
1012
+ stack.children = children;
1013
+ _resolveActiveColumn(stack); // stack.activeColumn may be modified here
1014
+ var activeIndex = this.getColumnIndex(stack.activeColumn);
1034
1015
 
1035
- // Prevent from flashing in stack mode
1036
- if(colIndex !== activeIndex && stack.collapsed !== false) {
1037
- this._setColumnVisibility(colIndex, false);
1016
+ // Hide the column to prevent it from flashing in stack mode
1017
+ if(!isSpreading || !isCollapsed) {
1018
+ for(i = 0; i < validCount; ++i) {
1019
+ var colIndex = colIndices[i];
1020
+ if(colIndex !== activeIndex) {
1021
+ this._setColumnVisibility(colIndex, false); // Hide a column
1022
+ }
1038
1023
  }
1039
1024
  }
1040
1025
 
1041
1026
  // Make sure that all columns stay packed together
1042
- this._moveStackedColumns(stack.stackRefs);
1027
+ this.reorderColumns(colIndices, colIndices[0]);
1043
1028
 
1044
- if(stack.spreading) {
1045
- stack.activeColumn = stack.stackRefs[stack.stackRefs.length - 1]; // Right most column is the active column
1046
- } else {
1029
+ // Update column selection
1030
+ if(!isSpreading) {
1047
1031
  var csp = this._getPlugin("ColumnSelectionPlugin");
1048
1032
  if(csp && csp.isEnabled()){
1049
1033
  var stackSelection = false;
1050
- for(i = 0; i < len; ++i){
1051
- colIndex = colRefs[i];
1034
+ for(i = 0; i < validCount; ++i){
1035
+ colIndex = colIndices[i];
1052
1036
  if(colIndex === activeIndex){
1053
1037
  continue;
1054
1038
  }
@@ -1059,12 +1043,14 @@ ColumnStackPlugin.prototype.stackColumns = function(colRefs, stackId, options) {
1059
1043
  }
1060
1044
  if(stackSelection) {
1061
1045
  csp.selectSingleColumn(activeIndex);
1062
- csp.dispatchSelectionChanged();
1046
+ if(csp.dispatchSelectionChanged) {
1047
+ csp.dispatchSelectionChanged();
1048
+ }
1063
1049
  }
1064
1050
  }
1065
1051
  }
1066
1052
 
1067
- this._stacks[sid] = stack;
1053
+ this._groupDefs.setGroup(sid, stack);
1068
1054
 
1069
1055
  var cfp = this._getPlugin("ColumnFilterPlugin");
1070
1056
  if(cfp) {
@@ -1084,7 +1070,6 @@ ColumnStackPlugin.prototype.stackColumns = function(colRefs, stackId, options) {
1084
1070
  * @return {boolean} If the stack has been updated, return true.
1085
1071
  */
1086
1072
  ColumnStackPlugin.prototype.setStack = function(colRefs, activeColRef) {
1087
-
1088
1073
  var sid = "_uniqueStack"; // WARNING : This hardcode for assign uniqe stacking id
1089
1074
 
1090
1075
  this.removeAllStacks();
@@ -1116,39 +1101,19 @@ ColumnStackPlugin.prototype.unstackColumns = function(colIndices) {
1116
1101
  colIndex = colIndices[i];
1117
1102
  if(colIndex < colCount) {
1118
1103
  var stackOpt = this._getColumnStackOptions(colIndex);
1119
- if(!stackOpt) {
1120
- continue; // Invalid index
1121
- }
1122
- if(stackOpt.collapsed === true && stackOpt.spreading === true) {
1123
- this.expandGroup(colIndex);
1104
+ if(stackOpt) {
1105
+ stacks[stackOpt.id] = 1; // Exclude duplicate stacks
1124
1106
  }
1125
- stacks[stackOpt.stackId] = 1; // Exclude duplicate stacks
1126
1107
  }
1127
1108
  }
1128
1109
 
1129
1110
  var dirty = false;
1130
-
1131
1111
  for(var sid in stacks) {
1132
- var stack = this._stacks[sid];
1133
- if(!stack) {
1134
- continue; // Invalid stackId
1135
- }
1136
1112
  dirty = true;
1137
-
1138
- var stackRefs = stack.stackRefs;
1139
- len = stackRefs.length;
1140
-
1141
- for(i = 0; i < len; ++i) {
1142
- var stackRef = stackRefs[i];
1143
- colIndex = this._getColumnIndex(stackRef);
1144
- this._removeColumnStackOptions(colIndex);
1145
- this._setColumnVisibility(colIndex, true);
1146
- }
1147
-
1113
+ this._removeStack(sid);
1148
1114
  if(this._idToFields) {
1149
1115
  delete this._idToFields[sid]; // TODO: Clear the map whenever it no longer has a member
1150
1116
  }
1151
- delete this._stacks[sid]; // Remove all reference to the stack
1152
1117
  }
1153
1118
  if(dirty) {
1154
1119
  var cfp = this._getPlugin("ColumnFilterPlugin");
@@ -1165,23 +1130,22 @@ ColumnStackPlugin.prototype.unstackColumns = function(colIndices) {
1165
1130
  * @return {boolean} Returns true if there is any change
1166
1131
  */
1167
1132
  ColumnStackPlugin.prototype._removeStack = function(stackId) {
1168
- var stack = this._stacks[stackId];
1133
+ var stack = this._groupDefs.getGroup(stackId);
1169
1134
  if(!stack) {
1170
1135
  return false;
1171
1136
  }
1172
1137
 
1173
- var colIndices = this._getColumnIndices(stack.stackRefs);
1138
+ var colIndices = this.getColumnIndices(stack.children);
1174
1139
  var len = colIndices.length;
1175
1140
  for(var i = 0; i < len; ++i) {
1176
- var colIndex = colIndices[i];
1177
- if(colIndex >= 0) {
1178
- if(stack.collapsed && stack.spreading) {
1179
- this.collapseGroup(colIndex, false);
1180
- }
1181
- this._removeColumnStackOptions(colIndex);
1182
- this._setColumnVisibility(colIndex, true);
1141
+ var colIndex = colIndices[i]; // colIndex is guaranteed to be positive (verified)
1142
+ if(stack.spreading && stack.collapsed) {
1143
+ this._collapseMember(colIndex, false);
1183
1144
  }
1145
+
1146
+ this._setColumnVisibility(colIndex, true);
1184
1147
  }
1148
+ this._groupDefs.removeGroup(stackId);
1185
1149
  return true;
1186
1150
  };
1187
1151
  /** @public
@@ -1205,19 +1169,20 @@ ColumnStackPlugin.prototype.removeStack = function(stackId) {
1205
1169
  * @return {boolean} true if at least one stacking removed
1206
1170
  */
1207
1171
  ColumnStackPlugin.prototype.removeAllStacks = function(enableUpdateUI) {
1208
- var dirty = false;
1209
- for(var stackId in this._stacks) {
1210
- dirty = true;
1211
- this._removeStack(stackId);
1172
+ var groupIds = this._groupDefs.getGroupIds();
1173
+ var groupCount = groupIds.length;
1174
+ for(var i = 0; i < groupCount; ++i) {
1175
+ this._removeStack(groupIds[i]);
1212
1176
  }
1213
- if(dirty) {
1177
+ if(groupCount) {
1214
1178
  this._idToFields = null;
1215
- this._stacks = {};
1179
+ this._groupDefs.removeAllGroups(); // TODO: May not necessary
1180
+
1216
1181
  if(enableUpdateUI !== false) {
1217
1182
  this._updateUI(); // asyncronous
1218
1183
  }
1219
1184
  }
1220
- return dirty;
1185
+ return groupCount ? true : false;
1221
1186
  };
1222
1187
  /** @public
1223
1188
  * @param {number|Event} colRef
@@ -1236,26 +1201,26 @@ ColumnStackPlugin.prototype.swapColumn = function(colRef, swappingIndex) {
1236
1201
  }
1237
1202
  }
1238
1203
 
1239
- var colData = this._getColumnStackOptions(colIndex);
1240
- if(!colData) {
1204
+ var stackOpt = this._getColumnStackOptions(colIndex);
1205
+ if(!stackOpt) {
1241
1206
  return false; // Invalid column index
1242
1207
  }
1243
- var stackRefs = colData.stackRefs;
1244
- if(!stackRefs) {
1208
+ var children = stackOpt.children;
1209
+ if(!children) {
1245
1210
  return false; // Invalid column type
1246
1211
  }
1247
- var newActiveColumn = stackRefs[swappingIndex];
1212
+ var newActiveColumn = children[swappingIndex];
1248
1213
  if(!newActiveColumn) {
1249
1214
  return false; // Invalid selected index
1250
1215
  }
1251
1216
 
1252
- if(newActiveColumn === colData.activeColumn) {
1217
+ if(newActiveColumn === stackOpt.activeColumn) {
1253
1218
  return false; // The given index is already active stacked column
1254
1219
  }
1255
1220
 
1256
- var prevActiveColumnIndex = this._getColumnIndex(colData.activeColumn);
1257
- var newActiveColumnIndex = this._getColumnIndex(newActiveColumn);
1258
- colData.activeColumn = newActiveColumn; // Change active column
1221
+ var prevActiveColumnIndex = this.getColumnIndex(stackOpt.activeColumn);
1222
+ var newActiveColumnIndex = this.getColumnIndex(newActiveColumn);
1223
+ stackOpt.activeColumn = newActiveColumn; // Change active column
1259
1224
 
1260
1225
  this._setColumnVisibility(newActiveColumnIndex, true);
1261
1226
  this._setColumnVisibility(prevActiveColumnIndex, false); // Hide current active column
@@ -1263,16 +1228,18 @@ ColumnStackPlugin.prototype.swapColumn = function(colRef, swappingIndex) {
1263
1228
  var csp = this._getPlugin("ColumnSelectionPlugin");
1264
1229
  if(csp && csp.isEnabled()) {
1265
1230
  csp.selectSingleColumn(newActiveColumnIndex);
1266
- csp.dispatchSelectionChanged();
1231
+ if(csp.dispatchSelectionChanged) {
1232
+ csp.dispatchSelectionChanged();
1233
+ }
1267
1234
  }
1268
1235
  var cfp = this._getPlugin("ColumnFilterPlugin");
1269
1236
  if(cfp) {
1270
1237
  cfp["refresh"]();
1271
1238
  }
1272
1239
 
1273
- colData = this._getColumnStackOptions(prevActiveColumnIndex);
1274
- this._updateIcon(prevActiveColumnIndex, colData);
1275
- this._updateIcon(newActiveColumnIndex, colData);
1240
+ stackOpt = this._getColumnStackOptions(prevActiveColumnIndex);
1241
+ this._updateIcon(prevActiveColumnIndex, stackOpt);
1242
+ this._updateIcon(newActiveColumnIndex, stackOpt);
1276
1243
 
1277
1244
  return true;
1278
1245
  };
@@ -1290,151 +1257,83 @@ ColumnStackPlugin.prototype._onPreSectionRender = function (e) {
1290
1257
  * @param {Object} e
1291
1258
  */
1292
1259
  ColumnStackPlugin.prototype._onColumnRemoved = function (e) {
1293
- var colData = /** @type{Object} */(e.columnData);
1294
- if(!colData) { return; }
1295
-
1296
- var stackRef = colData["stack"];
1297
- if(!stackRef) { return; }
1260
+ if(this._inResetting) {
1261
+ return;
1262
+ }
1298
1263
 
1299
- var stackOpt = stackRef["stackOpt"];
1300
- if(!stackOpt) { return; }
1264
+ var colRef = "";
1265
+ var colId = e.colId;
1266
+ var stackOpt = this._groupDefs.getParentGroup(colId);
1301
1267
 
1302
- // update members
1303
- if(stackOpt.stackRefs) {
1304
- var index = stackOpt.stackRefs.indexOf(stackRef);
1305
- if(index > -1) {
1306
- stackOpt.stackRefs.splice(index, 1);
1307
- if(stackOpt.stackRefs.length < 2) {
1308
- // delete group
1309
- var colIndex = this._getColumnIndex(stackOpt.stackRefs[0]);
1310
- if(colIndex > -1) {
1311
- this.unstackColumns([colIndex]);
1312
- } else {
1313
- // All stacked columns are removed
1314
- delete this._stacks[stackOpt.stackId];
1315
- }
1316
- } else {
1317
- this._updateActiveColumn(stackOpt);
1318
- }
1268
+ if(stackOpt) {
1269
+ colRef = colId;
1270
+ } else {
1271
+ var colData = /** @type{Object} */(e.columnData);
1272
+ var field = colData ? colData.field : "";
1273
+ stackOpt = this._groupDefs.getParentGroup(fields);
1274
+ if(stackOpt) {
1275
+ colRef = field;
1276
+ } else {
1277
+ return;
1319
1278
  }
1320
1279
  }
1321
-
1322
- colData["stack"] = null;
1280
+ // update members
1281
+ var children = stackOpt.children;
1282
+ if(children.length <= 2) {
1283
+ this._removeStack(stackOpt.id);
1284
+ this._updateUI();
1285
+ } else {
1286
+ this._groupDefs.removeGroupChild(stackOpt.id, colRef);
1287
+ this._updateActiveColumn(stackOpt); // This may trigger _updateUI
1288
+ }
1323
1289
  };
1324
-
1325
1290
  /** @private
1326
1291
  * @param {Object} e
1327
1292
  */
1328
1293
  ColumnStackPlugin.prototype._onColumnMoved = function (e) {
1329
- if(this._stacking) { // during the stacking, there is no need to recalculate stacks
1330
- return;
1294
+ if(this._inReordering || this._inResetting) {
1295
+ return; // during the reordering or resetting, there is no need to recalculate stacks
1331
1296
  }
1332
1297
 
1333
1298
  var toIndex = e.toColIndex;
1334
- var stackOpt = this._getColumnStackOptions(toIndex);
1335
-
1336
- // column swaping
1337
- if(stackOpt && !stackOpt.spreading) {
1338
- return;
1299
+ var colRef = "";
1300
+ var colId = this.getColumnId(toIndex);
1301
+ var stackOpt = this._groupDefs.getParentGroup(colId);
1302
+ if(stackOpt) {
1303
+ colRef = colId;
1304
+ } else {
1305
+ var field = this._getField(toIndex);
1306
+ stackOpt = this._groupDefs.getParentGroup(field);
1307
+ if(stackOpt) {
1308
+ colRef = field;
1309
+ }
1339
1310
  }
1340
1311
 
1341
- var stackRef = this._getUniqueRef(toIndex);
1342
-
1343
1312
  var leftStackOpt = this._getColumnStackOptions(toIndex - 1);
1344
1313
  var rightStackOpt = this._getColumnStackOptions(toIndex + 1);
1345
- var index, leftColStackRef;
1346
- // move inside
1347
- if(stackOpt) {
1348
- if(stackOpt === leftStackOpt) {
1349
- leftStackOpt.stackRefs.splice(leftStackOpt.stackRefs.indexOf(stackRef), 1);
1350
- leftColStackRef = this._getUniqueRef(toIndex - 1);
1351
- index = leftStackOpt.stackRefs.indexOf(leftColStackRef);
1352
- if(index > -1) {
1353
- leftStackOpt.stackRefs.splice(index + 1, 0, stackRef);
1354
- if(leftStackOpt.spreading) {
1355
- leftStackOpt.activeColumn = leftStackOpt.stackRefs[leftStackOpt.stackRefs.length - 1];
1356
- } else {
1357
- leftStackOpt.activeColumn = leftStackOpt.stackRefs[0];
1358
- }
1359
- }
1360
- this._updateUI();
1361
- return;
1362
- } else if(stackOpt === rightStackOpt) {
1363
- rightStackOpt.stackRefs.splice(rightStackOpt.stackRefs.indexOf(stackRef), 1);
1364
- var rightColStackRef = this._getUniqueRef(toIndex + 1);
1365
- index = rightStackOpt.stackRefs.indexOf(rightColStackRef);
1366
- if(index > -1) {
1367
- rightStackOpt.stackRefs.splice(index, 0, stackRef);
1368
- if(rightStackOpt.spreading) {
1369
- rightStackOpt.activeColumn = rightStackOpt.stackRefs[rightStackOpt.stackRefs.length - 1];
1370
- } else {
1371
- rightStackOpt.activeColumn = rightStackOpt.stackRefs[0];
1372
- }
1373
- }
1374
- this._updateUI();
1375
- return;
1376
- }
1377
- }
1378
1314
 
1315
+ var stackChange = true;
1379
1316
  var dirty = false;
1380
-
1381
- var fromIndex = e.fromColIndex;
1382
- var rIndex = fromIndex + 1;
1383
- if(toIndex > fromIndex) {
1384
- // move right
1385
- rIndex = fromIndex;
1386
- }
1387
- var lIndex = rIndex - 1;
1388
-
1389
- // move out checking
1390
- leftStackOpt = this._getColumnStackOptions(lIndex);
1391
- if(leftStackOpt && leftStackOpt.spreading) {
1392
- index = leftStackOpt.stackRefs.indexOf(stackRef);
1393
- if(index > -1) {
1394
- leftStackOpt.stackRefs.splice(index, 1);
1395
- if(leftStackOpt.spreading) {
1396
- leftStackOpt.activeColumn = leftStackOpt.stackRefs[leftStackOpt.stackRefs.length - 1];
1397
- } else {
1398
- leftStackOpt.activeColumn = leftStackOpt.stackRefs[0];
1399
- }
1400
- this._setColumnStackOptions(toIndex, null);
1401
- dirty = true;
1402
- }
1403
- }
1404
-
1405
- rightStackOpt = this._getColumnStackOptions(rIndex);
1406
- if(rightStackOpt && rightStackOpt.spreading) {
1407
- index = rightStackOpt.stackRefs.indexOf(stackRef);
1408
- if(index > -1) {
1409
- rightStackOpt.stackRefs.splice(index, 1);
1410
- if(rightStackOpt.spreading) {
1411
- rightStackOpt.activeColumn = rightStackOpt.stackRefs[rightStackOpt.stackRefs.length - 1];
1317
+ if(stackOpt) {
1318
+ // The move was within the same stack
1319
+ if(stackOpt === leftStackOpt || stackOpt === rightStackOpt) {
1320
+ this._repositionMembers(stackOpt);
1321
+ stackChange = false;
1322
+ } else { // The move was out from its own stack
1323
+ if(stackOpt.children.length <= 2) {
1324
+ this._removeStack(stackOpt.id);
1412
1325
  } else {
1413
- rightStackOpt.activeColumn = rightStackOpt.stackRefs[0];
1326
+ this._removeRefFromStack(stackOpt, colRef, toIndex);
1414
1327
  }
1415
- this._setColumnStackOptions(toIndex, null);
1416
- dirty = true;
1417
1328
  }
1329
+ dirty = true;
1418
1330
  }
1419
1331
 
1420
- // move in checking
1421
- rIndex = toIndex + 1;
1422
- lIndex = rIndex - 2;
1423
- leftStackOpt = this._getColumnStackOptions(lIndex);
1424
- rightStackOpt = this._getColumnStackOptions(rIndex);
1425
- if(leftStackOpt && leftStackOpt.spreading &&
1426
- (leftStackOpt === rightStackOpt)) {
1427
- leftColStackRef = this._getUniqueRef(toIndex - 1);
1428
- index = leftStackOpt.stackRefs.indexOf(leftColStackRef);
1429
- if(index > -1) {
1430
- leftStackOpt.stackRefs.splice(index + 1, 0, stackRef);
1431
- if(leftStackOpt.spreading) {
1432
- leftStackOpt.activeColumn = leftStackOpt.stackRefs[leftStackOpt.stackRefs.length - 1];
1433
- } else {
1434
- leftStackOpt.activeColumn = leftStackOpt.stackRefs[0];
1332
+ if(stackChange) {
1333
+ if(this._isWithinStack(toIndex)) {
1334
+ if(this._addRefToStack(leftStackOpt, toIndex)) {
1335
+ dirty = true;
1435
1336
  }
1436
- this._setColumnStackOptions(toIndex, leftStackOpt);
1437
- dirty = true;
1438
1337
  }
1439
1338
  }
1440
1339
 
@@ -1442,30 +1341,20 @@ ColumnStackPlugin.prototype._onColumnMoved = function (e) {
1442
1341
  this._updateUI();
1443
1342
  }
1444
1343
  };
1445
-
1446
1344
  /** @private
1447
1345
  * @param {Object} e
1448
1346
  */
1449
1347
  ColumnStackPlugin.prototype._onColumnAdded = function (e) {
1450
1348
  var colIndex = e.colIndex;
1451
- this._setUniqueRef(colIndex, {});
1452
1349
 
1453
1350
  if (this._autoStacking) {
1454
1351
  if(this._idToFields) {
1455
1352
  this._requestStackingByFields();
1456
1353
  }
1457
- } else { // TODO: This logic is too complicated
1458
- // add to group
1459
- var leftStackOpt = this._getColumnStackOptions(colIndex - 1);
1460
- var rightStackOpt = this._getColumnStackOptions(colIndex + 1);
1461
- if(leftStackOpt && (leftStackOpt === rightStackOpt)) {
1462
- var leftColStackRef = this._getUniqueRef(colIndex - 1);
1463
- var stackRef = this._getUniqueRef(colIndex);
1464
- var index = leftStackOpt.stackRefs.indexOf(leftColStackRef);
1465
- if(index > -1) { // TODO: Reuse existing logic instead of modifying states like this
1466
- leftStackOpt.stackRefs.splice(index + 1, 0, stackRef);
1467
- leftStackOpt.activeColumn = leftStackOpt.stackRefs[leftStackOpt.stackRefs.length - 1];
1468
- this._setColumnStackOptions(colIndex, leftStackOpt);
1354
+ } else if(!this._inResetting) {
1355
+ var stackOpt = this._isWithinStack(colIndex);
1356
+ if(stackOpt) {
1357
+ if(this._addRefToStack(stackOpt, colIndex)) {
1469
1358
  this._updateUI();
1470
1359
  }
1471
1360
  }
@@ -1474,6 +1363,113 @@ ColumnStackPlugin.prototype._onColumnAdded = function (e) {
1474
1363
  /** @private
1475
1364
  * @param {Object} e
1476
1365
  */
1366
+ ColumnStackPlugin.prototype._onBeforeBatchOperation = function (e) {
1367
+ if(e.batchType === "reset") {
1368
+ this._inResetting = true;
1369
+ } else if(e.batchType === "move") {
1370
+ this._inReordering = true;
1371
+ }
1372
+ };
1373
+ /** @private
1374
+ * @param {Object} e
1375
+ */
1376
+ ColumnStackPlugin.prototype._onAfterBatchOperation = function (e) {
1377
+ if(e.batchType === "reset") {
1378
+ this._inResetting = false;
1379
+ // TODO: Re-packing each stack
1380
+ var groups = this._groupDefs.getGroups();
1381
+ var groupCount = groups.length;
1382
+ for(var i = 0; i < groupCount; ++i) {
1383
+ this._updateActiveColumn(groups[i]);
1384
+ }
1385
+ } else if(e.batchType === "move") {
1386
+ this._inReordering = false;
1387
+ }
1388
+ };
1389
+
1390
+ /** @private
1391
+ * @param {number} colIndex
1392
+ * @return {Object} stackOption
1393
+ */
1394
+ ColumnStackPlugin.prototype._isWithinStack = function (colIndex) {
1395
+ var leftStackOpt = this._getColumnStackOptions(colIndex - 1);
1396
+ if(leftStackOpt) {
1397
+ var rightStackOpt = this._getColumnStackOptions(colIndex + 1);
1398
+ if(leftStackOpt === rightStackOpt) {
1399
+ return leftStackOpt;
1400
+ }
1401
+ }
1402
+ return null;
1403
+ };
1404
+ /** Add unique ref of the specified index to the proper position within the stack
1405
+ * @private
1406
+ * @param {Object} stackOption
1407
+ * @param {number} colIndex
1408
+ * @return {boolean}
1409
+ */
1410
+ ColumnStackPlugin.prototype._addRefToStack = function (stackOption, colIndex) {
1411
+ var colId = this._toIdOrField(colIndex);
1412
+
1413
+ // Hide the column to prevent it from flashing in stack mode
1414
+ if(!stackOption.spreading || !stackOption.collapsed) {
1415
+ var activeIndex = this.getColumnIndex(stackOption.activeColumn);
1416
+ if(colIndex >= 0 && colIndex !== activeIndex) {
1417
+ this._setColumnVisibility(colIndex, false); // Hide a column
1418
+ }
1419
+ }
1420
+
1421
+ // Find a position to be placed in the stack
1422
+ var rightColRef = this._toIdOrField(colIndex + 1);
1423
+ var children = stackOption.children;
1424
+ var pos = children.indexOf(rightColRef); // WARNING This does not work for field
1425
+ return this._groupDefs.addGroupChild(stackOption.id, colId, pos);
1426
+ };
1427
+ /** Remove unique ref of the specified index from the stack
1428
+ * @private
1429
+ * @param {Object} stackOption
1430
+ * @param {string} colRef For removing state
1431
+ * @param {number} colIndex For updating UI
1432
+ * @return {boolean}
1433
+ */
1434
+ ColumnStackPlugin.prototype._removeRefFromStack = function (stackOption, colRef, colIndex) {
1435
+ if(!this._groupDefs.removeGroupChild(stackOption.id, colRef)) {
1436
+ return false;
1437
+ }
1438
+
1439
+ var isCollapsed = stackOption.spreading && stackOption.collapsed;
1440
+ if(isCollapsed) {
1441
+ this._collapseMember(colIndex, false);
1442
+ } else {
1443
+ this._setColumnVisibility(colIndex, true);
1444
+ }
1445
+ this._updateActiveColumn(stackOption); // This may trigger _updateUI
1446
+ return true;
1447
+ };
1448
+
1449
+ /** @private
1450
+ * @param {Object} stackOption
1451
+ */
1452
+ ColumnStackPlugin.prototype._repositionMembers = function (stackOption) {
1453
+ var children = stackOption.children;
1454
+ var refCount = children ? children.length : 0;
1455
+ if(!refCount) {
1456
+ return;
1457
+ }
1458
+
1459
+ var i;
1460
+ var indexToIds = new Array(refCount);
1461
+ for(i = 0; i < refCount; i++) {
1462
+ var colRef = children[i];
1463
+ indexToIds[i] = [this.getColumnIndex(colRef), colRef];
1464
+ }
1465
+ indexToIds.sort(compareRef);
1466
+ for(i = 0; i < refCount; i++) {
1467
+ children[i] = indexToIds[i][1];
1468
+ }
1469
+ };
1470
+ /** @private
1471
+ * @param {Object} e
1472
+ */
1477
1473
  ColumnStackPlugin.prototype._onStackButtonClicked = function(e) {
1478
1474
  var elem = e.currentTarget;
1479
1475
  if(e.button) { // right click or middle click
@@ -1493,11 +1489,11 @@ ColumnStackPlugin.prototype._onStackButtonClicked = function(e) {
1493
1489
  return;
1494
1490
  }
1495
1491
 
1496
- var stackRefs = colData.stackRefs;
1497
- var activeIndex = stackRefs.indexOf(colData.activeColumn);
1492
+ var children = colData.children;
1493
+ var activeIndex = children.indexOf(colData.activeColumn);
1498
1494
 
1499
- var len = stackRefs.length;
1500
- var colIndices = this._getColumnIndices(stackRefs);
1495
+ var len = children.length;
1496
+ var colIndices = this.getColumnIndices(children);
1501
1497
  var menuData = new Array(len);
1502
1498
  for(var i = len; --i >= 0;) {
1503
1499
  menuData[i] = {
@@ -1510,9 +1506,9 @@ ColumnStackPlugin.prototype._onStackButtonClicked = function(e) {
1510
1506
  pos["menuData"] = menuData;
1511
1507
  pos["activeIndex"] = activeIndex;
1512
1508
  pos["columnIndices"] = colIndices;
1513
- pos["activeColIndex"] = this._getColumnIndex(colData.activeColumn);
1509
+ pos["activeColIndex"] = this.getColumnIndex(colData.activeColumn);
1514
1510
  pos["event"] = e;
1515
- pos["stackId"] = colData.stackId;
1511
+ pos["stackId"] = colData.id;
1516
1512
 
1517
1513
  elem.focus();
1518
1514
  this._dispatch("clicked", pos);
@@ -1546,9 +1542,9 @@ ColumnStackPlugin.prototype._requestStackingByFields = function() {
1546
1542
  */
1547
1543
  ColumnStackPlugin.prototype.getStackMemberIndices = function(stackId) {
1548
1544
  if(stackId) {
1549
- var stack = this._stacks[stackId];
1545
+ var stack = this._groupDefs.getGroup(stackId);
1550
1546
  if(stack){
1551
- return this._getColumnIndices(stack.stackRefs);
1547
+ return this.getColumnIndices(stack.children);
1552
1548
  }
1553
1549
  }
1554
1550
  return [];
@@ -1560,8 +1556,12 @@ ColumnStackPlugin.prototype.getStackMemberIndices = function(stackId) {
1560
1556
  * @return {!Array.<string>} Member column ids
1561
1557
  */
1562
1558
  ColumnStackPlugin.prototype.getStackMemberIds = function(stackId) {
1563
- var memberIndices = this.getStackMemberIndices(stackId);
1564
- return this.getColumnIdsByIndex(memberIndices);
1559
+ var colIndices = this.getStackMemberIndices(stackId);
1560
+ if(colIndices.length) {
1561
+ return colIndices.map(this._toIdOrField);
1562
+ }
1563
+
1564
+ return colIndices;
1565
1565
  };
1566
1566
 
1567
1567
  /** @public
@@ -1621,44 +1621,30 @@ ColumnStackPlugin.prototype.getColumnIdsByFields = function(fields) {
1621
1621
  * @param {string} stackId
1622
1622
  */
1623
1623
  ColumnStackPlugin.prototype.addColumnToStack = function(colRef, stackId) {
1624
- if(typeof colRef === "string") {
1625
- colRef = this.getColumnIndex(colRef);
1626
- if(colRef < 0) {
1627
- return;
1628
- }
1629
- }
1624
+ var colId = this._toIdOrField(colRef);
1630
1625
 
1631
- var stack = this._stacks[stackId];
1632
- var isColumnStackable = this.isColumnStackable([colRef]);
1626
+ var stack = this._groupDefs.getGroup(stackId);
1627
+ var isColumnStackable = !this._groupDefs.getParentGroup(colId);
1633
1628
 
1634
1629
  // if column not stackable or destination stack not exist, do nothing
1635
1630
  if(!isColumnStackable || !stack){
1636
1631
  return;
1637
1632
  }
1638
1633
 
1639
- var isCollapsed = stack.spreading && stack.collapsed;
1640
- if(isCollapsed){
1641
- this.expandGroup(this._getColumnIndex(stack.activeColumn));
1642
- }
1643
-
1644
- // Prevent from flashing in stack mode
1645
- if(stack.collapsed !== false) {
1646
- this._setColumnVisibility(colRef, false);
1634
+ // Hide the column to prevent it from flashing in stack mode
1635
+ if(!stack.spreading || !stack.collapsed) {
1636
+ var activeIndex = this.getColumnIndex(stack.activeColumn);
1637
+ var colIndex = this.getColumnIndex(colId);
1638
+ if(colIndex >= 0 && colIndex !== activeIndex) {
1639
+ this._setColumnVisibility(colIndex, false); // Hide a column
1640
+ }
1647
1641
  }
1648
1642
 
1649
1643
  // apply stacking
1650
- this._setColumnStackOptions(colRef, stack);
1651
- stack.stackRefs.push(this._getUniqueRef(colRef));
1652
-
1653
- if(stack.spreading) {
1654
- stack.activeColumn = stack.stackRefs[stack.stackRefs.length - 1];
1655
- stack.collapsed = isCollapsed;
1656
- } else {
1657
- stack.activeColumn = stack.stackRefs[0];
1658
- }
1644
+ this._groupDefs.addGroupChild(stackId, colId);
1659
1645
 
1660
1646
  // Pack stacked columns together
1661
- this._moveStackedColumns(stack.stackRefs);
1647
+ this._moveStackedColumns(stack.children);
1662
1648
 
1663
1649
  this._updateUI();
1664
1650
  };
@@ -1673,46 +1659,37 @@ ColumnStackPlugin.prototype.removeColumnFromStack = function(colRef) {
1673
1659
  return;
1674
1660
  }
1675
1661
 
1676
- var stackId = this.getStackId(colIndex);
1677
- var stack = this._stacks[stackId];
1662
+ var colId = this.getColumnId(colIndex);
1663
+ var stack = this._groupDefs.getParentGroup(colId);
1664
+ if(stack) {
1665
+ colRef = colId;
1666
+ } else {
1667
+ var field = this._getField(colIndex);
1668
+ stack = this._groupDefs.getParentGroup(field);
1669
+ if(stack) {
1670
+ colRef = field;
1671
+ }
1672
+ }
1673
+
1678
1674
  if(!stack) {
1679
1675
  return;
1680
1676
  }
1681
1677
 
1682
- var stackRefs = stack.stackRefs;
1683
- var memberCount = stackRefs.length;
1678
+ var children = stack.children;
1679
+ var memberCount = children.length;
1684
1680
  if(memberCount <= 2) {
1685
1681
  if(memberCount === 2) {
1686
- var uniqueObj = this._getUniqueRef(colIndex);
1687
- if(uniqueObj === stackRefs[0]) { // If the first column is removed from the stack, move it to the end of stack
1682
+ if(colRef === children[0]) { // If the first column is removed from the stack, move it to the end of stack
1688
1683
  // This assumes that the column order is already in correct position
1689
- this.moveColumnById(colIndex, this._getColumnIndex(stackRefs[1]) + 1);
1684
+ this.moveColumnById(colIndex, this.getColumnIndex(children[1]) + 1);
1690
1685
  }
1691
1686
  }
1692
- this.removeStack(stackId); // updateUI will be called
1687
+ this.removeStack(stack.id); // updateUI will be called
1693
1688
  return;
1694
1689
  }
1695
1690
 
1696
- var colIndices = this.getStackMemberIndices(stackId);
1697
- var memberIdx = colIndices.indexOf(colIndex);
1698
- var isCollapsed = stack.spreading && stack.collapsed;
1699
- if(memberIdx >= 0) {
1700
- if(isCollapsed) {
1701
- this._collapseMember(colIndices[memberIdx], false); // TODO: This may change the column index
1702
- }
1703
- stackRefs.splice(memberIdx, 1); // WARNING: colIndices is out of sync with stackRefs after this line
1704
- }
1705
-
1706
- this._removeColumnStackOptions(colIndex);
1707
- this._setColumnVisibility(colIndex, true);
1708
-
1709
- if(stack.spreading) {
1710
- stack.activeColumn = stackRefs[stackRefs.length - 1];
1711
- stack.collapsed = isCollapsed;
1712
- } else {
1713
- stack.activeColumn = stackRefs[0];
1714
- }
1715
- this._stacks[stackId] = stack; // TODO: This is unnecessary
1691
+ var colIndices = this.getColumnIndices(children);
1692
+ this._removeRefFromStack(stack, colRef, colIndex);
1716
1693
 
1717
1694
  this.moveColumnById(colIndex, colIndices[memberCount - 1] + 1); // This assumes that the column order is already in correct position
1718
1695
  this._updateUI();
@@ -1724,7 +1701,7 @@ ColumnStackPlugin.prototype.removeColumnFromStack = function(colRef) {
1724
1701
  * @param {string} stackId
1725
1702
  */
1726
1703
  ColumnStackPlugin.prototype.reorderStackColumns = function(colRefs, stackId) {
1727
- var stack = this._stacks[stackId];
1704
+ var stack = this._groupDefs.getGroup(stackId);
1728
1705
  if(!stack) {
1729
1706
  return;
1730
1707
  }
@@ -1742,14 +1719,14 @@ ColumnStackPlugin.prototype.reorderStackColumns = function(colRefs, stackId) {
1742
1719
  var i, colIndex;
1743
1720
  for(i = 0; i < len; i++ ){
1744
1721
  colIndex = colRefs[i];
1745
- if(stackMemberIndices.indexOf(colIndex) !== -1){
1722
+ if(stackMemberIndices.indexOf(colIndex) >= 0){
1746
1723
  newStackMembers.push(colIndex);
1747
1724
  }
1748
1725
  }
1749
1726
  if(newStackMembers.length !== stackMemberCount){
1750
1727
  for(i = 0; i < stackMemberCount; i++ ){
1751
1728
  colIndex = stackMemberIndices[i];
1752
- if(newStackMembers.indexOf(colIndex) === -1){
1729
+ if(newStackMembers.indexOf(colIndex) < 0){
1753
1730
  newStackMembers.push(colIndex);
1754
1731
  if(newStackMembers.length === stackMemberCount){
1755
1732
  break;
@@ -1767,9 +1744,8 @@ ColumnStackPlugin.prototype.reorderStackColumns = function(colRefs, stackId) {
1767
1744
  newStackMembers[i] = this._getField(newStackMembers[i]);
1768
1745
  }
1769
1746
 
1770
- this.unstackColumns(stackMemberIndices);
1747
+ this.removeStack(stackId);
1771
1748
  this.stackColumns(newStackMembers, stackId, options);
1772
-
1773
1749
  };
1774
1750
 
1775
1751
  /** @public
@@ -1778,12 +1754,7 @@ ColumnStackPlugin.prototype.reorderStackColumns = function(colRefs, stackId) {
1778
1754
  * @param {string} name
1779
1755
  */
1780
1756
  ColumnStackPlugin.prototype.setStackName = function(stackId, name) {
1781
- if(stackId !== null) {
1782
- var stack = this._stacks[stackId];
1783
- if(stack){
1784
- stack.name = name;
1785
- }
1786
- }
1757
+ this._groupDefs.setGroupName(stackId, name);
1787
1758
  };
1788
1759
 
1789
1760
  /** @public
@@ -1792,14 +1763,7 @@ ColumnStackPlugin.prototype.setStackName = function(stackId, name) {
1792
1763
  * @return {string} Stack name
1793
1764
  */
1794
1765
  ColumnStackPlugin.prototype.getStackName = function(stackId) {
1795
- var stackName = "";
1796
- if(stackId !== null) {
1797
- var stack = this._stacks[stackId];
1798
- if(stack){
1799
- stackName = stack.name;
1800
- }
1801
- }
1802
- return stackName;
1766
+ return this._groupDefs.getGroupName(stackId);
1803
1767
  };
1804
1768
 
1805
1769
  /** @public
@@ -1808,12 +1772,7 @@ ColumnStackPlugin.prototype.getStackName = function(stackId) {
1808
1772
  * @return {string} active column field
1809
1773
  */
1810
1774
  ColumnStackPlugin.prototype.getActiveColumnField = function(stackId) {
1811
- var field = "";
1812
- var activeColIndex = this.getActiveColumnIndex(stackId);
1813
- if(activeColIndex !== -1){
1814
- field = this._getField(activeColIndex);
1815
- }
1816
- return field;
1775
+ return this._getField(this.getActiveColumnIndex(stackId));
1817
1776
  };
1818
1777
 
1819
1778
  /** @public
@@ -1822,14 +1781,11 @@ ColumnStackPlugin.prototype.getActiveColumnField = function(stackId) {
1822
1781
  * @return {number} active column index
1823
1782
  */
1824
1783
  ColumnStackPlugin.prototype.getActiveColumnIndex = function(stackId) {
1825
- var activeColIndex = -1;
1826
- if(stackId !== null) {
1827
- var stack = this._stacks[stackId];
1828
- if(stack){
1829
- activeColIndex = this._getColumnIndex(stack.activeColumn);
1830
- }
1784
+ var stack = this._groupDefs.getGroup(stackId);
1785
+ if(stack){
1786
+ return this.getColumnIndex(stack.activeColumn);
1831
1787
  }
1832
- return activeColIndex;
1788
+ return -1;
1833
1789
  };
1834
1790
  //getActiveColumnIndex
1835
1791
 
@@ -1867,11 +1823,11 @@ ColumnStackPlugin.prototype.unsetParent = ColumnStackPlugin.prototype.removeColu
1867
1823
  */
1868
1824
  ColumnStackPlugin.prototype.reorderColumns = function(colList, destCol) {
1869
1825
  var dirty = false;
1870
- this._stacking = true;
1826
+ this._inReordering = true;
1871
1827
 
1872
1828
  dirty = this._reorderColumns(colList, destCol);
1873
1829
 
1874
- this._stacking = false;
1830
+ this._inReordering = false;
1875
1831
  return dirty;
1876
1832
  };
1877
1833
  /** Move the specified column to position before the destination
@@ -1881,13 +1837,7 @@ ColumnStackPlugin.prototype.reorderColumns = function(colList, destCol) {
1881
1837
  * @return {boolean}
1882
1838
  */
1883
1839
  ColumnStackPlugin.prototype.moveColumnById = function(srcCol, destCol) {
1884
- var dirty = false;
1885
- this._stacking = false;
1886
-
1887
- this._moveColumnById(srcCol, destCol);
1888
-
1889
- this._stacking = true;
1890
- return dirty;
1840
+ return this._moveColumnById(srcCol, destCol);
1891
1841
  };
1892
1842
 
1893
1843
  /** @private
@@ -1896,40 +1846,35 @@ ColumnStackPlugin.prototype.moveColumnById = function(srcCol, destCol) {
1896
1846
  * @param {boolean} visible
1897
1847
  */
1898
1848
  ColumnStackPlugin.prototype._setStackVisibility = function(stackId, visible) {
1899
- var stackOption = this._stacks[stackId];
1849
+ var stackOption = this._groupDefs.getGroup(stackId);
1900
1850
  if(!stackOption){
1901
1851
  return;
1902
1852
  }
1903
1853
 
1904
- if(stackOption["spreading"] && !stackOption["collapsed"]){
1905
- var stackRefs = stackOption["stackRefs"];
1906
- for(var i = 0; i < stackRefs.length; i++){
1907
- var colIndex = this._getColumnIndex(stackRefs[i]);
1908
- this._setColumnVisibility(colIndex, visible);
1854
+ if(stackOption.spreading && !stackOption.collapsed){
1855
+ var children = stackOption.children;
1856
+ for(var i = 0; i < children.length; i++){
1857
+ this._setColumnVisibility(this.getColumnIndex(children[i]), visible);
1909
1858
  }
1910
1859
  } else {
1911
- var activeColIndex = this._getColumnIndex(stackOption.activeColumn);
1860
+ var activeColIndex = this.getColumnIndex(stackOption.activeColumn);
1912
1861
  this._setColumnVisibility(activeColIndex, visible);
1913
1862
  }
1914
1863
  };
1915
1864
 
1865
+
1916
1866
  /** @private
1917
1867
  * @description Check for active column in a stack if it needs an update.
1918
1868
  * @param {Object} stackOpt
1919
1869
  */
1920
1870
  ColumnStackPlugin.prototype._updateActiveColumn = function(stackOpt) {
1921
- if(!stackOpt) { return; }
1922
-
1923
- var stackRefs = stackOpt["stackRefs"];
1924
- if(!stackRefs || !stackRefs.length) { return; }
1925
-
1926
- if(stackRefs.indexOf(stackOpt.activeColumn) === -1) {
1927
- stackOpt.activeColumn = stackOpt["spreading"] ? stackRefs[stackRefs.length - 1] : stackRefs[0];
1928
-
1871
+ if(_resolveActiveColumn(stackOpt)) {
1929
1872
  // TODO: Add a proper way to set visibility to activeColumn when activeColumn is changed
1930
- var activeColIndex = this._getColumnIndex(stackOpt.activeColumn);
1931
- this._setColumnVisibility(activeColIndex, true);
1932
- this._updateUI();
1873
+ var activeColIndex = this.getColumnIndex(stackOpt.activeColumn);
1874
+ if(activeColIndex >= 0) {
1875
+ this._setColumnVisibility(activeColIndex, true);
1876
+ this._updateUI();
1877
+ }
1933
1878
  }
1934
1879
  };
1935
1880
 
@@ -1961,14 +1906,14 @@ ColumnStackPlugin.prototype.showStack = function(stackId) {
1961
1906
  * @return {boolean|null}
1962
1907
  */
1963
1908
  ColumnStackPlugin.prototype.isStackHidden = function(stackId) {
1964
- var stackOption = this._stacks[stackId];
1909
+ var stackOption = this._groupDefs.getGroup(stackId);
1965
1910
  var host = this._host || this._hosts[0];
1966
1911
 
1967
1912
  if(!stackOption || !host){
1968
1913
  return null;
1969
1914
  }
1970
1915
 
1971
- var activeColIndex = this._getColumnIndex(stackOption.activeColumn);
1916
+ var activeColIndex = this.getColumnIndex(stackOption.activeColumn);
1972
1917
  var isVisible = host.isColumnVisible(activeColIndex);
1973
1918
 
1974
1919
  return !isVisible;
@@ -1983,7 +1928,7 @@ ColumnStackPlugin.prototype.moveStack = function(stackId, destCol) {
1983
1928
  if(!stackId){
1984
1929
  return false;
1985
1930
  }
1986
- var colList = this.getStackMemberIds(stackId);
1931
+ var colList = this.getStackMemberIds(stackId); // WARNING: column ids or fields
1987
1932
  var dirty = this.reorderColumns(colList, destCol);
1988
1933
  return dirty;
1989
1934
  };