@refinitiv-ui/efx-grid 6.0.126 → 6.0.128

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- import { arrayToObject, cloneObject } from "../../tr-grid-util/es6/Util.js";
1
+ import { arrayToObject } from "../../tr-grid-util/es6/Util.js";
2
2
  /* eslint-disable */
3
3
  import { DataCache } from "../../core/es6/data/DataCache.js";
4
4
  import { DataTable } from "../../core/es6/data/DataTable.js";
@@ -54,20 +54,32 @@ const ROW_TYPES = {
54
54
  */
55
55
  const ROW_ID_PATTERN = /^_[^_]+_$/;
56
56
 
57
+ /** @private
58
+ * @function
59
+ * @param {Object} obj
60
+ * @returns {!Object} Always returns a new object
61
+ */
62
+ let _cloneObject = function(obj) {
63
+ let newObj = {};
64
+ for(let key in obj) {
65
+ newObj[key] = obj[key];
66
+ }
67
+ return newObj;
68
+ };
69
+
57
70
  /** @constructor
58
71
  * @param {RowDefinition~Options=} rowOptions
59
72
  */
60
73
  let RowDefinition = function(rowOptions) {
61
74
  this._changes = {};
62
- if(rowOptions && rowOptions["segmentId"]) { // This row will be classification header row
63
- this._dataId = this._rowId = rowOptions["segmentId"];
75
+ if(rowOptions && rowOptions["segmentId"]) { // This row will be classified header row (subsegment header row)
76
+ this._rowId = rowOptions["segmentId"];
64
77
  this._autoGenerated = true;
65
78
  this._subSegment = true;
66
79
  return;
67
80
  }
68
81
 
69
82
  this._rowId = "_" + RowDefinition._runningId++ + "_";
70
- this._dataId = this._rowId;
71
83
 
72
84
  if(rowOptions) {
73
85
  if(rowOptions["asConstituent"]) { // Create row definition as a constituent of a chain
@@ -120,10 +132,6 @@ RowDefinition.prototype._realTime = true;
120
132
  * @private
121
133
  */
122
134
  RowDefinition.prototype._rowId = ""; // Row Id must be unique
123
- /** @type {string}
124
- * @private
125
- */
126
- RowDefinition.prototype._dataId = ""; // Data id can be different from rowId for constituents of a chain
127
135
  /** @type {boolean}
128
136
  * @private
129
137
  */
@@ -132,6 +140,11 @@ RowDefinition.prototype._userId = false;
132
140
  * @private
133
141
  */
134
142
  RowDefinition.prototype._dc = null;
143
+ /** Quotes2 subscription object
144
+ * @type {Object}
145
+ * @private
146
+ */
147
+ RowDefinition.prototype._subs = null;
135
148
  /** @type {DataView}
136
149
  * @private
137
150
  */
@@ -207,22 +220,20 @@ RowDefinition.prototype.dispose = function() {
207
220
  this._children = null;
208
221
  }
209
222
 
223
+ if(this._dc) {
224
+ this._dc.setRowData(this._rowId, null); // Clear all data held by this row def
225
+ this._dc = null;
226
+ }
210
227
  this._staticValues = null; // Static values contain user data
211
228
  this._parent = null; // WARNING: This does not impact children count of the parent. A child must be destroyed by its parent only
212
229
  this._depthLevel = 0;
213
230
  this.unsubscribeForUpdates();
214
- if(this._dc) {
215
- let rowData = this.getRowData();
216
- if(rowData) {
217
- rowData[ROW_DEF] = null;
218
- }
219
- this._dc = null;
220
- }
221
- if(this._view) {
222
- this._view = null;
223
- }
224
- this._rowId = this._dataId = "";
231
+ this._subs = null;
232
+ this._view = null; // WARNING: This does not remove this row from the UI view
233
+
234
+ this._subId = this._rowId = "";
225
235
  this._userId = false;
236
+ this._autoGenerated = false;
226
237
  this._userModel = null;
227
238
  };
228
239
  /** @public
@@ -245,7 +256,7 @@ RowDefinition.prototype.initialize = function(rowOptions) {
245
256
  if(userRowId.match(ROW_ID_PATTERN)) {
246
257
  console.warn("Please change the rowId format to avoid duplicated rows' id causing unexpected behavior.");
247
258
  } else {
248
- this._rowId = this._dataId = userRowId;
259
+ this._rowId = userRowId;
249
260
  this._userId = true;
250
261
  }
251
262
  }
@@ -301,7 +312,7 @@ RowDefinition.prototype.initialize = function(rowOptions) {
301
312
 
302
313
  let symbol = this._ric || this._chainRic;
303
314
  if(symbol || this._permId){
304
- this.setContent(symbol, extractedOptions); // this._dataId is modified
315
+ this.setContent(symbol, extractedOptions);
305
316
  }
306
317
 
307
318
  val = rowOptions["values"];
@@ -310,28 +321,20 @@ RowDefinition.prototype.initialize = function(rowOptions) {
310
321
  this.setStaticRowData(val, rowOptions["fields"]);
311
322
  }
312
323
  };
313
- /** @private
324
+ /** @description Constituents will have subscription id, but have no subscription. It's row id will be different from subscription id. It will have a reference to its parent. It cannot be generated by themselve -- always be autogenerated.
325
+ * @private
314
326
  * @param {!Object} rowOptions
315
327
  */
316
328
  RowDefinition.prototype._initializeAsConstituent = function(rowOptions) {
317
329
  this._autoGenerated = true;
318
330
  let parentDef = /** @type{RowDefinition} */(rowOptions["parent"]);
319
331
  if(this.setParent(parentDef)) {
320
- this._dataId = /** @type{string} */(rowOptions["dataId"]); // Constituent will have the same subId as its parent but with different ric
321
332
  this._ric = /** @type{string} */(rowOptions["ric"]);
322
333
  this._dc = parentDef._dc; // Parent chain must have data cache
323
- if(this._dc) {
324
- let rowData = this.getRowData(); // Do not trigger any data update
325
- if(rowData) { // Row data from data cache must exist beforehand
326
- rowData[ROW_DEF] = this; // This is for easy referencing from data streaming, also for preventing duplication
327
- }
328
- }
329
- }
330
- let val = rowOptions["values"];
331
- // eslint-disable-next-line no-undefined
332
- if(val !== undefined) {
333
- this.setStaticRowData(val, rowOptions["fields"]);
334
+ this._subId = parentDef._subId; // Child constituent uses the same subscription as its parent
334
335
  }
336
+ this._staticValues = rowOptions["values"] || null;
337
+ this.resetRowData();
335
338
  };
336
339
  /** @private
337
340
  * @param {DataView} view
@@ -386,9 +389,9 @@ RowDefinition.prototype.setContent = function(userInput, extractedOptions) {
386
389
  }
387
390
 
388
391
  let dirty = (this._userInput !== userInput);
389
- let permId = extractedOptions["permId"];
392
+ let permId = extractedOptions["permId"] || "";
390
393
  if(this._permId !== permId){
391
- this._permId = permId || "";
394
+ this._permId = permId;
392
395
  dirty = true;
393
396
  }
394
397
  if(!dirty) {
@@ -405,10 +408,7 @@ RowDefinition.prototype.setContent = function(userInput, extractedOptions) {
405
408
  let dv = this._view;
406
409
  let stalledSorting = _stallSorting(dv, realtimeRow && asChain, false); // To preserve current position of the segment/chain
407
410
 
408
- if(!this.unsubscribeForUpdates()){
409
- this._clearStaticData();
410
- }
411
- this.resetUpdates(); // Remove all previous data updates because a new content is just entered
411
+ this.unsubscribeForUpdates(); // Static data remains intact
412
412
 
413
413
  this._userInput = userInput;
414
414
  let collapsed = extractedOptions["collapsed"];
@@ -422,9 +422,6 @@ RowDefinition.prototype.setContent = function(userInput, extractedOptions) {
422
422
  this._isChain = asChain != null ? asChain : null; // this could be null or undefined
423
423
  this._chainRic = chainRic || "";
424
424
  }
425
- // A symbol can be either RIC or permId
426
- // JET/RTK will generate data id to be rowId (given from this rowDef) + ric
427
- this._dataId = this._rowId + this.getSymbol();
428
425
 
429
426
  if(dv) {
430
427
  let segmentId = dv.getSegmentParentRowId(this._rowId);
@@ -443,27 +440,18 @@ RowDefinition.prototype.setContent = function(userInput, extractedOptions) {
443
440
  dv.setSegmentSeparator(this._rowId, true);
444
441
  }
445
442
 
446
- if(collapsed !== this.isChainCollapsed()) {
443
+ if(collapsed !== this.isChainCollapsed()) { // WARNING: This is not optimizsed
447
444
  dv.collapseSegment(this._rowId, collapsed);
448
445
  }
449
446
  this._collapsed = null;
450
447
 
451
448
  _stallSorting(dv, false, stalledSorting);
452
449
  if(segmentId) { // If data id is changed and the row is a child of a segment, then segment child data id must be updated
453
- dv.addSegmentChild(segmentId, this._rowId, this._dataId);
450
+ dv.addSegmentChild(segmentId, this._rowId);
454
451
  }
455
452
  }
456
453
 
457
- if(!this.subscribeForUpdates()) {
458
- if(this._dc) {
459
- // This will work for runtime ric modification, but not for first initilization.
460
- // Avoid losing the ROW_DEF pointer.
461
- let rowData = {};
462
- rowData[ROW_DEF] = this; // Enable tracking back and updating data
463
- rowData["X_RIC_NAME"] = this.getDisplayText();
464
- this.setRowData(rowData);
465
- }
466
- }
454
+ this.subscribeForUpdates();
467
455
  return true;
468
456
  };
469
457
  /** @public
@@ -575,12 +563,12 @@ RowDefinition.prototype.getConfigObject = function(rowOptions) {
575
563
 
576
564
  return obj;
577
565
  };
578
- /** Since an index chain (e.g. .FTSE) can automatically produce rows for its constituent, we need to separate rowId and dataId, so that the constituents still use the same data Id as that of its parent.
566
+ /** Data id is deprecated
579
567
  * @public
580
- * @return {string} Data Id will never be empty string unless the row has been removed
568
+ * @return {string} Always return empty string
581
569
  */
582
570
  RowDefinition.prototype.getDataId = function() {
583
- return this._dataId;
571
+ return "";
584
572
  };
585
573
  /** @public
586
574
  * @return {string}
@@ -616,26 +604,19 @@ RowDefinition.prototype.getType = function() {
616
604
  /** This method should always be called right after the initialization
617
605
  * @public
618
606
  * @param {DataCache} dataSource
607
+ * @param {Object=} subs Quotes2 subscription object
619
608
  */
620
- RowDefinition.prototype.setDataSource = function(dataSource) {
609
+ RowDefinition.prototype.setDataSource = function(dataSource, subs) {
621
610
  this._dc = dataSource || null;
622
- if(!this._dc) {
623
- return;
624
- }
625
-
626
- let rowData = this.getRowData();
627
- if(!rowData) {
628
- rowData = {};
629
- rowData[ROW_DEF] = this; // Enable tracking back and updating data
630
- rowData["X_RIC_NAME"] = this.getDisplayText();
631
- this.setRowData(rowData); // TODO: This will dispatch dataChanged event and caused update to be added, which should not happen
632
- }
633
611
 
634
- // Add static value to the new source
635
- if(this._staticValues) {
636
- this.setRowData(this._staticValues);
612
+ if(this._dc) {
613
+ this.setRowData({}); // Trigger data change
614
+ if(this._staticValues) {
615
+ this.setRowData(this._staticValues); // Trigger dataComposed and add updates
616
+ }
637
617
  }
638
618
 
619
+ this._subs = subs || null;
639
620
  // This will work for runtime row insertion, but not for first initilization.
640
621
  this.subscribeForUpdates();
641
622
  };
@@ -650,14 +631,14 @@ RowDefinition.prototype.getDataSource = function() {
650
631
  * @return {Object} rowData
651
632
  */
652
633
  RowDefinition.prototype.getRowData = function() {
653
- return this._dc ? this._dc.getRowData(this._dataId) : {};
634
+ return this._dc ? this._dc.getRowData(this._rowId) : {};
654
635
  };
655
636
  /** @public
656
637
  * @param {string} field
657
638
  * @return {*} data
658
639
  */
659
640
  RowDefinition.prototype.getData = function(field) {
660
- return this._dc ? this._dc.getData(this._dataId, field) : null;
641
+ return this._dc ? this._dc.getData(this._rowId, field) : null;
661
642
  };
662
643
 
663
644
  /** This method replaces all existing static data by the given data -- all existing static data are removed.
@@ -682,7 +663,7 @@ RowDefinition.prototype.setStaticRowData = function(data, indexToFieldMap) {
682
663
  * @return {Object.<string, *>}
683
664
  */
684
665
  RowDefinition.prototype._cloneStaticRowData = function() {
685
- return this._staticValues ? cloneObject(this._staticValues) : null;
666
+ return this._staticValues ? _cloneObject(this._staticValues) : null;
686
667
  };
687
668
  /** @public
688
669
  * @param {string} field
@@ -752,7 +733,20 @@ RowDefinition.prototype.cloneRowData = function(obj, exceptionObj) {
752
733
  */
753
734
  RowDefinition.prototype.setRowData = function(data) {
754
735
  if(this._dc) {
755
- this._dc.setRowData(this._dataId, data);
736
+ this._dc.setRowData(this._rowId, data);
737
+ }
738
+ };
739
+ /** @public
740
+ */
741
+ RowDefinition.prototype.resetRowData = function() {
742
+ let dc = this._dc;
743
+ if(dc) {
744
+ let rowId = this._rowId;
745
+ dc.setRowData(rowId, null);
746
+ dc.setRowData(rowId, {}); // Row data is guaranteed to be existed
747
+ if(this._staticValues) {
748
+ dc.setRowData(rowId, this._staticValues); // Trigger dataComposed and add updates
749
+ }
756
750
  }
757
751
  };
758
752
  /** @public
@@ -761,18 +755,20 @@ RowDefinition.prototype.setRowData = function(data) {
761
755
  */
762
756
  RowDefinition.prototype.setData = function(field, value) {
763
757
  if(this._dc) {
764
- this._dc.setData(this._dataId, field, value);
758
+ this._dc.setData(this._rowId, field, value);
765
759
  }
766
760
  };
767
761
  /** @private
768
762
  */
769
763
  RowDefinition.prototype._clearStaticData = function() {
770
- if(this._staticValues) {
771
- for(let key in this._staticValues) {
772
- this._staticValues[key] = null;
773
- }
764
+ let staticValues = this._staticValues;
765
+ if(staticValues) {
774
766
  if(this._dc) {
775
- this._dc.setRowData(this._dataId, this._staticValues);
767
+ let obj = {};
768
+ for(let key in staticValues) {
769
+ obj[key] = null;
770
+ }
771
+ this._dc.setRowData(this._rowId, obj);
776
772
  }
777
773
  this._staticValues = null;
778
774
  }
@@ -810,7 +806,7 @@ RowDefinition.prototype.getDisplayText = function() {
810
806
  return this._label;
811
807
  }
812
808
 
813
- if(this._ric) {
809
+ if(this._ric) { // Use chainRic instead if this is a chain
814
810
  return this._ric;
815
811
  }
816
812
 
@@ -840,6 +836,12 @@ RowDefinition.prototype.isChain = function() {
840
836
  return this._isChain;
841
837
  };
842
838
  /** @public
839
+ * @return {boolean}
840
+ */
841
+ RowDefinition.prototype.isConstituent = function() {
842
+ return (this._autoGenerated && this._subId && this._rowId !== this._subId) ? true : false;
843
+ };
844
+ /** @public
843
845
  * @function
844
846
  * @param {RowDefinition} rowDef
845
847
  * @return {boolean}
@@ -898,19 +900,23 @@ RowDefinition.prototype.isRealTimeRow = function() {
898
900
  };
899
901
 
900
902
  /** @public
903
+ * @param {Object=} subs
901
904
  * @return {boolean} If a subscription is made, return true.
902
905
  */
903
- RowDefinition.prototype.subscribeForUpdates = function() {
904
- if(!this.isRealTimeRow() && !this.getPermId()) {
905
- return false;
906
+ RowDefinition.prototype.subscribeForUpdates = function(subs) {
907
+ if(subs) {
908
+ this._subs = subs;
909
+ } else {
910
+ subs = this._subs;
906
911
  }
907
-
908
- let subs = this._dc ? this._dc.getSubscriptions() : null;
909
912
  if(!subs) {
910
913
  return false;
911
914
  }
915
+ if(!this.isRealTimeRow() && !this.getPermId()) {
916
+ return false;
917
+ }
912
918
  // TODO: Check if the same subscription is being made.
913
- let prevRowData = this.unsubscribeForUpdates();
919
+ this.unsubscribeForUpdates();
914
920
 
915
921
  if(this.isChain()) {
916
922
  let symbol = this._chainRic;
@@ -928,57 +934,51 @@ RowDefinition.prototype.subscribeForUpdates = function() {
928
934
  this._subId = subs["addRic"](this._ric, this._rowId);
929
935
  }
930
936
 
931
- if(prevRowData) {
932
- this._dc.setRowData(this._dataId, prevRowData); // TODO: We may need to create a new object instead of prevRowData for data correctness
933
- } else {
934
- this._dc.setRowData(this._dataId, {"X_RIC_NAME": this.getDisplayText(), "ROW_DEF": this}); // Trigger data update immediately
935
- }
937
+ let xRicName = this.getDisplayText(); // WARNING: This does not cover real-time X_RIC_NAME updates
938
+ this.setRowData({"X_RIC_NAME": xRicName}); // Trigger data update immediately
939
+
936
940
  return true;
937
941
  };
938
- /** @public
939
- * @returns {*}
942
+ /** Unsubscribe existing real-time data service. Static data is maintained
943
+ * @public
944
+ * @returns {null} Always return null
940
945
  */
941
946
  RowDefinition.prototype.unsubscribeForUpdates = function() {
942
- if(!this._subId) {
943
- return;
947
+ if(this.isSubscribing()) { // Only normal real-time rows and chains have both subId and subscription object
948
+ this._subs["removeSubscription"](this._subId);
949
+ this._subId = "";
950
+ this.resetUpdates();
951
+ this.resetRowData(); // Real-time data is removed while static data is maintained
944
952
  }
945
-
946
- let subs = this._dc.getSubscriptions();
947
- let prevRowData = this.getRowData();
948
-
949
- subs["removeSubscription"](this._subId);
950
- this._subId = "";
951
- this.resetUpdates();
952
- // TODO: Reset only if this is the last ric
953
- this._dc.setRowData(this._dataId, null); // Trigger data update immediately
954
- this._clearStaticData();
955
- // eslint-disable-next-line consistent-return
956
- return prevRowData;
953
+ return null;
957
954
  };
958
955
  /** @public
959
956
  * @return {boolean}
960
957
  */
961
958
  RowDefinition.prototype.isSubscribing = function() {
962
- return this._subId ? true : false;
959
+ return (this._subId && this._subs) ? true : false;
960
+ };
961
+ /** @public
962
+ * @return {string}
963
+ */
964
+ RowDefinition.prototype.getSubId = function() {
965
+ return this._subId;
963
966
  };
964
967
 
965
968
  /** Take event argument from streaming update with "changes" array property
966
969
  * @public
967
- * @param {Object} e Object with changes property
970
+ * @param {Object} changes
968
971
  * @return {boolean} Returns true if there is any change
969
972
  */
970
- RowDefinition.prototype.addUpdate = function(e) {
971
- if(e) {
972
- let changes = e["changes"];
973
- if(changes) {
974
- let dirty = 0;
975
- for(let field in changes) {
976
- this._changes[field] = dirty = 1;
977
- }
978
- if(dirty) {
979
- ++this._updateCount;
980
- return true;
981
- }
973
+ RowDefinition.prototype.addUpdate = function(changes) {
974
+ if(changes) {
975
+ let dirty = 0;
976
+ for(let field in changes) {
977
+ this._changes[field] = dirty = 1;
978
+ }
979
+ if(dirty) {
980
+ ++this._updateCount;
981
+ return true;
982
982
  }
983
983
  }
984
984
  return false;
@@ -1001,27 +1001,27 @@ RowDefinition.prototype.resetUpdates = function() {
1001
1001
 
1002
1002
  /** @public
1003
1003
  * @param {DataView} view
1004
- * @param {string=} destRowId Destination position where the row will be placed BEFORE the specified position.
1004
+ * @param {string=} destRowId Destination position where the row will be placed BEFORE the specified position
1005
+ * @returns {boolean} Returns true for successful registration
1005
1006
  */
1006
1007
  RowDefinition.prototype.registerToView = function(view, destRowId) {
1007
1008
  if(!view || this._view === view) {
1008
- return; // Already in the view
1009
+ return false; // Already in the view
1009
1010
  }
1010
- this._view = view;
1011
1011
 
1012
- let rowId = this.getRowId();
1013
- if(view.getRowData(rowId)) {
1014
- console.warn("Duplicated rows' id.");
1015
- return;
1012
+ this._view = view;
1013
+ let rowId = this._rowId;
1014
+ let rowData = view.getRowData(rowId);
1015
+ if(rowData && rowData[ROW_DEF]) {
1016
+ console.warn("Duplicate row id detected");
1017
+ return false;
1016
1018
  }
1017
1019
 
1018
- let rowData = null;
1019
1020
  if(this._subSegment) {
1020
- rowData = this._view.getRowData(this.getRowId());
1021
- if(rowData) {
1022
- rowData[ROW_DEF] = this; // no event trigger
1021
+ if(rowData) { // Row data for sub segment has been created by data view
1022
+ rowData[ROW_DEF] = this; // It only needs to register row def to the row data
1023
1023
  }
1024
- return;
1024
+ return true;
1025
1025
  }
1026
1026
 
1027
1027
  rowData = {};
@@ -1034,7 +1034,13 @@ RowDefinition.prototype.registerToView = function(view, destRowId) {
1034
1034
  if(parentRowId) {
1035
1035
  if(isSegment) { // A chain or a segment cannot be put inside another segment
1036
1036
  destRowId = _getEndOfSegmentRowId(view, destRowId);
1037
- } // else { // Normal row is inserted into a segment
1037
+ } else if(!this.isConstituent()) { // Normal row cannot be put inside another chain
1038
+ let parentRowDef = view.getData(parentRowId, ROW_DEF);
1039
+ if(parentRowDef.isChain()) {
1040
+ parentRowId = null;
1041
+ destRowId = _getEndOfSegmentRowId(view, destRowId);
1042
+ } // else { // Normal row is inserted into a segment
1043
+ } // Constituent can be inserted inside a chain
1038
1044
  }
1039
1045
  }
1040
1046
 
@@ -1049,8 +1055,10 @@ RowDefinition.prototype.registerToView = function(view, destRowId) {
1049
1055
  this._collapsed = null;
1050
1056
  }
1051
1057
  } else if(!this._parent && parentRowId) { // Constituent cannot be added to another segment
1052
- view.addSegmentChild(parentRowId, rowId, this._dataId);
1058
+ view.addSegmentChild(parentRowId, rowId);
1053
1059
  }
1060
+
1061
+ return true;
1054
1062
  };
1055
1063
  /** @private
1056
1064
  * @param {Array.<string>=} rowIds
@@ -1067,7 +1075,9 @@ RowDefinition.prototype._deregisterFromView = function(rowIds) {
1067
1075
  }
1068
1076
  return "";
1069
1077
  };
1070
- /** @public
1078
+ /** Deprecated.
1079
+ * @public
1080
+ * @ignore
1071
1081
  * @function
1072
1082
  * @param {Array.<string>} rowIds
1073
1083
  * @param {RowDefinition} rowDef
@@ -1084,77 +1094,74 @@ RowDefinition.deregisterFromView = function(rowIds, rowDef) {
1084
1094
  * @return {Object}
1085
1095
  */
1086
1096
  RowDefinition.prototype._getChildStaticRowData = function(ric) {
1087
- if(!this._staticValues) {
1088
- return null;
1097
+ if(this._staticValues) {
1098
+ let childValues = this._staticValues[RowDefinition._childDataField];
1099
+ if(childValues) {
1100
+ return childValues[ric] || null;
1101
+ }
1089
1102
  }
1103
+ return null;
1104
+ };
1090
1105
 
1091
- let childValues = this._staticValues[RowDefinition._childDataField];
1092
- if(!childValues) {
1093
- return null;
1106
+ /** @public
1107
+ * @ignore
1108
+ * @param {string} ric
1109
+ * @return {RowDefinition}
1110
+ */
1111
+ RowDefinition.prototype.getConstituent = function(ric) {
1112
+ if(this._children && ric) {
1113
+ for(let i = this._children.length; --i >= 0;) {
1114
+ if(this._children[i]._ric === ric) {
1115
+ return this._children[i];
1116
+ }
1117
+ }
1094
1118
  }
1119
+ return null;
1120
+ };
1095
1121
 
1096
- return childValues[ric] || null;
1122
+ /** Check whether the given ric can be a constituent of this row definition. WARNING: permId is not handled by this method
1123
+ * @public
1124
+ * @ignore
1125
+ * @param {string} ric
1126
+ * @return {boolean} Returns true if the given ric can be its constituent
1127
+ */
1128
+ RowDefinition.prototype.verifyConstituent = function(ric) {
1129
+ if(this._isChain && ric) { // A constituent must have a valid ric
1130
+ if(this._ric !== ric) { // A constituent cannot have the same ric as its parent
1131
+ if(this._ric !== ("0#" + ric)) { // Real-time service may return ric without 0#
1132
+ return this._ric !== ("/" + ric); // Real-time service may return ric with slash (delayed ric)
1133
+ }
1134
+ }
1135
+ }
1136
+ return false;
1097
1137
  };
1098
- /** @public
1138
+ /** verifyConstituent should be called before calling this method
1139
+ * @public
1099
1140
  * @ignore
1100
1141
  * @param {string} ric
1101
1142
  * @return {RowDefinition} Return newly created child definition, if success
1102
1143
  */
1103
1144
  RowDefinition.prototype.addConstituent = function(ric) {
1104
- if(!ric || this._ric === ric) { // Parent cannot have a child with no name or the same name as itself
1105
- return null;
1106
- }
1107
1145
  if(!(this._rowId && this._subId && this._isChain)) { // Chain must be alive (has not been disposed). Chain must be subscribed as chain
1108
1146
  return null;
1109
1147
  }
1110
1148
 
1111
- let childDef = null;
1112
- if(this._children) {
1113
- for(let i = this._children.length; --i >= 0;) {
1114
- if(this._children[i]._ric === ric) {
1115
- childDef = this._children[i]; // Constituent has already been added
1116
- break;
1117
- }
1118
- }
1119
- } else {
1149
+ if(!this._children) {
1120
1150
  this._children = [];
1121
1151
  }
1122
1152
 
1123
- let newChild = !childDef;
1124
- if(newChild) {
1125
- let rowOptions = {
1126
- "asConstituent": true,
1127
- "dataId": this._subId + ric,
1128
- "ric": ric,
1129
- "parent": this
1130
- };
1131
-
1132
- let staticData = this._getChildStaticRowData(ric);
1133
- if(staticData) {
1134
- rowOptions["values"] = staticData;
1135
- }
1136
-
1137
- childDef = new RowDefinition(rowOptions);
1138
- }
1139
-
1140
- if(this._view) {
1141
- let rowId = this.getRowId();
1142
- // WARNING: insert position, we prioritize using CHILD_ORDER (From server) in dc first. If it does not exist, the last row of the segment will be pushed
1143
- let childOrder = childDef.getData("CHILD_ORDER");
1144
- let dt = this._view.getDataSource();
1153
+ let rowOptions = {
1154
+ "asConstituent": true,
1155
+ "ric": ric,
1156
+ "parent": this
1157
+ };
1145
1158
 
1146
- let parentIndex = dt.getRowIndex(rowId);
1147
- let position = parentIndex + this.getChildCount(); // push the last position
1148
- if(childOrder != null) {
1149
- // Warning: We need to carry a value of 1 because the CHILD_ORDER starts with 0, and it will be added to the parentIndex. In case the parent rowIndex is not included.
1150
- position = parentIndex + childOrder + 1; // insert between segment
1151
- } // else {} it will be push in the last row of segment
1152
- childDef.registerToView(this._view, dt.getRowId(position));
1153
- // TODO: Handle nested children
1154
- this._view.addSegmentChild(rowId, childDef.getRowId(), childDef.getDataId());
1159
+ let staticData = this._getChildStaticRowData(ric); // TODO: Remove the child's static data from parent
1160
+ if(staticData) {
1161
+ rowOptions["values"] = staticData;
1155
1162
  }
1156
1163
 
1157
- return newChild ? childDef : null;
1164
+ return new RowDefinition(rowOptions); // childDef is added to this._children in the constructor
1158
1165
  };
1159
1166
 
1160
1167
  /** Used to convert autogenerated row to regular real-time row
@@ -1162,25 +1169,18 @@ RowDefinition.prototype.addConstituent = function(ric) {
1162
1169
  * @ignore
1163
1170
  */
1164
1171
  RowDefinition.prototype.toRealTimeRow = function() {
1165
- if(!this._ric) { // Empty row
1166
- return;
1167
- }
1168
- if(this.isRowHeader() || !this._parent) {
1169
- return; // If the row is already a normal row or row header, it cannot be converted
1172
+ if(!this.isConstituent()) {
1173
+ return; // Only a constituent can be converted to a real-time row
1170
1174
  }
1171
1175
 
1176
+ let subs = this._parent._subs;
1172
1177
  this._realTime = true;
1173
- this._dc.setRowData(this._dataId, null); // Remove existing data. WARNING: Trigger data update immediately
1174
- this._dataId = this._rowId + this._ric; // JET/RTK will generate data id to be rowId (given from this rowDef) + ric;
1175
-
1176
1178
  this._autoGenerated = false;
1177
1179
  this._parent = null;
1178
1180
  this._depthLevel = 0;
1179
1181
 
1180
- this.subscribeForUpdates();
1181
- if(this._staticValues) { // Add static value to the new allocated row
1182
- this.setRowData(this._staticValues);
1183
- }
1182
+ this.resetRowData(); // WARNING: existing real-time data is lost after this line
1183
+ this.subscribeForUpdates(subs); // Static data remains intact
1184
1184
  };
1185
1185
 
1186
1186
  /** @public
@@ -1190,11 +1190,7 @@ RowDefinition.prototype.unlinkChain = function() {
1190
1190
  return;
1191
1191
  }
1192
1192
 
1193
- let staticData = this._cloneStaticRowData();
1194
- this.unsubscribeForUpdates();
1195
-
1196
- // Restore static data
1197
- this.setStaticRowData(staticData);
1193
+ this.unsubscribeForUpdates(); // Static data remains intact
1198
1194
 
1199
1195
  let view = this._view;
1200
1196
  if(view) {
@@ -1309,6 +1305,22 @@ RowDefinition.prototype.getChildCount = function() {
1309
1305
  return (this._children) ? this._children.length : 0;
1310
1306
  };
1311
1307
  /** @public
1308
+ * @return {number}
1309
+ */
1310
+ RowDefinition.prototype.countChildInView = function() {
1311
+ if(this._children) {
1312
+ let viewCount = 0;
1313
+ let childCount = this._children.length;
1314
+ for(let i = 0; i < childCount; ++i) {
1315
+ if(this._children[i]._view) {
1316
+ ++viewCount;
1317
+ }
1318
+ }
1319
+ return viewCount;
1320
+ }
1321
+ return 0;
1322
+ };
1323
+ /** @public
1312
1324
  * @return {RowDefinition}
1313
1325
  */
1314
1326
  RowDefinition.prototype.getParent = function() {