@refinitiv-ui/efx-grid 6.0.147 → 6.0.149

Sign up to get free protection for your applications and to get access to all the features.
@@ -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