@refinitiv-ui/efx-grid 6.0.35 → 6.0.37

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