@refinitiv-ui/efx-grid 6.0.125 → 6.0.127

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,16 @@ 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(_cloneObject(this._staticValues)); // Trigger data change
637
614
  }
638
615
 
616
+ this._subs = subs || null;
639
617
  // This will work for runtime row insertion, but not for first initilization.
640
618
  this.subscribeForUpdates();
641
619
  };
@@ -650,14 +628,14 @@ RowDefinition.prototype.getDataSource = function() {
650
628
  * @return {Object} rowData
651
629
  */
652
630
  RowDefinition.prototype.getRowData = function() {
653
- return this._dc ? this._dc.getRowData(this._dataId) : {};
631
+ return this._dc ? this._dc.getRowData(this._rowId) : {};
654
632
  };
655
633
  /** @public
656
634
  * @param {string} field
657
635
  * @return {*} data
658
636
  */
659
637
  RowDefinition.prototype.getData = function(field) {
660
- return this._dc ? this._dc.getData(this._dataId, field) : null;
638
+ return this._dc ? this._dc.getData(this._rowId, field) : null;
661
639
  };
662
640
 
663
641
  /** This method replaces all existing static data by the given data -- all existing static data are removed.
@@ -682,7 +660,7 @@ RowDefinition.prototype.setStaticRowData = function(data, indexToFieldMap) {
682
660
  * @return {Object.<string, *>}
683
661
  */
684
662
  RowDefinition.prototype._cloneStaticRowData = function() {
685
- return this._staticValues ? cloneObject(this._staticValues) : null;
663
+ return this._staticValues ? _cloneObject(this._staticValues) : null;
686
664
  };
687
665
  /** @public
688
666
  * @param {string} field
@@ -752,7 +730,15 @@ RowDefinition.prototype.cloneRowData = function(obj, exceptionObj) {
752
730
  */
753
731
  RowDefinition.prototype.setRowData = function(data) {
754
732
  if(this._dc) {
755
- this._dc.setRowData(this._dataId, data);
733
+ this._dc.setRowData(this._rowId, data);
734
+ }
735
+ };
736
+ /** @public
737
+ */
738
+ RowDefinition.prototype.resetRowData = function() {
739
+ if(this._dc) {
740
+ this._dc.setRowData(this._rowId, null);
741
+ this._dc.setRowData(this._rowId, _cloneObject(this._staticValues)); // Row data is guaranteed to be existed
756
742
  }
757
743
  };
758
744
  /** @public
@@ -761,18 +747,20 @@ RowDefinition.prototype.setRowData = function(data) {
761
747
  */
762
748
  RowDefinition.prototype.setData = function(field, value) {
763
749
  if(this._dc) {
764
- this._dc.setData(this._dataId, field, value);
750
+ this._dc.setData(this._rowId, field, value);
765
751
  }
766
752
  };
767
753
  /** @private
768
754
  */
769
755
  RowDefinition.prototype._clearStaticData = function() {
770
- if(this._staticValues) {
771
- for(let key in this._staticValues) {
772
- this._staticValues[key] = null;
773
- }
756
+ let staticValues = this._staticValues;
757
+ if(staticValues) {
774
758
  if(this._dc) {
775
- this._dc.setRowData(this._dataId, this._staticValues);
759
+ let obj = {};
760
+ for(let key in staticValues) {
761
+ obj[key] = null;
762
+ }
763
+ this._dc.setRowData(this._rowId, obj);
776
764
  }
777
765
  this._staticValues = null;
778
766
  }
@@ -810,7 +798,7 @@ RowDefinition.prototype.getDisplayText = function() {
810
798
  return this._label;
811
799
  }
812
800
 
813
- if(this._ric) {
801
+ if(this._ric) { // Use chainRic instead if this is a chain
814
802
  return this._ric;
815
803
  }
816
804
 
@@ -840,6 +828,12 @@ RowDefinition.prototype.isChain = function() {
840
828
  return this._isChain;
841
829
  };
842
830
  /** @public
831
+ * @return {boolean}
832
+ */
833
+ RowDefinition.prototype.isConstituent = function() {
834
+ return (this._autoGenerated && this._subId && this._rowId !== this._subId) ? true : false;
835
+ };
836
+ /** @public
843
837
  * @function
844
838
  * @param {RowDefinition} rowDef
845
839
  * @return {boolean}
@@ -898,19 +892,23 @@ RowDefinition.prototype.isRealTimeRow = function() {
898
892
  };
899
893
 
900
894
  /** @public
895
+ * @param {Object=} subs
901
896
  * @return {boolean} If a subscription is made, return true.
902
897
  */
903
- RowDefinition.prototype.subscribeForUpdates = function() {
904
- if(!this.isRealTimeRow() && !this.getPermId()) {
905
- return false;
898
+ RowDefinition.prototype.subscribeForUpdates = function(subs) {
899
+ if(subs) {
900
+ this._subs = subs;
901
+ } else {
902
+ subs = this._subs;
906
903
  }
907
-
908
- let subs = this._dc ? this._dc.getSubscriptions() : null;
909
904
  if(!subs) {
910
905
  return false;
911
906
  }
907
+ if(!this.isRealTimeRow() && !this.getPermId()) {
908
+ return false;
909
+ }
912
910
  // TODO: Check if the same subscription is being made.
913
- let prevRowData = this.unsubscribeForUpdates();
911
+ this.unsubscribeForUpdates();
914
912
 
915
913
  if(this.isChain()) {
916
914
  let symbol = this._chainRic;
@@ -928,57 +926,51 @@ RowDefinition.prototype.subscribeForUpdates = function() {
928
926
  this._subId = subs["addRic"](this._ric, this._rowId);
929
927
  }
930
928
 
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
- }
929
+ let xRicName = this.getDisplayText(); // WARNING: This does not cover real-time X_RIC_NAME updates
930
+ this.setRowData({"X_RIC_NAME": xRicName}); // Trigger data update immediately
931
+
936
932
  return true;
937
933
  };
938
- /** @public
939
- * @returns {*}
934
+ /** Unsubscribe existing real-time data service. Static data is maintained
935
+ * @public
936
+ * @returns {null} Always return null
940
937
  */
941
938
  RowDefinition.prototype.unsubscribeForUpdates = function() {
942
- if(!this._subId) {
943
- return;
939
+ if(this.isSubscribing()) { // Only normal real-time rows and chains have both subId and subscription object
940
+ this._subs["removeSubscription"](this._subId);
941
+ this._subId = "";
942
+ this.resetUpdates();
943
+ this.resetRowData(); // Real-time data is removed while static data is maintained
944
944
  }
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;
945
+ return null;
957
946
  };
958
947
  /** @public
959
948
  * @return {boolean}
960
949
  */
961
950
  RowDefinition.prototype.isSubscribing = function() {
962
- return this._subId ? true : false;
951
+ return (this._subId && this._subs) ? true : false;
952
+ };
953
+ /** @public
954
+ * @return {string}
955
+ */
956
+ RowDefinition.prototype.getSubId = function() {
957
+ return this._subId;
963
958
  };
964
959
 
965
960
  /** Take event argument from streaming update with "changes" array property
966
961
  * @public
967
- * @param {Object} e Object with changes property
962
+ * @param {Object} changes
968
963
  * @return {boolean} Returns true if there is any change
969
964
  */
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
- }
965
+ RowDefinition.prototype.addUpdate = function(changes) {
966
+ if(changes) {
967
+ let dirty = 0;
968
+ for(let field in changes) {
969
+ this._changes[field] = dirty = 1;
970
+ }
971
+ if(dirty) {
972
+ ++this._updateCount;
973
+ return true;
982
974
  }
983
975
  }
984
976
  return false;
@@ -1001,27 +993,27 @@ RowDefinition.prototype.resetUpdates = function() {
1001
993
 
1002
994
  /** @public
1003
995
  * @param {DataView} view
1004
- * @param {string=} destRowId Destination position where the row will be placed BEFORE the specified position.
996
+ * @param {string=} destRowId Destination position where the row will be placed BEFORE the specified position
997
+ * @returns {boolean} Returns true for successful registration
1005
998
  */
1006
999
  RowDefinition.prototype.registerToView = function(view, destRowId) {
1007
1000
  if(!view || this._view === view) {
1008
- return; // Already in the view
1001
+ return false; // Already in the view
1009
1002
  }
1010
- this._view = view;
1011
1003
 
1012
- let rowId = this.getRowId();
1013
- if(view.getRowData(rowId)) {
1014
- console.warn("Duplicated rows' id.");
1015
- return;
1004
+ this._view = view;
1005
+ let rowId = this._rowId;
1006
+ let rowData = view.getRowData(rowId);
1007
+ if(rowData && rowData[ROW_DEF]) {
1008
+ console.warn("Duplicate row id detected");
1009
+ return false;
1016
1010
  }
1017
1011
 
1018
- let rowData = null;
1019
1012
  if(this._subSegment) {
1020
- rowData = this._view.getRowData(this.getRowId());
1021
- if(rowData) {
1022
- rowData[ROW_DEF] = this; // no event trigger
1013
+ if(rowData) { // Row data for sub segment has been created by data view
1014
+ rowData[ROW_DEF] = this; // It only needs to register row def to the row data
1023
1015
  }
1024
- return;
1016
+ return true;
1025
1017
  }
1026
1018
 
1027
1019
  rowData = {};
@@ -1034,7 +1026,13 @@ RowDefinition.prototype.registerToView = function(view, destRowId) {
1034
1026
  if(parentRowId) {
1035
1027
  if(isSegment) { // A chain or a segment cannot be put inside another segment
1036
1028
  destRowId = _getEndOfSegmentRowId(view, destRowId);
1037
- } // else { // Normal row is inserted into a segment
1029
+ } else if(!this.isConstituent()) { // Normal row cannot be put inside another chain
1030
+ let parentRowDef = view.getData(parentRowId, ROW_DEF);
1031
+ if(parentRowDef.isChain()) {
1032
+ parentRowId = null;
1033
+ destRowId = _getEndOfSegmentRowId(view, destRowId);
1034
+ } // else { // Normal row is inserted into a segment
1035
+ } // Constituent can be inserted inside a chain
1038
1036
  }
1039
1037
  }
1040
1038
 
@@ -1049,8 +1047,10 @@ RowDefinition.prototype.registerToView = function(view, destRowId) {
1049
1047
  this._collapsed = null;
1050
1048
  }
1051
1049
  } else if(!this._parent && parentRowId) { // Constituent cannot be added to another segment
1052
- view.addSegmentChild(parentRowId, rowId, this._dataId);
1050
+ view.addSegmentChild(parentRowId, rowId);
1053
1051
  }
1052
+
1053
+ return true;
1054
1054
  };
1055
1055
  /** @private
1056
1056
  * @param {Array.<string>=} rowIds
@@ -1067,7 +1067,9 @@ RowDefinition.prototype._deregisterFromView = function(rowIds) {
1067
1067
  }
1068
1068
  return "";
1069
1069
  };
1070
- /** @public
1070
+ /** Deprecated.
1071
+ * @public
1072
+ * @ignore
1071
1073
  * @function
1072
1074
  * @param {Array.<string>} rowIds
1073
1075
  * @param {RowDefinition} rowDef
@@ -1084,77 +1086,74 @@ RowDefinition.deregisterFromView = function(rowIds, rowDef) {
1084
1086
  * @return {Object}
1085
1087
  */
1086
1088
  RowDefinition.prototype._getChildStaticRowData = function(ric) {
1087
- if(!this._staticValues) {
1088
- return null;
1089
+ if(this._staticValues) {
1090
+ let childValues = this._staticValues[RowDefinition._childDataField];
1091
+ if(childValues) {
1092
+ return childValues[ric] || null;
1093
+ }
1089
1094
  }
1095
+ return null;
1096
+ };
1090
1097
 
1091
- let childValues = this._staticValues[RowDefinition._childDataField];
1092
- if(!childValues) {
1093
- return null;
1098
+ /** @public
1099
+ * @ignore
1100
+ * @param {string} ric
1101
+ * @return {RowDefinition}
1102
+ */
1103
+ RowDefinition.prototype.getConstituent = function(ric) {
1104
+ if(this._children && ric) {
1105
+ for(let i = this._children.length; --i >= 0;) {
1106
+ if(this._children[i]._ric === ric) {
1107
+ return this._children[i];
1108
+ }
1109
+ }
1094
1110
  }
1111
+ return null;
1112
+ };
1095
1113
 
1096
- return childValues[ric] || null;
1114
+ /** Check whether the given ric can be a constituent of this row definition. WARNING: permId is not handled by this method
1115
+ * @public
1116
+ * @ignore
1117
+ * @param {string} ric
1118
+ * @return {boolean} Returns true if the given ric can be its constituent
1119
+ */
1120
+ RowDefinition.prototype.verifyConstituent = function(ric) {
1121
+ if(this._isChain && ric) { // A constituent must have a valid ric
1122
+ if(this._ric !== ric) { // A constituent cannot have the same ric as its parent
1123
+ if(this._ric !== ("0#" + ric)) { // Real-time service may return ric without 0#
1124
+ return this._ric !== ("/" + ric); // Real-time service may return ric with slash (delayed ric)
1125
+ }
1126
+ }
1127
+ }
1128
+ return false;
1097
1129
  };
1098
- /** @public
1130
+ /** verifyConstituent should be called before calling this method
1131
+ * @public
1099
1132
  * @ignore
1100
1133
  * @param {string} ric
1101
1134
  * @return {RowDefinition} Return newly created child definition, if success
1102
1135
  */
1103
1136
  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
1137
  if(!(this._rowId && this._subId && this._isChain)) { // Chain must be alive (has not been disposed). Chain must be subscribed as chain
1108
1138
  return null;
1109
1139
  }
1110
1140
 
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 {
1141
+ if(!this._children) {
1120
1142
  this._children = [];
1121
1143
  }
1122
1144
 
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();
1145
+ let rowOptions = {
1146
+ "asConstituent": true,
1147
+ "ric": ric,
1148
+ "parent": this
1149
+ };
1145
1150
 
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());
1151
+ let staticData = this._getChildStaticRowData(ric); // TODO: Remove the child's static data from parent
1152
+ if(staticData) {
1153
+ rowOptions["values"] = staticData;
1155
1154
  }
1156
1155
 
1157
- return newChild ? childDef : null;
1156
+ return new RowDefinition(rowOptions); // childDef is added to this._children in the constructor
1158
1157
  };
1159
1158
 
1160
1159
  /** Used to convert autogenerated row to regular real-time row
@@ -1162,25 +1161,18 @@ RowDefinition.prototype.addConstituent = function(ric) {
1162
1161
  * @ignore
1163
1162
  */
1164
1163
  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
1164
+ if(!this.isConstituent()) {
1165
+ return; // Only a constituent can be converted to a real-time row
1170
1166
  }
1171
1167
 
1168
+ let subs = this._parent._subs;
1172
1169
  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
1170
  this._autoGenerated = false;
1177
1171
  this._parent = null;
1178
1172
  this._depthLevel = 0;
1179
1173
 
1180
- this.subscribeForUpdates();
1181
- if(this._staticValues) { // Add static value to the new allocated row
1182
- this.setRowData(this._staticValues);
1183
- }
1174
+ this.resetRowData(); // WARNING: existing real-time data is lost after this line
1175
+ this.subscribeForUpdates(subs); // Static data remains intact
1184
1176
  };
1185
1177
 
1186
1178
  /** @public
@@ -1190,11 +1182,7 @@ RowDefinition.prototype.unlinkChain = function() {
1190
1182
  return;
1191
1183
  }
1192
1184
 
1193
- let staticData = this._cloneStaticRowData();
1194
- this.unsubscribeForUpdates();
1195
-
1196
- // Restore static data
1197
- this.setStaticRowData(staticData);
1185
+ this.unsubscribeForUpdates(); // Static data remains intact
1198
1186
 
1199
1187
  let view = this._view;
1200
1188
  if(view) {
@@ -1309,6 +1297,22 @@ RowDefinition.prototype.getChildCount = function() {
1309
1297
  return (this._children) ? this._children.length : 0;
1310
1298
  };
1311
1299
  /** @public
1300
+ * @return {number}
1301
+ */
1302
+ RowDefinition.prototype.countChildInView = function() {
1303
+ if(this._children) {
1304
+ let viewCount = 0;
1305
+ let childCount = this._children.length;
1306
+ for(let i = 0; i < childCount; ++i) {
1307
+ if(this._children[i]._view) {
1308
+ ++viewCount;
1309
+ }
1310
+ }
1311
+ return viewCount;
1312
+ }
1313
+ return 0;
1314
+ };
1315
+ /** @public
1312
1316
  * @return {RowDefinition}
1313
1317
  */
1314
1318
  RowDefinition.prototype.getParent = function() {