@refinitiv-ui/efx-grid 6.0.147 → 6.0.148

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