@refinitiv-ui/efx-grid 6.0.147 → 6.0.149

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.
@@ -13030,7 +13030,7 @@ EventDispatcher_EventDispatcher.preventDefault = preventDefault;
13030
13030
  */
13031
13031
  let Segment = function(rid, sharedObj) {
13032
13032
  this._rid = rid;
13033
- this._children = {};
13033
+ this._childIds = [];
13034
13034
  this._shared = sharedObj;
13035
13035
  if(sharedObj.defaultCollapsing) {
13036
13036
  this._collapsed = true;
@@ -13061,14 +13061,6 @@ Segment._subSegSortLogic = function(a, b) {
13061
13061
 
13062
13062
  return 0;
13063
13063
  };
13064
- /** @private
13065
- * @function
13066
- * @param {Segment} segment
13067
- * @param {number} idx
13068
- */
13069
- Segment._assignSubSegmentOrder = function(segment, idx) {
13070
- segment.setOrder(idx + 1);
13071
- };
13072
13064
 
13073
13065
  /** @type {Object}
13074
13066
  * @private
@@ -13079,14 +13071,14 @@ Segment.prototype._shared = null;
13079
13071
  * @private
13080
13072
  */
13081
13073
  Segment.prototype._rid;
13082
- /** @type {!Object}
13074
+ /** @type {!Array.<string>}
13083
13075
  * @private
13084
13076
  */
13085
- Segment.prototype._children;
13086
- /** @type {number}
13077
+ Segment.prototype._childIds;
13078
+ /** @type {Object}
13087
13079
  * @private
13088
13080
  */
13089
- Segment.prototype._childCount = 0;
13081
+ Segment.prototype._childDataIds = null;
13090
13082
  /** @type {boolean}
13091
13083
  * @private
13092
13084
  */
@@ -13095,6 +13087,14 @@ Segment.prototype._collapsed = false;
13095
13087
  * @private
13096
13088
  */
13097
13089
  Segment.prototype._order = 0;
13090
+ /** @type {number}
13091
+ * @private
13092
+ */
13093
+ Segment.prototype._offsetOrder = 0;
13094
+ /** @type {number}
13095
+ * @private
13096
+ */
13097
+ Segment.prototype._depth = 0;
13098
13098
  /** @type {boolean}
13099
13099
  * @private
13100
13100
  */
@@ -13104,10 +13104,10 @@ Segment.prototype._disposed = false;
13104
13104
  * @private
13105
13105
  */
13106
13106
  Segment.prototype._subSegDef = null;
13107
- /** @type {number}
13107
+ /** @type {boolean}
13108
13108
  * @private
13109
13109
  */
13110
- Segment.prototype._subSegLevel = 0;
13110
+ Segment.prototype._subSegment = false; // This indicates that the segment is autogenerated (subsegment)
13111
13111
  /** @type {Object.<string, Segment>}
13112
13112
  * @private
13113
13113
  */
@@ -13115,7 +13115,7 @@ Segment.prototype._subSegMap = null; // For immediate sub-segment children
13115
13115
  /** @type {Array.<string>}
13116
13116
  * @private
13117
13117
  */
13118
- Segment.prototype._subSegNames = null; // For immediate sub-segment child names
13118
+ Segment.prototype._subSegNames = null; // For immediate sub-segment child names/ids
13119
13119
  /** @type {string}
13120
13120
  * @private
13121
13121
  */
@@ -13124,10 +13124,6 @@ Segment.prototype._subSegName = "";
13124
13124
  * @private
13125
13125
  */
13126
13126
  Segment.prototype._subSegVal;
13127
- /** @type {Segment}
13128
- * @private
13129
- */
13130
- Segment.prototype._subSegParent = null;
13131
13127
 
13132
13128
 
13133
13129
  /** @public
@@ -13149,14 +13145,13 @@ Segment.prototype.dispose = function() {
13149
13145
  this._subSegMap = this._subSegNames = null;
13150
13146
  }
13151
13147
  if(this._collapsed) {
13152
- if(this._childCount || this._subSegDef) {
13153
- this._shared.dirtyCollapsingState = true;
13154
- }
13148
+ this.markCollapsingStateDirty();
13155
13149
  }
13156
13150
 
13157
- this._childCount = 0;
13151
+ this._childDataIds = null;
13152
+ this._childIds.length = 0;
13158
13153
  this._shared = null;
13159
- this._subSegParent = this._subSegDef = this._subSegVal = null;
13154
+ this._subSegDef = this._subSegVal = null;
13160
13155
  };
13161
13156
  /** @public
13162
13157
  * @return {string}
@@ -13168,10 +13163,17 @@ Segment.prototype.getId = function() {
13168
13163
  * @return {string}
13169
13164
  */
13170
13165
  Segment.prototype.getParentId = function() {
13171
- if(this._subSegParent) {
13172
- return this._subSegParent.getId();
13166
+ return this._shared.childToSegment[this._rid] || "";
13167
+ };
13168
+ /** @public
13169
+ * @return {Segment}
13170
+ */
13171
+ Segment.prototype.getParent = function() {
13172
+ let parentId = this.getParentId();
13173
+ if(parentId) {
13174
+ return this._shared.segments[parentId] || null;
13173
13175
  }
13174
- return "";
13176
+ return null;
13175
13177
  };
13176
13178
  /** @public
13177
13179
  * @param {Array.<string>=} out_ary
@@ -13199,22 +13201,44 @@ Segment.prototype.getSubSegmentIds = function(out_ary) {
13199
13201
  /** @public
13200
13202
  * @param {string} rid
13201
13203
  * @param {string=} dataId Row id for retrieving data
13202
- * @return {boolean}
13204
+ * @return {boolean} Returns true only a new child is added
13203
13205
  */
13204
13206
  Segment.prototype.addChild = function(rid, dataId) {
13205
- if(rid) {
13207
+ if(!rid) {
13208
+ return false;
13209
+ }
13210
+ let prevSegmentId = this._shared.childToSegment[rid];
13211
+ if(prevSegmentId) {
13212
+ if(prevSegmentId !== this._rid) {
13213
+ let prevSegment = this._shared.segments[prevSegmentId];
13214
+ if(prevSegment && !prevSegment.hasSubSegments()) { // Children of a classified segment always stick the root segment
13215
+ prevSegment.removeChild(rid);
13216
+ }
13217
+ this._shared.childToSegment[rid] = this._rid;
13218
+ }
13219
+ } else {
13206
13220
  this._shared.childToSegment[rid] = this._rid;
13207
- if(this._children[rid]) {
13208
- this._children[rid] = dataId || rid; // Update data id
13209
- } else {
13210
- if(this._collapsed) {
13211
- this._shared.dirtyCollapsingState = true; // TODO: Check if we need to update this only when new child is added
13221
+ }
13222
+
13223
+ if(dataId != null) { // Update data id
13224
+ if(dataId && dataId !== rid) {
13225
+ if(!this._childDataIds) {
13226
+ this._childDataIds = {};
13212
13227
  }
13213
- this._children[rid] = dataId || rid;
13214
- ++this._childCount;
13215
- return true;
13228
+ this._childDataIds[rid] = dataId;
13229
+ } else if(this._childDataIds) {
13230
+ delete this._childDataIds[rid];
13216
13231
  }
13217
13232
  }
13233
+
13234
+ let at = this._childIds.indexOf(rid);
13235
+ if(at < 0) {
13236
+ if(this._collapsed) {
13237
+ this._shared.dirtyCollapsingState = true; // TODO: Check if we need to update this only when new child is added
13238
+ }
13239
+ this._childIds.push(rid);
13240
+ return true;
13241
+ }
13218
13242
  return false;
13219
13243
  };
13220
13244
  /** @public
@@ -13223,8 +13247,15 @@ Segment.prototype.addChild = function(rid, dataId) {
13223
13247
  * @return {boolean}
13224
13248
  */
13225
13249
  Segment.prototype.addChildren = function(rids, dataIds) {
13250
+ if(!rids) {
13251
+ return false;
13252
+ }
13226
13253
  let rowIds = Array.isArray(rids) ? rids : [rids];
13227
13254
  let rowCount = rowIds.length;
13255
+ if(!rowCount) {
13256
+ return false;
13257
+ }
13258
+
13228
13259
  let dirty = 0;
13229
13260
  let i;
13230
13261
  if(dataIds != null) {
@@ -13244,27 +13275,35 @@ Segment.prototype.addChildren = function(rids, dataIds) {
13244
13275
  * @return {boolean}
13245
13276
  */
13246
13277
  Segment.prototype.containsChild = function(rid) {
13247
- return this._children[rid] ? true : false;
13278
+ return this._childIds.indexOf(rid) >= 0;
13279
+ };
13280
+ /** @public
13281
+ * @param {string} rid
13282
+ * @return {number}
13283
+ */
13284
+ Segment.prototype.getChildIndex = function(rid) {
13285
+ return this._childIds.indexOf(rid);
13248
13286
  };
13249
13287
  /** @public
13250
13288
  * @param {string} rid
13251
13289
  * @return {boolean}
13252
13290
  */
13253
13291
  Segment.prototype.removeChild = function(rid) {
13254
- if(this._subSegLevel) {
13292
+ if(this._subSegment) {
13255
13293
  return false; // Sub segments are not allowed to remove its children
13256
13294
  }
13257
- if(!this._childCount) {
13295
+ if(!this._childIds.length) {
13258
13296
  return false;
13259
13297
  }
13260
- if(!this._children[rid]) {
13298
+ let at = this._childIds.indexOf(rid);
13299
+ if(at < 0) {
13261
13300
  return false; // The specified rid is not a child of this segment
13262
13301
  }
13263
13302
 
13264
- let objMap = this._shared.childToSegment;
13265
- delete objMap[rid];
13266
- delete this._children[rid]; // Slow
13267
- --this._childCount;
13303
+ if(this._shared.childToSegment[rid] === this._rid) {
13304
+ delete this._shared.childToSegment[rid];
13305
+ }
13306
+ this._childIds.splice(at, 1); // Slow
13268
13307
 
13269
13308
  if(this._collapsed) {
13270
13309
  this._shared.dirtyCollapsingState = true;
@@ -13276,10 +13315,10 @@ Segment.prototype.removeChild = function(rid) {
13276
13315
  * @return {boolean}
13277
13316
  */
13278
13317
  Segment.prototype.removeChildren = function(rids) {
13279
- if(this._subSegLevel) {
13318
+ if(this._subSegment) {
13280
13319
  return false; // Sub segments are not allowed to remove its children
13281
13320
  }
13282
- if(!this._childCount) {
13321
+ if(!this._childIds.length) {
13283
13322
  return false;
13284
13323
  }
13285
13324
  let rowIds = Array.isArray(rids) ? rids : [rids];
@@ -13294,21 +13333,24 @@ Segment.prototype.removeChildren = function(rids) {
13294
13333
  * @return {boolean}
13295
13334
  */
13296
13335
  Segment.prototype.removeAllChildren = function() {
13297
- if(this._subSegLevel) {
13336
+ if(this._subSegment) {
13298
13337
  return false; // Sub segments are not allowed to remove its children
13299
13338
  }
13300
- if(!this._childCount) {
13339
+ let childCount = this._childIds.length;
13340
+ if(!this._childIds.length) {
13301
13341
  return false;
13302
13342
  }
13343
+ let segmentId = this._rid;
13303
13344
  let objMap = this._shared.childToSegment;
13304
- let chdr = this._children;
13305
- for(let rid in chdr) {
13306
- if(objMap[rid]) {
13307
- delete objMap[rid]; // TODO: Check if we need to do this
13345
+ let chdr = this._childIds;
13346
+ for(let i = 0; i < childCount; ++i) {
13347
+ let rid = chdr[i];
13348
+ if(objMap[rid] === segmentId) {
13349
+ delete objMap[rid];
13308
13350
  }
13309
13351
  }
13310
- this._children = {};
13311
- this._childCount = 0;
13352
+ this._childIds.length = 0;
13353
+ this._childDataIds = null;
13312
13354
 
13313
13355
  if(this._collapsed) {
13314
13356
  this._shared.dirtyCollapsingState = true;
@@ -13319,24 +13361,41 @@ Segment.prototype.removeAllChildren = function() {
13319
13361
  * @return {!Array.<string>}
13320
13362
  */
13321
13363
  Segment.prototype.getChildIds = function() {
13322
- return this._childCount ? Object.keys(this._children) : [];
13364
+ return this._childIds; // WARNING: Returns private members
13323
13365
  };
13324
13366
  /** @public
13325
13367
  * @return {!Object}
13326
13368
  */
13327
13369
  Segment.prototype.getChildren = function() {
13328
- return this._children;
13370
+ let obj = {};
13371
+ let chdr = this._childIds;
13372
+ let childCount = chdr.length;
13373
+ let dataIds = this._childDataIds || {};
13374
+ for(let i = 0; i < childCount; ++i) {
13375
+ let rid = chdr[i];
13376
+ obj[rid] = dataIds[rid] || rid;
13377
+ }
13378
+ return obj;
13329
13379
  };
13330
13380
  /** @public
13331
13381
  * @return {number}
13332
13382
  */
13333
13383
  Segment.prototype.getChildCount = function() {
13334
- return this._childCount;
13384
+ return this._childIds.length;
13385
+ };
13386
+ /** When a segment is not empty, the visibility of its content need to be updated.
13387
+ * @public
13388
+ */
13389
+ Segment.prototype.markCollapsingStateDirty = function() {
13390
+ // A segment can have a child and/or autogenerated segment (subsegment) to not be considered as empty.
13391
+ if(this._childIds.length || this._subSegDef) {
13392
+ this._shared.dirtyCollapsingState = true;
13393
+ }
13335
13394
  };
13336
13395
 
13337
13396
 
13338
13397
  /** @public
13339
- * @return {Array.<string>} fields
13398
+ * @return {Array.<string>}
13340
13399
  */
13341
13400
  Segment.prototype.getClassification = function() {
13342
13401
  if(this._subSegDef) {
@@ -13349,8 +13408,8 @@ Segment.prototype.getClassification = function() {
13349
13408
  * @return {boolean}
13350
13409
  */
13351
13410
  Segment.prototype.setClassification = function(fields) {
13352
- if(this._subSegLevel) {
13353
- return false; // non-root segment cannot be classified
13411
+ if(this._subSegment) {
13412
+ return false; // subsegment cannot be classified
13354
13413
  }
13355
13414
  let classifiers = null;
13356
13415
  if(this._subSegDef) {
@@ -13393,9 +13452,8 @@ Segment.prototype.setClassification = function(fields) {
13393
13452
  return true;
13394
13453
  } else if(classifiers) { // Remove existing ones
13395
13454
  this._subSegDef.classifiers = null;
13396
- this._subSegDef.subSegments = null;
13397
13455
  // this._subSegDef.classifierChanged = true;
13398
- this._subSegDef = null; // WARNING: All sub segments remain existing
13456
+ this._subSegDef = null; // WARNING: All subsegments remain existing
13399
13457
  return true;
13400
13458
  }
13401
13459
  return false;
@@ -13412,7 +13470,7 @@ Segment.prototype.classify = function(rows) {
13412
13470
  let segmentCount = segmentNames ? segmentNames.length : 0;
13413
13471
 
13414
13472
  if(!segmentCount) {
13415
- if(this._subSegLevel >= classifierCount) {
13473
+ if(this._depth >= classifierCount) {
13416
13474
  return false; // Current segment level is beyond existing classification level and this segment should already be removed
13417
13475
  }
13418
13476
  }
@@ -13422,7 +13480,7 @@ Segment.prototype.classify = function(rows) {
13422
13480
  sharedObj.dirtyCollapsingState = true;
13423
13481
  }
13424
13482
 
13425
- // Prepare existing sub segments for checking change in its members
13483
+ // Prepare existing subsegments for checking change in its members
13426
13484
  let i;
13427
13485
  let segmentName = "";
13428
13486
  let nonExistenceGroups = {};
@@ -13436,9 +13494,8 @@ Segment.prototype.classify = function(rows) {
13436
13494
  nonExistenceGroups[segmentName] = 1;
13437
13495
 
13438
13496
  segment = segmentMap[segmentName];
13439
- if(segment._childCount) { // Quick cleaning up
13440
- segment._children = {};
13441
- segment._childCount = 0;
13497
+ if(segment._childIds.length) { // Quick cleaning up
13498
+ segment._childIds.length = 0;
13442
13499
  }
13443
13500
  if(segment._collapsed) {
13444
13501
  sharedObj.dirtyCollapsingState = true;
@@ -13446,20 +13503,23 @@ Segment.prototype.classify = function(rows) {
13446
13503
  }
13447
13504
  }
13448
13505
 
13449
- // Loop through row children and assign them to their corresponding sub segment
13450
- let isRootSegment = !this._subSegLevel;
13506
+ // Loop through row children and assign them to their corresponding subsegment
13507
+ let rootSegment = !this._subSegment;
13508
+ let dataIds = this._childDataIds || {};
13451
13509
  let rid;
13452
- let children = this._children;
13453
- if(this._subSegLevel < classifierCount && rows) {
13510
+ let chdr = this._childIds;
13511
+ let childCount = chdr.length;
13512
+ if(this._depth < classifierCount && rows) {
13454
13513
  if(!segmentMap) {
13455
13514
  segmentMap = this._subSegMap = {};
13456
13515
  segmentNames = this._subSegNames = [];
13457
13516
  }
13458
13517
 
13459
- let classifier = classifiers[this._subSegLevel];
13518
+ let classifier = classifiers[this._depth];
13460
13519
 
13461
- for(rid in children) {
13462
- let dataId = children[rid];
13520
+ for(i = 0; i < childCount; ++i) {
13521
+ rid = chdr[i];
13522
+ let dataId = dataIds[rid] || rid;
13463
13523
  let record = rows[dataId];
13464
13524
  let val = record ? record[classifier] : null; // WARNING: row could already be removed
13465
13525
 
@@ -13476,31 +13536,34 @@ Segment.prototype.classify = function(rows) {
13476
13536
 
13477
13537
  segment = segmentMap[segmentName];
13478
13538
  if(!segment) { // New group is detected
13479
- segment = new Segment(this._rid + "/" + segmentName, sharedObj);
13539
+ let subSegId = this._rid + "/" + segmentName;
13540
+ segment = new Segment(subSegId, sharedObj);
13480
13541
  segment._subSegDef = this._subSegDef;
13481
- segment._subSegLevel = this._subSegLevel + 1;
13542
+ segment._subSegment = true; // this indicates that the segment is autogenerated (subsegment)
13543
+ segment._depth = this._depth + 1;
13482
13544
  segment._subSegName = segmentName;
13483
13545
  segment._subSegVal = val;
13484
- segment._subSegParent = this;
13546
+ sharedObj.childToSegment[subSegId] = this._rid; // WARNING: this will mix autogenerated rows with actual rows
13485
13547
 
13486
13548
  segmentMap[segmentName] = segment;
13487
13549
  segmentNames.push(segmentName);
13488
13550
 
13489
13551
  this._dispatch("subSegmentAdded", {
13490
- "rid": segment.getId(),
13552
+ "rid": subSegId,
13491
13553
  "segment": segment
13492
13554
  });
13493
13555
  }
13494
13556
 
13495
13557
  segment.addChild(rid, dataId);
13496
13558
  }
13497
- } else if(isRootSegment) { // In case of no classification
13498
- for(rid in children) {
13559
+ } else if(rootSegment) { // In case of no classification
13560
+ for(i = 0; i < childCount; ++i) {
13561
+ rid = chdr[i];
13499
13562
  sharedObj.childToSegment[rid] = this._rid; // Relocate child in case of it has been moved to a non existence group
13500
13563
  }
13501
13564
  }
13502
13565
 
13503
- // Remove all sub segments with no members
13566
+ // Remove all subsegments with no members
13504
13567
  if(removalCount > 0) {
13505
13568
  if(removalCount >= segmentNames.length) {
13506
13569
  segmentNames.length = 0;
@@ -13528,7 +13591,7 @@ Segment.prototype.classify = function(rows) {
13528
13591
  }
13529
13592
  }
13530
13593
 
13531
- // Sort and classify existing sub segments
13594
+ // Sort and classify existing subsegments
13532
13595
  segmentCount = segmentNames ? segmentNames.length : 0;
13533
13596
  if(segmentCount) {
13534
13597
  segmentNames.sort(Segment._subSegSortLogic);
@@ -13538,22 +13601,17 @@ Segment.prototype.classify = function(rows) {
13538
13601
  }
13539
13602
  }
13540
13603
 
13541
- // Collecting all sub segments including all descendants and reassigning segment order.
13542
- if(isRootSegment) { // If this is a root segment
13543
- if(this._subSegDef) {
13544
- if(segmentCount) {
13545
- let subSegments = this._subSegDef.subSegments = [];
13546
- this.getAllSubSegments(subSegments);
13547
- subSegments.forEach(Segment._assignSubSegmentOrder);
13548
- } else {
13549
- this._subSegDef.subSegments = null;
13550
- }
13551
- // this._subSegDef.classifierChanged = false;
13604
+ // Collecting all subsegments including all descendants and reassigning segment order.
13605
+ if(rootSegment && this._subSegDef) {
13606
+ if(segmentCount) {
13607
+ this.calcSubSegmentOrder(0);
13552
13608
  }
13609
+ // this._subSegDef.classifierChanged = false;
13553
13610
  }
13554
13611
  return true;
13555
13612
  };
13556
- /** @public
13613
+ /** SubSegment implies being classified
13614
+ * @public
13557
13615
  * @return {boolean}
13558
13616
  */
13559
13617
  Segment.prototype.hasSubSegments = function() {
@@ -13562,21 +13620,37 @@ Segment.prototype.hasSubSegments = function() {
13562
13620
  }
13563
13621
  return false;
13564
13622
  };
13565
- /** @public
13623
+ /** SubSegment implies autogenerated segment
13624
+ * @public
13566
13625
  * @return {boolean}
13567
13626
  */
13568
13627
  Segment.prototype.isSubSegment = function() {
13569
- return this._subSegLevel ? true : false;
13628
+ return this._subSegment;
13629
+ };
13630
+ /** @public
13631
+ * @return {boolean}
13632
+ */
13633
+ Segment.prototype.isRootSegment = function() {
13634
+ return this._shared.childToSegment[this._rid] ? false : true;
13570
13635
  };
13571
13636
  /** @public
13572
13637
  * @return {Segment}
13573
13638
  */
13574
13639
  Segment.prototype.getFirstAncestor = function() {
13575
- if(this._subSegLevel && this._subSegDef) {
13576
- let ancestor = this._subSegDef.root;
13577
- return /** @type{Segment} */(ancestor) || null;
13640
+ let ancestor = null;
13641
+ if(this._subSegment && this._subSegDef) { // Quick way to get root
13642
+ ancestor = this._subSegDef.root;
13643
+ } else { // Slow
13644
+ ancestor = this.getParent();
13645
+ if(ancestor) {
13646
+ let parentSeg = ancestor.getParent();
13647
+ while(parentSeg) { // This could cause infinite loop
13648
+ ancestor = parentSeg;
13649
+ parentSeg = ancestor.getParent();
13650
+ }
13651
+ }
13578
13652
  }
13579
- return null;
13653
+ return ancestor || null;
13580
13654
  };
13581
13655
  /** @public
13582
13656
  * @param {Array.<Segment>=} out_ary
@@ -13598,13 +13672,75 @@ Segment.prototype.getAllSubSegments = function(out_ary) {
13598
13672
  }
13599
13673
  return out_ary || null;
13600
13674
  };
13675
+ /** This method sets order, last order, and depth to entire tree structure in the segment, including the segment itself
13676
+ * @public
13677
+ * @param {number} counter
13678
+ * @return {number}
13679
+ */
13680
+ Segment.prototype.updateTreeStructure = function(counter) {
13681
+ if(!counter) {
13682
+ counter = 0;
13683
+ if(!this._subSegment) { // Subsegment's depth cannot be reset back to 0
13684
+ this._depth = 0; // WARNING: this assumes counter at 0 is the root segment
13685
+ }
13686
+ }
13687
+ if(this.hasSubSegments()) {
13688
+ return this.setLastOrder(counter); // Sub segments has already been calculated
13689
+ }
13690
+ let segmentSeparators = this._shared.segments;
13691
+ let childCount = this._childIds.length;
13692
+ let prevSeg = null;
13693
+ for(let i = 0; i < childCount; ++i) {
13694
+ let rid = this._childIds[i];
13695
+ let segment = segmentSeparators[rid];
13696
+ let segmentId = (segment) ? segment.getId() : "Uncategorized";
13697
+ if(prevSeg !== segmentId) {
13698
+ ++counter; // 0 become 1
13699
+ prevSeg = segmentId;
13700
+ }
13701
+ if(segment) {
13702
+ segment._depth = this._depth + 1;
13703
+ segment.setOrder(counter);
13704
+ counter = segment.updateTreeStructure(counter);
13705
+ }
13706
+ }
13707
+
13708
+ return this.setLastOrder(counter);
13709
+ };
13710
+ /** @public
13711
+ * @param {number} counter
13712
+ * @return {number}
13713
+ */
13714
+ Segment.prototype.calcSubSegmentOrder = function(counter) {
13715
+ if(!this.hasSubSegments()) {
13716
+ return this.setLastOrder(counter);
13717
+ }
13718
+
13719
+ let segmentMap = this._subSegMap;
13720
+ let childCount = this._subSegNames.length;
13721
+ let prevSeg = null;
13722
+ for(let i = 0; i < childCount; ++i) {
13723
+ let segmentName = this._subSegNames[i];
13724
+ let segment = segmentMap[segmentName];
13725
+ let segmentId = (segment) ? segment.getId() : "Uncategorized";
13726
+ if(prevSeg !== segmentId) {
13727
+ ++counter; // 0 become 1
13728
+ prevSeg = segmentId;
13729
+ }
13730
+ if(segment) {
13731
+ segment.setOrder(counter);
13732
+ counter = segment.calcSubSegmentOrder(counter);
13733
+ }
13734
+ }
13735
+ return this.setLastOrder(counter);
13736
+ };
13601
13737
  /** @public
13602
13738
  * @return {number}
13603
13739
  */
13604
13740
  Segment.prototype.getSegmentLevel = function() {
13605
- return this._subSegLevel;
13741
+ return this._depth;
13606
13742
  };
13607
- /** This method will be called on sub segments only
13743
+ /** This method will be called on subsegments only
13608
13744
  * @public
13609
13745
  * @param {Object=} rows
13610
13746
  * @param {Object=} clsSource
@@ -13629,7 +13765,7 @@ Segment.prototype.setRowData = function(rows, clsSource) {
13629
13765
  let segment = this;
13630
13766
  while(segment && segment.isSubSegment()) {
13631
13767
  segment.getSubSegmentName(row);
13632
- segment = segment._subSegParent;
13768
+ segment = segment.getParent();
13633
13769
  }
13634
13770
  };
13635
13771
  /** @public
@@ -13637,9 +13773,9 @@ Segment.prototype.setRowData = function(rows, clsSource) {
13637
13773
  * @return {string}
13638
13774
  */
13639
13775
  Segment.prototype.getSubSegmentName = function(row) {
13640
- if(row && this._subSegLevel) {
13776
+ if(row && this._subSegment) {
13641
13777
  let classifiers = this.getClassification();
13642
- let field = classifiers[this._subSegLevel - 1];
13778
+ let field = classifiers[this._depth - 1];
13643
13779
  if(field) {
13644
13780
  row[field] = this._subSegName;
13645
13781
  }
@@ -13655,9 +13791,7 @@ Segment.prototype.collapse = function(bool) {
13655
13791
  bool = (bool !== false);
13656
13792
  if(this._collapsed !== bool) {
13657
13793
  this._collapsed = bool;
13658
- if(this._childCount || this._subSegDef) {
13659
- this._shared.dirtyCollapsingState = true;
13660
- }
13794
+ this.markCollapsingStateDirty();
13661
13795
  return true;
13662
13796
  }
13663
13797
  return false;
@@ -13675,82 +13809,87 @@ Segment.prototype.expand = function(bool) {
13675
13809
  Segment.prototype.isCollapsed = function() {
13676
13810
  return this._collapsed;
13677
13811
  };
13678
- /** @public
13812
+ /** Get all collapsing state from all children (subsegments and child segments), excluding the segment itself
13813
+ * @public
13679
13814
  * @param {Object=} objMap
13680
13815
  * @param {boolean=} parentState=false Collapsing state from parent segment
13681
13816
  * @return {boolean}
13682
13817
  */
13683
13818
  Segment.prototype.getCollapsingStates = function(objMap, parentState) {
13684
- let segmentNames = this._subSegNames;
13685
- if(!this._subSegLevel) { // Only root segment
13686
- if(!segmentNames) { // No sub segment
13687
- if(!this._collapsed) {
13688
- return false;
13689
- }
13690
- }
13819
+ let childList = this._subSegNames;
13820
+ let segmentMap = this._subSegMap;
13821
+ let subSegment = true; // Normal segments can have subsegments without being subsegment themselve
13822
+ if(!childList && this._shared) { // Ensure that segment has not been disposed
13823
+ childList = this._childIds;
13824
+ segmentMap = this._shared.segments;
13825
+ subSegment = false;
13826
+ }
13827
+ let childCount = childList ? childList.length : 0;
13828
+ if(!childCount) {
13829
+ return false;
13691
13830
  }
13692
13831
 
13832
+ let dirty = false;
13833
+ let childCollapsed = (parentState || this._collapsed) ? true : false;
13834
+
13693
13835
  if(!objMap) {
13694
13836
  objMap = {};
13695
13837
  }
13696
- let dirty = false;
13697
- if(this._subSegLevel) { // Sub segments are also subjected to collapsing
13698
- if(parentState) {
13699
- objMap[this._rid] = true;
13838
+ for(let i = 0; i < childCount; ++i) {
13839
+ let rid = "";
13840
+ let segment = null;
13841
+ if(subSegment) {
13842
+ segment = segmentMap[childList[i]]; // Use segment name to retrieve subsegment
13843
+ rid = segment.getId();
13844
+ } else {
13845
+ rid = childList[i];
13846
+ segment = segmentMap[rid]; // Use row id to retrieve segment from shared map
13847
+ }
13848
+ if(childCollapsed) {
13849
+ objMap[rid] = childCollapsed; // Collapsing states for all children is registered here
13700
13850
  dirty = true;
13701
13851
  }
13702
- }
13703
- if(this._childCount) {
13704
- let collapsed = parentState || this._collapsed;
13705
- if(segmentNames) {
13706
- let segmentMap = this._subSegMap;
13707
- let segmentCount = segmentNames.length;
13708
- for(let i = 0; i < segmentCount; ++i) {
13709
- let segment = segmentMap[segmentNames[i]];
13710
- objMap[segment.getId()] = !!parentState;
13711
- if(segment.getCollapsingStates(objMap, collapsed)) {
13712
- dirty = true;
13713
- }
13714
- }
13715
- } else if(collapsed) {
13716
- let chdr = this._children;
13717
- for(let rid in chdr) {
13718
- objMap[rid] = collapsed;
13719
- }
13852
+ if(segment && segment.getCollapsingStates(objMap, childCollapsed)) {
13720
13853
  dirty = true;
13721
13854
  }
13722
13855
  }
13723
13856
  return dirty;
13724
13857
  };
13725
13858
 
13859
+ /** @private
13860
+ * @return {number}
13861
+ */
13862
+ Segment.prototype._getOrder = function() {
13863
+ return this._order * 10000;
13864
+ };
13865
+ /** @private
13866
+ * @return {number}
13867
+ */
13868
+ Segment.prototype._getLastOrder = function() {
13869
+ return this._getOrder() + this._offsetOrder;
13870
+ };
13726
13871
  /** @public
13727
13872
  * @return {number}
13728
13873
  */
13729
13874
  Segment.prototype.getOrder = function() {
13730
- if(this._subSegLevel) {
13731
- let ancestor = this.getFirstAncestor();
13732
- if(ancestor) {
13733
- // WARNING: this._order cannot be greater than 9999
13734
- return ancestor.getOrder() + this._order;
13735
- }
13875
+ let ancestor = this.getFirstAncestor();
13876
+ if(ancestor) {
13877
+ // WARNING: this._order cannot be greater than 9999
13878
+ return ancestor._getOrder() + this._order;
13736
13879
  }
13737
- return this._order * 10000;
13880
+ return this._getOrder();
13738
13881
  };
13739
13882
  /** Get the last (highest) order from the entire tree regardless of the current position segment in the hierachy
13740
13883
  * @public
13741
13884
  * @return {number}
13742
13885
  */
13743
13886
  Segment.prototype.getLastOrder = function() {
13744
- if(this._subSegDef) {
13745
- let subSegments = this._subSegDef.subSegments;
13746
- if(subSegments) {
13747
- let lastSegment = subSegments[subSegments.length - 1];
13748
- if(lastSegment) {
13749
- return lastSegment.getOrder();
13750
- }
13751
- }
13887
+ let ancestor = this.getFirstAncestor();
13888
+ if(ancestor) {
13889
+ // WARNING: this._order cannot be greater than 9999
13890
+ return ancestor._getLastOrder();
13752
13891
  }
13753
- return this.getOrder();
13892
+ return this._getLastOrder();
13754
13893
  };
13755
13894
  /** @public
13756
13895
  * @param {number} val
@@ -13758,6 +13897,13 @@ Segment.prototype.getLastOrder = function() {
13758
13897
  Segment.prototype.setOrder = function(val) {
13759
13898
  this._order = val;
13760
13899
  };
13900
+ /** @public
13901
+ * @param {number} val
13902
+ * @returns {number} Returns the number set
13903
+ */
13904
+ Segment.prototype.setLastOrder = function(val) {
13905
+ return (this._offsetOrder = val);
13906
+ };
13761
13907
 
13762
13908
  /** @private
13763
13909
  * @type {Array.<string>}
@@ -13765,9 +13911,10 @@ Segment.prototype.setOrder = function(val) {
13765
13911
  Segment._tabs = null;
13766
13912
  /** @public
13767
13913
  * @param {Array.<string>=} lines
13914
+ * @param {number=} tabLevel
13768
13915
  * @return {!Array.<string>} lines
13769
13916
  */
13770
- Segment.prototype.log = function(lines) {
13917
+ Segment.prototype.log = function(lines, tabLevel) {
13771
13918
  if(!lines) {
13772
13919
  lines = [];
13773
13920
  }
@@ -13782,23 +13929,43 @@ Segment.prototype.log = function(lines) {
13782
13929
  tabCh += " ";
13783
13930
  }
13784
13931
  }
13932
+ if(!tabLevel) {
13933
+ tabLevel = 0;
13934
+ }
13935
+
13785
13936
  let collapsedCh = this._collapsed ? "+ " : "- ";
13786
- lines.push(tabs[this._subSegLevel] + collapsedCh + this._rid);
13937
+ lines.push(tabs[tabLevel] + collapsedCh + this._rid);
13787
13938
 
13788
- let segmentNames = this._subSegNames;
13789
- if(segmentNames) {
13790
- let segmentCount = segmentNames.length;
13791
- let segmentMap = this._subSegMap;
13792
- for(i = 0; i < segmentCount; ++i) {
13793
- segmentMap[segmentNames[i]].log(lines);
13939
+ let childLevel = tabLevel + 1;
13940
+ let childIndent = tabs[childLevel];
13941
+
13942
+ let childList = this._subSegNames;
13943
+ let segmentMap = this._subSegMap;
13944
+ let subSegment = true; // Normal segments can have subsegments without being subsegment themselve
13945
+ if(!childList && this._shared) { // Ensure that segment has not been disposed
13946
+ childList = this._childIds;
13947
+ segmentMap = this._shared.segments;
13948
+ subSegment = false;
13949
+ }
13950
+ let childCount = childList ? childList.length : 0;
13951
+
13952
+ for(i = 0; i < childCount; ++i) {
13953
+ let rid = "";
13954
+ let segment = null;
13955
+ if(subSegment) {
13956
+ segment = segmentMap[childList[i]]; // Use segment name to retrieve subsegment
13957
+ rid = segment.getId();
13958
+ } else {
13959
+ rid = childList[i];
13960
+ segment = segmentMap[rid]; // Use row id to retrieve segment from shared map
13794
13961
  }
13795
- } else if(this._childCount) {
13796
- let indent = tabs[this._subSegLevel + 1];
13797
- for(let rid in this._children) {
13798
- lines.push(indent + "- " + rid);
13962
+
13963
+ if(segment) {
13964
+ segment.log(lines, childLevel);
13965
+ } else {
13966
+ lines.push(childIndent + "- " + rid);
13799
13967
  }
13800
13968
  }
13801
-
13802
13969
  return lines;
13803
13970
  };
13804
13971
 
@@ -13823,6 +13990,7 @@ let SegmentCollection = function() {
13823
13990
  this._removalList = [];
13824
13991
 
13825
13992
  this._shared = {
13993
+ segments: this._segments,
13826
13994
  childToSegment: {}, // child Id to segment Id
13827
13995
  dirtyCollapsingState: false,
13828
13996
  defaultCollapsing: false
@@ -13883,10 +14051,6 @@ SegmentCollection.prototype.dispose = function() {
13883
14051
  */
13884
14052
  SegmentCollection.prototype.addSegment = function(rid, childRids) {
13885
14053
  if(rid && !this._segments[rid]) {
13886
- if(this.getParentRowId(rid)) {
13887
- console.log("child of a segment cannot be set as a segment separator");
13888
- return false;
13889
- }
13890
14054
  let segment = this._segments[rid] = new data_Segment(rid, this._shared);
13891
14055
  segment.addEventListener("subSegmentAdded", this._onSubSegmentAdded);
13892
14056
  segment.addEventListener("subSegmentRemoved", this._onSubSegmentRemoved);
@@ -13933,33 +14097,41 @@ SegmentCollection.prototype.getParentRowId = function(rid) {
13933
14097
  */
13934
14098
  SegmentCollection.prototype.removeSegment = function(rid) {
13935
14099
  let segment = this._segments[rid];
13936
- if(segment) {
13937
- if(this._segmentCount <= 1) {
13938
- return this.removeAllSegments();
13939
- }
13940
- if(segment.isSubSegment()) {
13941
- this._removalList.push(segment.getId());
13942
- }
13943
- let subSegIds = segment.getSubSegmentIds();
13944
- if(subSegIds) {
13945
- let len = subSegIds.length;
13946
- for(let i = 0; i < len; ++i) {
13947
- let subSegId = subSegIds[i];
13948
- if(this._segments[subSegId]) {
13949
- this._removalList.push(subSegId);
13950
- delete this._segments[subSegId]; // Slow
13951
- --this._segmentCount;
13952
- }
14100
+ if(!segment) {
14101
+ return false;
14102
+ }
14103
+
14104
+ if(this._segmentCount <= 1) {
14105
+ return this.removeAllSegments();
14106
+ }
14107
+ let subSegment = segment.isSubSegment();
14108
+ if(subSegment) {
14109
+ this._removalList.push(segment.getId());
14110
+ }
14111
+ let subSegIds = segment.getSubSegmentIds();
14112
+ if(subSegIds) {
14113
+ let len = subSegIds.length;
14114
+ for(let i = 0; i < len; ++i) {
14115
+ let subSegId = subSegIds[i];
14116
+ if(this._segments[subSegId]) {
14117
+ this._removalList.push(subSegId);
14118
+ delete this._segments[subSegId]; // Slow
14119
+ --this._segmentCount;
13953
14120
  }
13954
14121
  }
13955
- segment.removeAllChildren(); // This is important for updating childToSegment
13956
- segment.dispose();
13957
-
13958
- delete this._segments[rid]; // Slow
13959
- --this._segmentCount;
13960
- return true;
13961
14122
  }
13962
- return false;
14123
+ if(!subSegment) {
14124
+ let parentSeg = segment.getParent();
14125
+ if(parentSeg) { // Move existing children to its parent
14126
+ parentSeg.addChildren(segment.getChildIds()); // WARNING: passing private member
14127
+ }
14128
+ }
14129
+ segment.removeAllChildren(); // This is important for updating childToSegment
14130
+ segment.dispose();
14131
+
14132
+ delete this._segments[rid]; // Slow
14133
+ --this._segmentCount;
14134
+ return true;
13963
14135
  };
13964
14136
  /** @public
13965
14137
  * @return {boolean} Returns true if there is any change. Otherwise, returns false
@@ -13972,6 +14144,7 @@ SegmentCollection.prototype.removeAllSegments = function() {
13972
14144
  this._segments = {};
13973
14145
  this._segmentCount = 0;
13974
14146
  this._segmentList = null;
14147
+ this._shared.segments = this._segments;
13975
14148
  this._shared.childToSegment = {};
13976
14149
 
13977
14150
  this._classification = this._classifierChanged = false;
@@ -14097,7 +14270,7 @@ SegmentCollection.prototype.getCollapsedRows = function() {
14097
14270
  collapsedRids = {};
14098
14271
  for(let rid in segmentSeparators) {
14099
14272
  let segment = segmentSeparators[rid];
14100
- if(!segment.isSubSegment()) {
14273
+ if(segment.isRootSegment()) {
14101
14274
  if(segment.getCollapsingStates(collapsedRids)) {
14102
14275
  ++count;
14103
14276
  }
@@ -14109,6 +14282,31 @@ SegmentCollection.prototype.getCollapsedRows = function() {
14109
14282
  return this._collapsedRids;
14110
14283
  };
14111
14284
 
14285
+ /** Invalidate segment order cache, if the given row id is a segment separator
14286
+ * @private
14287
+ * @param {string|Array.<string>} segmentIds
14288
+ * @returns {boolean} Returns true if there is any change
14289
+ */
14290
+ SegmentCollection.prototype._invalidateSegmentOrder = function(segmentIds) {
14291
+ if(this._segmentList) {
14292
+ if(typeof segmentIds === "string") {
14293
+ if(this._segments[segmentIds]) {
14294
+ this._segmentList = null;
14295
+ return true;
14296
+ }
14297
+ } else if(Array.isArray(segmentIds)) {
14298
+ let len = segmentIds.length;
14299
+ for(let i = 0; i < len; ++i) {
14300
+ let segmentId = segmentIds[i];
14301
+ if(this._segments[segmentId]) {
14302
+ this._segmentList = null;
14303
+ return true;
14304
+ }
14305
+ }
14306
+ }
14307
+ }
14308
+ return false;
14309
+ };
14112
14310
  /** @public
14113
14311
  * @param {string} segmentId
14114
14312
  * @param {string} rid
@@ -14118,6 +14316,8 @@ SegmentCollection.prototype.getCollapsedRows = function() {
14118
14316
  SegmentCollection.prototype.addSegmentChild = function(segmentId, rid, dataId) {
14119
14317
  let segment = this._segments[segmentId];
14120
14318
  if(segment && !segment.isSubSegment()) {
14319
+ // If a segment becomes a child of other segment, then the segment order needs to be recalculated
14320
+ this._invalidateSegmentOrder(rid);
14121
14321
  return segment.addChild(rid, dataId);
14122
14322
  }
14123
14323
  return false;
@@ -14131,11 +14331,14 @@ SegmentCollection.prototype.addSegmentChild = function(segmentId, rid, dataId) {
14131
14331
  SegmentCollection.prototype.addSegmentChildren = function(segmentId, rids, dataIds) {
14132
14332
  let segment = this._segments[segmentId];
14133
14333
  if(segment && !segment.isSubSegment()) {
14334
+ // If a segment becomes a child of other segment, then the segment order needs to be recalculated
14335
+ this._invalidateSegmentOrder(rids);
14134
14336
  return segment.addChildren(rids, dataIds);
14135
14337
  }
14136
14338
  return false;
14137
14339
  };
14138
- /** @public
14340
+ /** This only works for immediate children of the specified segment
14341
+ * @public
14139
14342
  * @param {string} segmentId
14140
14343
  * @param {string} rid
14141
14344
  * @return {boolean} Returns true if there is any change. Otherwise, returns false
@@ -14155,6 +14358,7 @@ SegmentCollection.prototype.containsSegmentChild = function(segmentId, rid) {
14155
14358
  SegmentCollection.prototype.removeSegmentChild = function(segmentId, rid) {
14156
14359
  let segment = this._segments[segmentId];
14157
14360
  if(segment) {
14361
+ this._invalidateSegmentOrder(rid);
14158
14362
  return segment.removeChild(rid);
14159
14363
  }
14160
14364
  return false;
@@ -14167,6 +14371,7 @@ SegmentCollection.prototype.removeSegmentChild = function(segmentId, rid) {
14167
14371
  SegmentCollection.prototype.removeSegmentChildren = function(segmentId, rids) {
14168
14372
  let segment = this._segments[segmentId];
14169
14373
  if(segment) {
14374
+ this._invalidateSegmentOrder(rids);
14170
14375
  return segment.removeChildren(rids);
14171
14376
  }
14172
14377
  return false;
@@ -14186,6 +14391,7 @@ SegmentCollection.prototype.removeAllSegmentChildren = function() {
14186
14391
  }
14187
14392
 
14188
14393
  if(dirty) {
14394
+ this._segmentList = null; // WARNING: not optimized
14189
14395
  this.classify(null);
14190
14396
  }
14191
14397
 
@@ -14244,10 +14450,14 @@ SegmentCollection.prototype.fillSegments = function(rids) {
14244
14450
  };
14245
14451
  /** @public
14246
14452
  * @param {Array.<string>} rids
14453
+ * @param {boolean=} useCache=false If this is true, skip the calculation when there is already a cache for segment order
14247
14454
  */
14248
- SegmentCollection.prototype.calcSegmentOrder = function(rids) {
14455
+ SegmentCollection.prototype.calcSegmentOrder = function(rids, useCache) {
14249
14456
  let segmentList = this._segmentList;
14250
14457
  if(segmentList) {
14458
+ if(useCache) {
14459
+ return; // Use previous cache for segment order
14460
+ }
14251
14461
  segmentList.length = 0;
14252
14462
  } else {
14253
14463
  segmentList = this._segmentList = [];
@@ -14261,9 +14471,10 @@ SegmentCollection.prototype.calcSegmentOrder = function(rids) {
14261
14471
  let rid = rids[i];
14262
14472
  let segment = segmentSeparators[rid];
14263
14473
  if(segment) {
14264
- if(!segment.isSubSegment()) {
14474
+ if(segment.isRootSegment()) {
14265
14475
  this._segmentList.push(segment);
14266
14476
  segment.setOrder(++order); // WARNING: Segments and sub segments start with 1
14477
+ segment.updateTreeStructure(0);
14267
14478
  }
14268
14479
  if(--segmentCount <= 0) {
14269
14480
  break;
@@ -14287,7 +14498,7 @@ SegmentCollection.prototype.getSegmentValues = function(rids, partial) {
14287
14498
  let prevSegment = null;
14288
14499
  let segmentValues = new Array(rowCount);
14289
14500
  let segmentVal = 0;
14290
- let highestVal = -10;
14501
+ let highestVal = 0;
14291
14502
  let offset = 0;
14292
14503
  for(let r = 0; r < rowCount; ++r) {
14293
14504
  let rid = rids[r];
@@ -14295,7 +14506,7 @@ SegmentCollection.prototype.getSegmentValues = function(rids, partial) {
14295
14506
  if(curSegment) { // segment separator
14296
14507
  segmentVal = curSegment.getOrder() * 100;
14297
14508
  offset = 0;
14298
- if(!curSegment.isSubSegment()) {
14509
+ if(curSegment.isRootSegment()) {
14299
14510
  if(prevSegment !== curSegment) {
14300
14511
  prevSegment = curSegment;
14301
14512
  highestVal = curSegment.getLastOrder() * 100;
@@ -14307,12 +14518,14 @@ SegmentCollection.prototype.getSegmentValues = function(rids, partial) {
14307
14518
  curSegment = segmentSeparators[parentId];
14308
14519
  segmentVal = curSegment.getOrder() * 100;
14309
14520
  offset = 1;
14310
- if(partial) {
14521
+ if(partial) { // This fixes the out of order sub segment
14311
14522
  highestVal = curSegment.getLastOrder() * 100;
14312
14523
  }
14313
14524
  } else { // row outside of segments
14314
14525
  if(highestVal) {
14315
- segmentVal = highestVal;
14526
+ if(segmentVal < highestVal) {
14527
+ segmentVal = highestVal;
14528
+ }
14316
14529
  offset = 10;
14317
14530
  } else {
14318
14531
  segmentVal = offset = 0;
@@ -14336,7 +14549,10 @@ SegmentCollection.prototype.logStructure = function() {
14336
14549
  let segmentCount = segmentList.length;
14337
14550
  let lines = [];
14338
14551
  for(let i = 0; i < segmentCount; ++i) {
14339
- segmentList[i].log(lines);
14552
+ let segment = segmentList[i];
14553
+ if(segment.isRootSegment()) {
14554
+ segment.log(lines);
14555
+ }
14340
14556
  }
14341
14557
 
14342
14558
  return lines.join("\n");
@@ -14346,10 +14562,14 @@ SegmentCollection.prototype.logStructure = function() {
14346
14562
  */
14347
14563
  SegmentCollection.prototype.logRowIdMap = function() {
14348
14564
  let lines = [];
14565
+ let segmentSeparators = this._segments;
14349
14566
  let childToSegmentId = this._shared.childToSegment;
14350
14567
  for(let rid in childToSegmentId) {
14351
14568
  let segmentId = childToSegmentId[rid];
14352
- lines.push(rid + " > " + segmentId);
14569
+ let segment = segmentSeparators[segmentId];
14570
+ if(!segment || !segment.isSubSegment()) {
14571
+ lines.push(rid + " > " + segmentId);
14572
+ }
14353
14573
  }
14354
14574
 
14355
14575
  return lines.join("\n");
@@ -15481,64 +15701,65 @@ DataTable.prototype._initSegmentCollection = function() {
15481
15701
  this._segments.addEventListener("subSegmentChanged", this._onSubSegmentChanged);
15482
15702
  }
15483
15703
  };
15484
- /**
15704
+ /** Add or remove all segments from the given row ids
15485
15705
  * @public
15486
15706
  * @param {Array.<string>} rids
15487
15707
  * @param {boolean=} enabled
15488
15708
  * @return {boolean} Return true if there is any change
15489
15709
  */
15490
15710
  DataTable.prototype.setSegmentSeparators = function(rids, enabled) {
15491
- let change = false;
15492
- if (rids) {
15493
- let len = rids.length;
15494
- let segmentChanged = false;
15495
- for (let i = 0; i < len; i++) {
15496
- if(enabled !== false) {
15497
- let rid = rids[i];
15498
- this._initSegmentCollection();
15499
- if(this._autoSegmentFilling) {
15500
- let parentId = this._segments.getParentRowId(rid);
15501
- if(parentId) {
15502
- this._segments.removeSegmentChild(parentId, rid);
15503
- }
15711
+ if (!rids) {
15712
+ return false;
15713
+ }
15714
+ let adding = (enabled !== false);
15715
+ let segmentAdded = 0;
15716
+ let segmentRemoved = 0;
15717
+
15718
+ let len = rids.length;
15719
+ for (let i = 0; i < len; i++) {
15720
+ let rid = rids[i];
15721
+ if(adding) {
15722
+ this._initSegmentCollection();
15723
+ if(this._autoSegmentFilling) {
15724
+ let parentId = this._segments.getParentRowId(rid);
15725
+ if(parentId) {
15726
+ this._segments.removeSegmentChild(parentId, rid);
15504
15727
  }
15505
- segmentChanged = this._segments.addSegment(rid);
15506
- } else if (this._segments) { // remove case
15507
- let segment = this._segments.getSegment(rid);
15508
- if(segment) {
15509
- if(this._segments.removeSegment(rid)) {
15510
- change = true;
15511
- if(!this._segments.getSegmentCount()) {
15512
- this._segments = null;
15513
- }
15728
+ }
15729
+ segmentAdded |= this._segments.addSegment(rid);
15730
+ } else if (this._segments) { // Removing
15731
+ let segment = this._segments.getSegment(rid);
15732
+ if(segment) {
15733
+ if(this._segments.removeSegment(rid)) {
15734
+ segmentRemoved = 1;
15735
+ if(!this._segments.getSegmentCount()) {
15736
+ this._segments = null;
15514
15737
  }
15515
15738
  }
15516
15739
  }
15517
-
15518
- }
15519
- if (enabled !== false && segmentChanged) {
15520
- this._segments.calcSegmentOrder(this._rids);
15521
- change = true;
15522
- }
15523
- if(change) {
15524
- this.dispatchGlobalChange();
15525
15740
  }
15526
15741
  }
15527
- return change;
15528
-
15742
+ if (segmentAdded) {
15743
+ this._segments.calcSegmentOrder(this._rids);
15744
+ }
15745
+ let changed = segmentAdded || segmentRemoved;
15746
+ if(changed && this._needFiring()) {
15747
+ this.dispatchGlobalChange();
15748
+ }
15749
+ return changed ? true : false;
15529
15750
  };
15530
15751
 
15531
15752
  /**
15532
15753
  * @public
15533
15754
  * @param {string} rid
15534
- * @param {boolean=} enabled
15755
+ * @param {*=} options=null Segment options. If the value is false, segment separator will be stripped off from the given rid
15535
15756
  * @return {boolean} Return true if there is any change
15536
15757
  */
15537
- DataTable.prototype.setSegmentSeparator = function(rid, enabled) {
15758
+ DataTable.prototype.setSegmentSeparator = function(rid, options) {
15538
15759
  let change = false;
15539
15760
  let memberCount = 0;
15540
15761
  if(rid && typeof rid === "string") {
15541
- if(enabled !== false) {
15762
+ if(options !== false) {
15542
15763
  this._initSegmentCollection();
15543
15764
  if(this._autoSegmentFilling) {
15544
15765
  let parentId = this._segments.getParentRowId(rid);
@@ -15546,11 +15767,12 @@ DataTable.prototype.setSegmentSeparator = function(rid, enabled) {
15546
15767
  this._segments.removeSegmentChild(parentId, rid);
15547
15768
  }
15548
15769
  }
15549
- if(this._segments.addSegment(rid)) {
15770
+ let children = (options && options["children"]) ? options["children"] : null;
15771
+ if(this._segments.addSegment(rid, children)) {
15550
15772
  this._segments.calcSegmentOrder(this._rids);
15551
15773
  change = true;
15552
15774
  }
15553
- } else if(this._segments) { // mean remove separator
15775
+ } else if(this._segments) { // Remove the separator
15554
15776
  let segment = this._segments.getSegment(rid);
15555
15777
  if(segment) {
15556
15778
  memberCount = segment.getChildCount();
@@ -15745,6 +15967,20 @@ DataTable.prototype.fillSegments = function() {
15745
15967
  }
15746
15968
  return false;
15747
15969
  };
15970
+ /** @private
15971
+ * @param {boolean=} adding This indicates that segment is changed by adding a new child
15972
+ */
15973
+ DataTable.prototype._onSegmentChildChanged = function(adding) {
15974
+ if(this._segments) {
15975
+ this._segments.calcSegmentOrder(this._rids, true);
15976
+ }
15977
+ if(adding !== false) {
15978
+ this._sort(null);
15979
+ }
15980
+ this._dispatchPositionChange(); // Force rerendering, even if there is no position change
15981
+
15982
+ this.requestClassifying();
15983
+ };
15748
15984
  /** @public
15749
15985
  * @param {string} segmentId Row id
15750
15986
  * @param {string} rid Row id
@@ -15755,10 +15991,7 @@ DataTable.prototype.addSegmentChild = function(segmentId, rid, dataId) {
15755
15991
  if(this._segments) {
15756
15992
  let dirty = this._segments.addSegmentChild(segmentId, rid, dataId);
15757
15993
  if(dirty) {
15758
- this._sort(null);
15759
- this._dispatchPositionChange(); // Force rerendering, even if there is no position change
15760
-
15761
- this.requestClassifying();
15994
+ this._onSegmentChildChanged();
15762
15995
  return true;
15763
15996
  }
15764
15997
  }
@@ -15774,17 +16007,15 @@ DataTable.prototype.addSegmentChildren = function(segmentId, rids, dataIds) {
15774
16007
  if(this._segments) {
15775
16008
  let dirty = this._segments.addSegmentChildren(segmentId, rids, dataIds);
15776
16009
  if(dirty) {
15777
- this._sort(null);
15778
- this._dispatchPositionChange(); // Force rerendering, even if there is no position change
15779
-
15780
- this.requestClassifying();
16010
+ this._onSegmentChildChanged();
15781
16011
  return true;
15782
16012
  }
15783
16013
  }
15784
16014
  return false;
15785
16015
  };
15786
16016
 
15787
- /** @public
16017
+ /** Deprecated. Remove all existing children and add new children according to the give array
16018
+ * @public
15788
16019
  * @param {Array.<Object>} segmentArr Segment array that contain "segmentId", "rowIds" to set segment children
15789
16020
  * @return {boolean} Return true if there is any change
15790
16021
  */
@@ -15792,20 +16023,16 @@ DataTable.prototype.setSegmentChildren = function(segmentArr) {
15792
16023
  if(!this._segments) {
15793
16024
  return false;
15794
16025
  }
15795
- this.removeAllSegmentChildren();
16026
+ let dirty = this._segments.removeAllSegmentChildren();
15796
16027
  let len = segmentArr.length;
15797
- let dirty;
15798
16028
  for (let i = 0; i < len; i++) {
15799
16029
  let obj = segmentArr[i];
15800
- if(this._segments.addSegmentChildren(obj.segmentId, obj.rowIds)) {
16030
+ if(this._segments.addSegmentChildren(obj["segmentId"], obj["rowIds"])) {
15801
16031
  dirty = true;
15802
16032
  }
15803
16033
  }
15804
16034
  if(dirty) {
15805
- this._sort(null);
15806
- this._dispatchPositionChange(); // Force rerendering, even if there is no position change
15807
-
15808
- this.requestClassifying();
16035
+ this._onSegmentChildChanged();
15809
16036
  return true;
15810
16037
  }
15811
16038
 
@@ -15820,8 +16047,7 @@ DataTable.prototype.removeSegmentChild = function(segmentId, rid) {
15820
16047
  if(this._segments) {
15821
16048
  let dirty = this._segments.removeSegmentChild(segmentId, rid);
15822
16049
  if(dirty) {
15823
- this.dispatchGlobalChange();
15824
- this.requestClassifying();
16050
+ this._onSegmentChildChanged(false);
15825
16051
  }
15826
16052
  return dirty;
15827
16053
  }
@@ -15836,8 +16062,7 @@ DataTable.prototype.removeSegmentChildren = function(segmentId, rids) {
15836
16062
  if(this._segments) {
15837
16063
  let dirty = this._segments.removeSegmentChildren(segmentId, rids);
15838
16064
  if(dirty) {
15839
- this.dispatchGlobalChange();
15840
- this.requestClassifying();
16065
+ this._onSegmentChildChanged(false);
15841
16066
  }
15842
16067
  return dirty;
15843
16068
  }
@@ -15850,7 +16075,7 @@ DataTable.prototype.removeAllSegmentChildren = function() {
15850
16075
  if(this._segments) {
15851
16076
  let dirty = this._segments.removeAllSegmentChildren(); // This immediately remove all sub segments
15852
16077
  if (dirty) {
15853
- this.dispatchGlobalChange();
16078
+ this._onSegmentChildChanged(false);
15854
16079
  }
15855
16080
  return dirty;
15856
16081
  }
@@ -21053,13 +21278,13 @@ DataView.prototype.setSegmentSeparators = function(rowIds, enabled) {
21053
21278
  }
21054
21279
  return false;
21055
21280
  };
21056
- /** Set visible row as segment separator (hidden or filtered rows cannot be a segment separator)
21281
+ /** Set visible row as a segment separator (hidden or filtered rows cannot be a segment separator)
21057
21282
  * @public
21058
21283
  * @param {string|number} rowRef Row id or row index
21059
- * @param {boolean=} enabled
21284
+ * @param {*=} options=null Segment options. If the value is false, segment separator will be stripped off from the given rid
21060
21285
  * @return {boolean} Return true if there is any change
21061
21286
  */
21062
- DataView.prototype.setSegmentSeparator = function(rowRef, enabled) {
21287
+ DataView.prototype.setSegmentSeparator = function(rowRef, options) {
21063
21288
  let rowId = "";
21064
21289
  if(typeof rowRef === "number") {
21065
21290
  rowId = this.getRowId(rowRef);
@@ -21068,12 +21293,11 @@ DataView.prototype.setSegmentSeparator = function(rowRef, enabled) {
21068
21293
  }
21069
21294
 
21070
21295
  if(rowId) {
21071
- enabled = enabled !== false;
21072
- if(enabled) {
21296
+ if(options !== false) { // undefined, null, object, or true value
21073
21297
  this.synchronizeRowOrder();
21074
21298
  }
21075
21299
  // TODO: Force expanding of segment before unsetting segment separator
21076
- return this._dt.setSegmentSeparator(rowId, enabled);
21300
+ return this._dt.setSegmentSeparator(rowId, options);
21077
21301
  }
21078
21302
  return false;
21079
21303
  };
@@ -25964,7 +26188,7 @@ Core_Core.prototype._hasPendingRowChange = false;
25964
26188
  * @return {string}
25965
26189
  */
25966
26190
  Core_Core.getVersion = function () {
25967
- return "5.1.137";
26191
+ return "5.1.139";
25968
26192
  };
25969
26193
  /** {@link ElementWrapper#dispose}
25970
26194
  * @override