@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.
@@ -8,7 +8,7 @@ import EventDispatcher from "../../../tr-grid-util/es6/EventDispatcher.js";
8
8
  */
9
9
  let Segment = function(rid, sharedObj) {
10
10
  this._rid = rid;
11
- this._children = {};
11
+ this._childIds = [];
12
12
  this._shared = sharedObj;
13
13
  if(sharedObj.defaultCollapsing) {
14
14
  this._collapsed = true;
@@ -39,14 +39,6 @@ Segment._subSegSortLogic = function(a, b) {
39
39
 
40
40
  return 0;
41
41
  };
42
- /** @private
43
- * @function
44
- * @param {Segment} segment
45
- * @param {number} idx
46
- */
47
- Segment._assignSubSegmentOrder = function(segment, idx) {
48
- segment.setOrder(idx + 1);
49
- };
50
42
 
51
43
  /** @type {Object}
52
44
  * @private
@@ -57,14 +49,14 @@ Segment.prototype._shared = null;
57
49
  * @private
58
50
  */
59
51
  Segment.prototype._rid;
60
- /** @type {!Object}
52
+ /** @type {!Array.<string>}
61
53
  * @private
62
54
  */
63
- Segment.prototype._children;
64
- /** @type {number}
55
+ Segment.prototype._childIds;
56
+ /** @type {Object}
65
57
  * @private
66
58
  */
67
- Segment.prototype._childCount = 0;
59
+ Segment.prototype._childDataIds = null;
68
60
  /** @type {boolean}
69
61
  * @private
70
62
  */
@@ -73,6 +65,14 @@ Segment.prototype._collapsed = false;
73
65
  * @private
74
66
  */
75
67
  Segment.prototype._order = 0;
68
+ /** @type {number}
69
+ * @private
70
+ */
71
+ Segment.prototype._offsetOrder = 0;
72
+ /** @type {number}
73
+ * @private
74
+ */
75
+ Segment.prototype._depth = 0;
76
76
  /** @type {boolean}
77
77
  * @private
78
78
  */
@@ -82,10 +82,10 @@ Segment.prototype._disposed = false;
82
82
  * @private
83
83
  */
84
84
  Segment.prototype._subSegDef = null;
85
- /** @type {number}
85
+ /** @type {boolean}
86
86
  * @private
87
87
  */
88
- Segment.prototype._subSegLevel = 0;
88
+ Segment.prototype._subSegment = false; // This indicates that the segment is autogenerated (subsegment)
89
89
  /** @type {Object.<string, Segment>}
90
90
  * @private
91
91
  */
@@ -93,7 +93,7 @@ Segment.prototype._subSegMap = null; // For immediate sub-segment children
93
93
  /** @type {Array.<string>}
94
94
  * @private
95
95
  */
96
- Segment.prototype._subSegNames = null; // For immediate sub-segment child names
96
+ Segment.prototype._subSegNames = null; // For immediate sub-segment child names/ids
97
97
  /** @type {string}
98
98
  * @private
99
99
  */
@@ -102,10 +102,6 @@ Segment.prototype._subSegName = "";
102
102
  * @private
103
103
  */
104
104
  Segment.prototype._subSegVal;
105
- /** @type {Segment}
106
- * @private
107
- */
108
- Segment.prototype._subSegParent = null;
109
105
 
110
106
 
111
107
  /** @public
@@ -127,14 +123,13 @@ Segment.prototype.dispose = function() {
127
123
  this._subSegMap = this._subSegNames = null;
128
124
  }
129
125
  if(this._collapsed) {
130
- if(this._childCount || this._subSegDef) {
131
- this._shared.dirtyCollapsingState = true;
132
- }
126
+ this.markCollapsingStateDirty();
133
127
  }
134
128
 
135
- this._childCount = 0;
129
+ this._childDataIds = null;
130
+ this._childIds.length = 0;
136
131
  this._shared = null;
137
- this._subSegParent = this._subSegDef = this._subSegVal = null;
132
+ this._subSegDef = this._subSegVal = null;
138
133
  };
139
134
  /** @public
140
135
  * @return {string}
@@ -146,10 +141,17 @@ Segment.prototype.getId = function() {
146
141
  * @return {string}
147
142
  */
148
143
  Segment.prototype.getParentId = function() {
149
- if(this._subSegParent) {
150
- return this._subSegParent.getId();
144
+ return this._shared.childToSegment[this._rid] || "";
145
+ };
146
+ /** @public
147
+ * @return {Segment}
148
+ */
149
+ Segment.prototype.getParent = function() {
150
+ let parentId = this.getParentId();
151
+ if(parentId) {
152
+ return this._shared.segments[parentId] || null;
151
153
  }
152
- return "";
154
+ return null;
153
155
  };
154
156
  /** @public
155
157
  * @param {Array.<string>=} out_ary
@@ -177,22 +179,44 @@ Segment.prototype.getSubSegmentIds = function(out_ary) {
177
179
  /** @public
178
180
  * @param {string} rid
179
181
  * @param {string=} dataId Row id for retrieving data
180
- * @return {boolean}
182
+ * @return {boolean} Returns true only a new child is added
181
183
  */
182
184
  Segment.prototype.addChild = function(rid, dataId) {
183
- if(rid) {
185
+ if(!rid) {
186
+ return false;
187
+ }
188
+ let prevSegmentId = this._shared.childToSegment[rid];
189
+ if(prevSegmentId) {
190
+ if(prevSegmentId !== this._rid) {
191
+ let prevSegment = this._shared.segments[prevSegmentId];
192
+ if(prevSegment && !prevSegment.hasSubSegments()) { // Children of a classified segment always stick the root segment
193
+ prevSegment.removeChild(rid);
194
+ }
195
+ this._shared.childToSegment[rid] = this._rid;
196
+ }
197
+ } else {
184
198
  this._shared.childToSegment[rid] = this._rid;
185
- if(this._children[rid]) {
186
- this._children[rid] = dataId || rid; // Update data id
187
- } else {
188
- if(this._collapsed) {
189
- this._shared.dirtyCollapsingState = true; // TODO: Check if we need to update this only when new child is added
199
+ }
200
+
201
+ if(dataId != null) { // Update data id
202
+ if(dataId && dataId !== rid) {
203
+ if(!this._childDataIds) {
204
+ this._childDataIds = {};
190
205
  }
191
- this._children[rid] = dataId || rid;
192
- ++this._childCount;
193
- return true;
206
+ this._childDataIds[rid] = dataId;
207
+ } else if(this._childDataIds) {
208
+ delete this._childDataIds[rid];
194
209
  }
195
210
  }
211
+
212
+ let at = this._childIds.indexOf(rid);
213
+ if(at < 0) {
214
+ if(this._collapsed) {
215
+ this._shared.dirtyCollapsingState = true; // TODO: Check if we need to update this only when new child is added
216
+ }
217
+ this._childIds.push(rid);
218
+ return true;
219
+ }
196
220
  return false;
197
221
  };
198
222
  /** @public
@@ -201,8 +225,15 @@ Segment.prototype.addChild = function(rid, dataId) {
201
225
  * @return {boolean}
202
226
  */
203
227
  Segment.prototype.addChildren = function(rids, dataIds) {
228
+ if(!rids) {
229
+ return false;
230
+ }
204
231
  let rowIds = Array.isArray(rids) ? rids : [rids];
205
232
  let rowCount = rowIds.length;
233
+ if(!rowCount) {
234
+ return false;
235
+ }
236
+
206
237
  let dirty = 0;
207
238
  let i;
208
239
  if(dataIds != null) {
@@ -222,27 +253,35 @@ Segment.prototype.addChildren = function(rids, dataIds) {
222
253
  * @return {boolean}
223
254
  */
224
255
  Segment.prototype.containsChild = function(rid) {
225
- return this._children[rid] ? true : false;
256
+ return this._childIds.indexOf(rid) >= 0;
257
+ };
258
+ /** @public
259
+ * @param {string} rid
260
+ * @return {number}
261
+ */
262
+ Segment.prototype.getChildIndex = function(rid) {
263
+ return this._childIds.indexOf(rid);
226
264
  };
227
265
  /** @public
228
266
  * @param {string} rid
229
267
  * @return {boolean}
230
268
  */
231
269
  Segment.prototype.removeChild = function(rid) {
232
- if(this._subSegLevel) {
270
+ if(this._subSegment) {
233
271
  return false; // Sub segments are not allowed to remove its children
234
272
  }
235
- if(!this._childCount) {
273
+ if(!this._childIds.length) {
236
274
  return false;
237
275
  }
238
- if(!this._children[rid]) {
276
+ let at = this._childIds.indexOf(rid);
277
+ if(at < 0) {
239
278
  return false; // The specified rid is not a child of this segment
240
279
  }
241
280
 
242
- let objMap = this._shared.childToSegment;
243
- delete objMap[rid];
244
- delete this._children[rid]; // Slow
245
- --this._childCount;
281
+ if(this._shared.childToSegment[rid] === this._rid) {
282
+ delete this._shared.childToSegment[rid];
283
+ }
284
+ this._childIds.splice(at, 1); // Slow
246
285
 
247
286
  if(this._collapsed) {
248
287
  this._shared.dirtyCollapsingState = true;
@@ -254,10 +293,10 @@ Segment.prototype.removeChild = function(rid) {
254
293
  * @return {boolean}
255
294
  */
256
295
  Segment.prototype.removeChildren = function(rids) {
257
- if(this._subSegLevel) {
296
+ if(this._subSegment) {
258
297
  return false; // Sub segments are not allowed to remove its children
259
298
  }
260
- if(!this._childCount) {
299
+ if(!this._childIds.length) {
261
300
  return false;
262
301
  }
263
302
  let rowIds = Array.isArray(rids) ? rids : [rids];
@@ -272,21 +311,24 @@ Segment.prototype.removeChildren = function(rids) {
272
311
  * @return {boolean}
273
312
  */
274
313
  Segment.prototype.removeAllChildren = function() {
275
- if(this._subSegLevel) {
314
+ if(this._subSegment) {
276
315
  return false; // Sub segments are not allowed to remove its children
277
316
  }
278
- if(!this._childCount) {
317
+ let childCount = this._childIds.length;
318
+ if(!this._childIds.length) {
279
319
  return false;
280
320
  }
321
+ let segmentId = this._rid;
281
322
  let objMap = this._shared.childToSegment;
282
- let chdr = this._children;
283
- for(let rid in chdr) {
284
- if(objMap[rid]) {
285
- delete objMap[rid]; // TODO: Check if we need to do this
323
+ let chdr = this._childIds;
324
+ for(let i = 0; i < childCount; ++i) {
325
+ let rid = chdr[i];
326
+ if(objMap[rid] === segmentId) {
327
+ delete objMap[rid];
286
328
  }
287
329
  }
288
- this._children = {};
289
- this._childCount = 0;
330
+ this._childIds.length = 0;
331
+ this._childDataIds = null;
290
332
 
291
333
  if(this._collapsed) {
292
334
  this._shared.dirtyCollapsingState = true;
@@ -297,24 +339,41 @@ Segment.prototype.removeAllChildren = function() {
297
339
  * @return {!Array.<string>}
298
340
  */
299
341
  Segment.prototype.getChildIds = function() {
300
- return this._childCount ? Object.keys(this._children) : [];
342
+ return this._childIds; // WARNING: Returns private members
301
343
  };
302
344
  /** @public
303
345
  * @return {!Object}
304
346
  */
305
347
  Segment.prototype.getChildren = function() {
306
- return this._children;
348
+ let obj = {};
349
+ let chdr = this._childIds;
350
+ let childCount = chdr.length;
351
+ let dataIds = this._childDataIds || {};
352
+ for(let i = 0; i < childCount; ++i) {
353
+ let rid = chdr[i];
354
+ obj[rid] = dataIds[rid] || rid;
355
+ }
356
+ return obj;
307
357
  };
308
358
  /** @public
309
359
  * @return {number}
310
360
  */
311
361
  Segment.prototype.getChildCount = function() {
312
- return this._childCount;
362
+ return this._childIds.length;
363
+ };
364
+ /** When a segment is not empty, the visibility of its content need to be updated.
365
+ * @public
366
+ */
367
+ Segment.prototype.markCollapsingStateDirty = function() {
368
+ // A segment can have a child and/or autogenerated segment (subsegment) to not be considered as empty.
369
+ if(this._childIds.length || this._subSegDef) {
370
+ this._shared.dirtyCollapsingState = true;
371
+ }
313
372
  };
314
373
 
315
374
 
316
375
  /** @public
317
- * @return {Array.<string>} fields
376
+ * @return {Array.<string>}
318
377
  */
319
378
  Segment.prototype.getClassification = function() {
320
379
  if(this._subSegDef) {
@@ -327,8 +386,8 @@ Segment.prototype.getClassification = function() {
327
386
  * @return {boolean}
328
387
  */
329
388
  Segment.prototype.setClassification = function(fields) {
330
- if(this._subSegLevel) {
331
- return false; // non-root segment cannot be classified
389
+ if(this._subSegment) {
390
+ return false; // subsegment cannot be classified
332
391
  }
333
392
  let classifiers = null;
334
393
  if(this._subSegDef) {
@@ -371,9 +430,8 @@ Segment.prototype.setClassification = function(fields) {
371
430
  return true;
372
431
  } else if(classifiers) { // Remove existing ones
373
432
  this._subSegDef.classifiers = null;
374
- this._subSegDef.subSegments = null;
375
433
  // this._subSegDef.classifierChanged = true;
376
- this._subSegDef = null; // WARNING: All sub segments remain existing
434
+ this._subSegDef = null; // WARNING: All subsegments remain existing
377
435
  return true;
378
436
  }
379
437
  return false;
@@ -390,7 +448,7 @@ Segment.prototype.classify = function(rows) {
390
448
  let segmentCount = segmentNames ? segmentNames.length : 0;
391
449
 
392
450
  if(!segmentCount) {
393
- if(this._subSegLevel >= classifierCount) {
451
+ if(this._depth >= classifierCount) {
394
452
  return false; // Current segment level is beyond existing classification level and this segment should already be removed
395
453
  }
396
454
  }
@@ -400,7 +458,7 @@ Segment.prototype.classify = function(rows) {
400
458
  sharedObj.dirtyCollapsingState = true;
401
459
  }
402
460
 
403
- // Prepare existing sub segments for checking change in its members
461
+ // Prepare existing subsegments for checking change in its members
404
462
  let i;
405
463
  let segmentName = "";
406
464
  let nonExistenceGroups = {};
@@ -414,9 +472,8 @@ Segment.prototype.classify = function(rows) {
414
472
  nonExistenceGroups[segmentName] = 1;
415
473
 
416
474
  segment = segmentMap[segmentName];
417
- if(segment._childCount) { // Quick cleaning up
418
- segment._children = {};
419
- segment._childCount = 0;
475
+ if(segment._childIds.length) { // Quick cleaning up
476
+ segment._childIds.length = 0;
420
477
  }
421
478
  if(segment._collapsed) {
422
479
  sharedObj.dirtyCollapsingState = true;
@@ -424,20 +481,23 @@ Segment.prototype.classify = function(rows) {
424
481
  }
425
482
  }
426
483
 
427
- // Loop through row children and assign them to their corresponding sub segment
428
- let isRootSegment = !this._subSegLevel;
484
+ // Loop through row children and assign them to their corresponding subsegment
485
+ let rootSegment = !this._subSegment;
486
+ let dataIds = this._childDataIds || {};
429
487
  let rid;
430
- let children = this._children;
431
- if(this._subSegLevel < classifierCount && rows) {
488
+ let chdr = this._childIds;
489
+ let childCount = chdr.length;
490
+ if(this._depth < classifierCount && rows) {
432
491
  if(!segmentMap) {
433
492
  segmentMap = this._subSegMap = {};
434
493
  segmentNames = this._subSegNames = [];
435
494
  }
436
495
 
437
- let classifier = classifiers[this._subSegLevel];
496
+ let classifier = classifiers[this._depth];
438
497
 
439
- for(rid in children) {
440
- let dataId = children[rid];
498
+ for(i = 0; i < childCount; ++i) {
499
+ rid = chdr[i];
500
+ let dataId = dataIds[rid] || rid;
441
501
  let record = rows[dataId];
442
502
  let val = record ? record[classifier] : null; // WARNING: row could already be removed
443
503
 
@@ -454,31 +514,34 @@ Segment.prototype.classify = function(rows) {
454
514
 
455
515
  segment = segmentMap[segmentName];
456
516
  if(!segment) { // New group is detected
457
- segment = new Segment(this._rid + "/" + segmentName, sharedObj);
517
+ let subSegId = this._rid + "/" + segmentName;
518
+ segment = new Segment(subSegId, sharedObj);
458
519
  segment._subSegDef = this._subSegDef;
459
- segment._subSegLevel = this._subSegLevel + 1;
520
+ segment._subSegment = true; // this indicates that the segment is autogenerated (subsegment)
521
+ segment._depth = this._depth + 1;
460
522
  segment._subSegName = segmentName;
461
523
  segment._subSegVal = val;
462
- segment._subSegParent = this;
524
+ sharedObj.childToSegment[subSegId] = this._rid; // WARNING: this will mix autogenerated rows with actual rows
463
525
 
464
526
  segmentMap[segmentName] = segment;
465
527
  segmentNames.push(segmentName);
466
528
 
467
529
  this._dispatch("subSegmentAdded", {
468
- "rid": segment.getId(),
530
+ "rid": subSegId,
469
531
  "segment": segment
470
532
  });
471
533
  }
472
534
 
473
535
  segment.addChild(rid, dataId);
474
536
  }
475
- } else if(isRootSegment) { // In case of no classification
476
- for(rid in children) {
537
+ } else if(rootSegment) { // In case of no classification
538
+ for(i = 0; i < childCount; ++i) {
539
+ rid = chdr[i];
477
540
  sharedObj.childToSegment[rid] = this._rid; // Relocate child in case of it has been moved to a non existence group
478
541
  }
479
542
  }
480
543
 
481
- // Remove all sub segments with no members
544
+ // Remove all subsegments with no members
482
545
  if(removalCount > 0) {
483
546
  if(removalCount >= segmentNames.length) {
484
547
  segmentNames.length = 0;
@@ -506,7 +569,7 @@ Segment.prototype.classify = function(rows) {
506
569
  }
507
570
  }
508
571
 
509
- // Sort and classify existing sub segments
572
+ // Sort and classify existing subsegments
510
573
  segmentCount = segmentNames ? segmentNames.length : 0;
511
574
  if(segmentCount) {
512
575
  segmentNames.sort(Segment._subSegSortLogic);
@@ -516,22 +579,17 @@ Segment.prototype.classify = function(rows) {
516
579
  }
517
580
  }
518
581
 
519
- // Collecting all sub segments including all descendants and reassigning segment order.
520
- if(isRootSegment) { // If this is a root segment
521
- if(this._subSegDef) {
522
- if(segmentCount) {
523
- let subSegments = this._subSegDef.subSegments = [];
524
- this.getAllSubSegments(subSegments);
525
- subSegments.forEach(Segment._assignSubSegmentOrder);
526
- } else {
527
- this._subSegDef.subSegments = null;
528
- }
529
- // this._subSegDef.classifierChanged = false;
582
+ // Collecting all subsegments including all descendants and reassigning segment order.
583
+ if(rootSegment && this._subSegDef) {
584
+ if(segmentCount) {
585
+ this.calcSubSegmentOrder(0);
530
586
  }
587
+ // this._subSegDef.classifierChanged = false;
531
588
  }
532
589
  return true;
533
590
  };
534
- /** @public
591
+ /** SubSegment implies being classified
592
+ * @public
535
593
  * @return {boolean}
536
594
  */
537
595
  Segment.prototype.hasSubSegments = function() {
@@ -540,21 +598,37 @@ Segment.prototype.hasSubSegments = function() {
540
598
  }
541
599
  return false;
542
600
  };
543
- /** @public
601
+ /** SubSegment implies autogenerated segment
602
+ * @public
544
603
  * @return {boolean}
545
604
  */
546
605
  Segment.prototype.isSubSegment = function() {
547
- return this._subSegLevel ? true : false;
606
+ return this._subSegment;
607
+ };
608
+ /** @public
609
+ * @return {boolean}
610
+ */
611
+ Segment.prototype.isRootSegment = function() {
612
+ return this._shared.childToSegment[this._rid] ? false : true;
548
613
  };
549
614
  /** @public
550
615
  * @return {Segment}
551
616
  */
552
617
  Segment.prototype.getFirstAncestor = function() {
553
- if(this._subSegLevel && this._subSegDef) {
554
- let ancestor = this._subSegDef.root;
555
- return /** @type{Segment} */(ancestor) || null;
618
+ let ancestor = null;
619
+ if(this._subSegment && this._subSegDef) { // Quick way to get root
620
+ ancestor = this._subSegDef.root;
621
+ } else { // Slow
622
+ ancestor = this.getParent();
623
+ if(ancestor) {
624
+ let parentSeg = ancestor.getParent();
625
+ while(parentSeg) { // This could cause infinite loop
626
+ ancestor = parentSeg;
627
+ parentSeg = ancestor.getParent();
628
+ }
629
+ }
556
630
  }
557
- return null;
631
+ return ancestor || null;
558
632
  };
559
633
  /** @public
560
634
  * @param {Array.<Segment>=} out_ary
@@ -576,13 +650,75 @@ Segment.prototype.getAllSubSegments = function(out_ary) {
576
650
  }
577
651
  return out_ary || null;
578
652
  };
653
+ /** This method sets order, last order, and depth to entire tree structure in the segment, including the segment itself
654
+ * @public
655
+ * @param {number} counter
656
+ * @return {number}
657
+ */
658
+ Segment.prototype.updateTreeStructure = function(counter) {
659
+ if(!counter) {
660
+ counter = 0;
661
+ if(!this._subSegment) { // Subsegment's depth cannot be reset back to 0
662
+ this._depth = 0; // WARNING: this assumes counter at 0 is the root segment
663
+ }
664
+ }
665
+ if(this.hasSubSegments()) {
666
+ return this.setLastOrder(counter); // Sub segments has already been calculated
667
+ }
668
+ let segmentSeparators = this._shared.segments;
669
+ let childCount = this._childIds.length;
670
+ let prevSeg = null;
671
+ for(let i = 0; i < childCount; ++i) {
672
+ let rid = this._childIds[i];
673
+ let segment = segmentSeparators[rid];
674
+ let segmentId = (segment) ? segment.getId() : "Uncategorized";
675
+ if(prevSeg !== segmentId) {
676
+ ++counter; // 0 become 1
677
+ prevSeg = segmentId;
678
+ }
679
+ if(segment) {
680
+ segment._depth = this._depth + 1;
681
+ segment.setOrder(counter);
682
+ counter = segment.updateTreeStructure(counter);
683
+ }
684
+ }
685
+
686
+ return this.setLastOrder(counter);
687
+ };
688
+ /** @public
689
+ * @param {number} counter
690
+ * @return {number}
691
+ */
692
+ Segment.prototype.calcSubSegmentOrder = function(counter) {
693
+ if(!this.hasSubSegments()) {
694
+ return this.setLastOrder(counter);
695
+ }
696
+
697
+ let segmentMap = this._subSegMap;
698
+ let childCount = this._subSegNames.length;
699
+ let prevSeg = null;
700
+ for(let i = 0; i < childCount; ++i) {
701
+ let segmentName = this._subSegNames[i];
702
+ let segment = segmentMap[segmentName];
703
+ let segmentId = (segment) ? segment.getId() : "Uncategorized";
704
+ if(prevSeg !== segmentId) {
705
+ ++counter; // 0 become 1
706
+ prevSeg = segmentId;
707
+ }
708
+ if(segment) {
709
+ segment.setOrder(counter);
710
+ counter = segment.calcSubSegmentOrder(counter);
711
+ }
712
+ }
713
+ return this.setLastOrder(counter);
714
+ };
579
715
  /** @public
580
716
  * @return {number}
581
717
  */
582
718
  Segment.prototype.getSegmentLevel = function() {
583
- return this._subSegLevel;
719
+ return this._depth;
584
720
  };
585
- /** This method will be called on sub segments only
721
+ /** This method will be called on subsegments only
586
722
  * @public
587
723
  * @param {Object=} rows
588
724
  * @param {Object=} clsSource
@@ -607,7 +743,7 @@ Segment.prototype.setRowData = function(rows, clsSource) {
607
743
  let segment = this;
608
744
  while(segment && segment.isSubSegment()) {
609
745
  segment.getSubSegmentName(row);
610
- segment = segment._subSegParent;
746
+ segment = segment.getParent();
611
747
  }
612
748
  };
613
749
  /** @public
@@ -615,9 +751,9 @@ Segment.prototype.setRowData = function(rows, clsSource) {
615
751
  * @return {string}
616
752
  */
617
753
  Segment.prototype.getSubSegmentName = function(row) {
618
- if(row && this._subSegLevel) {
754
+ if(row && this._subSegment) {
619
755
  let classifiers = this.getClassification();
620
- let field = classifiers[this._subSegLevel - 1];
756
+ let field = classifiers[this._depth - 1];
621
757
  if(field) {
622
758
  row[field] = this._subSegName;
623
759
  }
@@ -633,9 +769,7 @@ Segment.prototype.collapse = function(bool) {
633
769
  bool = (bool !== false);
634
770
  if(this._collapsed !== bool) {
635
771
  this._collapsed = bool;
636
- if(this._childCount || this._subSegDef) {
637
- this._shared.dirtyCollapsingState = true;
638
- }
772
+ this.markCollapsingStateDirty();
639
773
  return true;
640
774
  }
641
775
  return false;
@@ -653,82 +787,87 @@ Segment.prototype.expand = function(bool) {
653
787
  Segment.prototype.isCollapsed = function() {
654
788
  return this._collapsed;
655
789
  };
656
- /** @public
790
+ /** Get all collapsing state from all children (subsegments and child segments), excluding the segment itself
791
+ * @public
657
792
  * @param {Object=} objMap
658
793
  * @param {boolean=} parentState=false Collapsing state from parent segment
659
794
  * @return {boolean}
660
795
  */
661
796
  Segment.prototype.getCollapsingStates = function(objMap, parentState) {
662
- let segmentNames = this._subSegNames;
663
- if(!this._subSegLevel) { // Only root segment
664
- if(!segmentNames) { // No sub segment
665
- if(!this._collapsed) {
666
- return false;
667
- }
668
- }
797
+ let childList = this._subSegNames;
798
+ let segmentMap = this._subSegMap;
799
+ let subSegment = true; // Normal segments can have subsegments without being subsegment themselve
800
+ if(!childList && this._shared) { // Ensure that segment has not been disposed
801
+ childList = this._childIds;
802
+ segmentMap = this._shared.segments;
803
+ subSegment = false;
804
+ }
805
+ let childCount = childList ? childList.length : 0;
806
+ if(!childCount) {
807
+ return false;
669
808
  }
670
809
 
810
+ let dirty = false;
811
+ let childCollapsed = (parentState || this._collapsed) ? true : false;
812
+
671
813
  if(!objMap) {
672
814
  objMap = {};
673
815
  }
674
- let dirty = false;
675
- if(this._subSegLevel) { // Sub segments are also subjected to collapsing
676
- if(parentState) {
677
- objMap[this._rid] = true;
816
+ for(let i = 0; i < childCount; ++i) {
817
+ let rid = "";
818
+ let segment = null;
819
+ if(subSegment) {
820
+ segment = segmentMap[childList[i]]; // Use segment name to retrieve subsegment
821
+ rid = segment.getId();
822
+ } else {
823
+ rid = childList[i];
824
+ segment = segmentMap[rid]; // Use row id to retrieve segment from shared map
825
+ }
826
+ if(childCollapsed) {
827
+ objMap[rid] = childCollapsed; // Collapsing states for all children is registered here
678
828
  dirty = true;
679
829
  }
680
- }
681
- if(this._childCount) {
682
- let collapsed = parentState || this._collapsed;
683
- if(segmentNames) {
684
- let segmentMap = this._subSegMap;
685
- let segmentCount = segmentNames.length;
686
- for(let i = 0; i < segmentCount; ++i) {
687
- let segment = segmentMap[segmentNames[i]];
688
- objMap[segment.getId()] = !!parentState;
689
- if(segment.getCollapsingStates(objMap, collapsed)) {
690
- dirty = true;
691
- }
692
- }
693
- } else if(collapsed) {
694
- let chdr = this._children;
695
- for(let rid in chdr) {
696
- objMap[rid] = collapsed;
697
- }
830
+ if(segment && segment.getCollapsingStates(objMap, childCollapsed)) {
698
831
  dirty = true;
699
832
  }
700
833
  }
701
834
  return dirty;
702
835
  };
703
836
 
837
+ /** @private
838
+ * @return {number}
839
+ */
840
+ Segment.prototype._getOrder = function() {
841
+ return this._order * 10000;
842
+ };
843
+ /** @private
844
+ * @return {number}
845
+ */
846
+ Segment.prototype._getLastOrder = function() {
847
+ return this._getOrder() + this._offsetOrder;
848
+ };
704
849
  /** @public
705
850
  * @return {number}
706
851
  */
707
852
  Segment.prototype.getOrder = function() {
708
- if(this._subSegLevel) {
709
- let ancestor = this.getFirstAncestor();
710
- if(ancestor) {
711
- // WARNING: this._order cannot be greater than 9999
712
- return ancestor.getOrder() + this._order;
713
- }
853
+ let ancestor = this.getFirstAncestor();
854
+ if(ancestor) {
855
+ // WARNING: this._order cannot be greater than 9999
856
+ return ancestor._getOrder() + this._order;
714
857
  }
715
- return this._order * 10000;
858
+ return this._getOrder();
716
859
  };
717
860
  /** Get the last (highest) order from the entire tree regardless of the current position segment in the hierachy
718
861
  * @public
719
862
  * @return {number}
720
863
  */
721
864
  Segment.prototype.getLastOrder = function() {
722
- if(this._subSegDef) {
723
- let subSegments = this._subSegDef.subSegments;
724
- if(subSegments) {
725
- let lastSegment = subSegments[subSegments.length - 1];
726
- if(lastSegment) {
727
- return lastSegment.getOrder();
728
- }
729
- }
865
+ let ancestor = this.getFirstAncestor();
866
+ if(ancestor) {
867
+ // WARNING: this._order cannot be greater than 9999
868
+ return ancestor._getLastOrder();
730
869
  }
731
- return this.getOrder();
870
+ return this._getLastOrder();
732
871
  };
733
872
  /** @public
734
873
  * @param {number} val
@@ -736,6 +875,13 @@ Segment.prototype.getLastOrder = function() {
736
875
  Segment.prototype.setOrder = function(val) {
737
876
  this._order = val;
738
877
  };
878
+ /** @public
879
+ * @param {number} val
880
+ * @returns {number} Returns the number set
881
+ */
882
+ Segment.prototype.setLastOrder = function(val) {
883
+ return (this._offsetOrder = val);
884
+ };
739
885
 
740
886
  /** @private
741
887
  * @type {Array.<string>}
@@ -743,9 +889,10 @@ Segment.prototype.setOrder = function(val) {
743
889
  Segment._tabs = null;
744
890
  /** @public
745
891
  * @param {Array.<string>=} lines
892
+ * @param {number=} tabLevel
746
893
  * @return {!Array.<string>} lines
747
894
  */
748
- Segment.prototype.log = function(lines) {
895
+ Segment.prototype.log = function(lines, tabLevel) {
749
896
  if(!lines) {
750
897
  lines = [];
751
898
  }
@@ -760,23 +907,43 @@ Segment.prototype.log = function(lines) {
760
907
  tabCh += " ";
761
908
  }
762
909
  }
910
+ if(!tabLevel) {
911
+ tabLevel = 0;
912
+ }
913
+
763
914
  let collapsedCh = this._collapsed ? "+ " : "- ";
764
- lines.push(tabs[this._subSegLevel] + collapsedCh + this._rid);
915
+ lines.push(tabs[tabLevel] + collapsedCh + this._rid);
765
916
 
766
- let segmentNames = this._subSegNames;
767
- if(segmentNames) {
768
- let segmentCount = segmentNames.length;
769
- let segmentMap = this._subSegMap;
770
- for(i = 0; i < segmentCount; ++i) {
771
- segmentMap[segmentNames[i]].log(lines);
917
+ let childLevel = tabLevel + 1;
918
+ let childIndent = tabs[childLevel];
919
+
920
+ let childList = this._subSegNames;
921
+ let segmentMap = this._subSegMap;
922
+ let subSegment = true; // Normal segments can have subsegments without being subsegment themselve
923
+ if(!childList && this._shared) { // Ensure that segment has not been disposed
924
+ childList = this._childIds;
925
+ segmentMap = this._shared.segments;
926
+ subSegment = false;
927
+ }
928
+ let childCount = childList ? childList.length : 0;
929
+
930
+ for(i = 0; i < childCount; ++i) {
931
+ let rid = "";
932
+ let segment = null;
933
+ if(subSegment) {
934
+ segment = segmentMap[childList[i]]; // Use segment name to retrieve subsegment
935
+ rid = segment.getId();
936
+ } else {
937
+ rid = childList[i];
938
+ segment = segmentMap[rid]; // Use row id to retrieve segment from shared map
772
939
  }
773
- } else if(this._childCount) {
774
- let indent = tabs[this._subSegLevel + 1];
775
- for(let rid in this._children) {
776
- lines.push(indent + "- " + rid);
940
+
941
+ if(segment) {
942
+ segment.log(lines, childLevel);
943
+ } else {
944
+ lines.push(childIndent + "- " + rid);
777
945
  }
778
946
  }
779
-
780
947
  return lines;
781
948
  };
782
949