@refinitiv-ui/efx-grid 6.0.30 → 6.0.32

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.
Files changed (42) hide show
  1. package/lib/core/dist/core.js +310 -117
  2. package/lib/core/dist/core.min.js +1 -1
  3. package/lib/core/es6/grid/Core.js +53 -28
  4. package/lib/core/es6/grid/ILayoutGrid.js +3 -3
  5. package/lib/core/es6/grid/LayoutGrid.js +67 -23
  6. package/lib/core/es6/grid/VirtualizedLayoutGrid.js +92 -55
  7. package/lib/core/es6/grid/components/Scrollbar.js +19 -1
  8. package/lib/core/es6/grid/util/SelectionList.d.ts +6 -2
  9. package/lib/core/es6/grid/util/SelectionList.js +76 -7
  10. package/lib/filter-dialog/lib/filter-dialog.js +11 -8
  11. package/lib/filter-dialog/themes/base.less +7 -3
  12. package/lib/filter-dialog/themes/elemental/dark/es5/all-elements.js +1 -1
  13. package/lib/filter-dialog/themes/elemental/dark/filter-dialog.js +1 -1
  14. package/lib/filter-dialog/themes/elemental/light/es5/all-elements.js +1 -1
  15. package/lib/filter-dialog/themes/elemental/light/filter-dialog.js +1 -1
  16. package/lib/filter-dialog/themes/halo/dark/es5/all-elements.js +1 -1
  17. package/lib/filter-dialog/themes/halo/dark/filter-dialog.js +1 -1
  18. package/lib/filter-dialog/themes/halo/light/es5/all-elements.js +1 -1
  19. package/lib/filter-dialog/themes/halo/light/filter-dialog.js +1 -1
  20. package/lib/filter-dialog/themes/solar/charcoal/es5/all-elements.js +1 -1
  21. package/lib/filter-dialog/themes/solar/charcoal/filter-dialog.js +1 -1
  22. package/lib/filter-dialog/themes/solar/pearl/es5/all-elements.js +1 -1
  23. package/lib/filter-dialog/themes/solar/pearl/filter-dialog.js +1 -1
  24. package/lib/grid/index.js +1 -1
  25. package/lib/statistics-row/es6/StatisticsRow.d.ts +25 -25
  26. package/lib/statistics-row/es6/StatisticsRow.js +9 -4
  27. package/lib/tr-grid-column-selection/es6/ColumnSelection.d.ts +2 -0
  28. package/lib/tr-grid-column-selection/es6/ColumnSelection.js +14 -0
  29. package/lib/tr-grid-content-wrap/es6/ContentWrap.d.ts +4 -4
  30. package/lib/tr-grid-content-wrap/es6/ContentWrap.js +116 -70
  31. package/lib/tr-grid-row-dragging/es6/RowDragging.d.ts +23 -1
  32. package/lib/tr-grid-row-dragging/es6/RowDragging.js +339 -40
  33. package/lib/tr-grid-util/es6/DragUI.d.ts +2 -0
  34. package/lib/tr-grid-util/es6/DragUI.js +39 -9
  35. package/lib/tr-grid-util/es6/Popup.d.ts +3 -1
  36. package/lib/tr-grid-util/es6/Popup.js +57 -23
  37. package/lib/types/es6/ContentWrap.d.ts +4 -4
  38. package/lib/types/es6/RealtimeGrid/ColumnDefinition.d.ts +2 -0
  39. package/lib/types/es6/RealtimeGrid/FieldDefinition.d.ts +4 -0
  40. package/lib/types/es6/RealtimeGrid/Grid.d.ts +1 -0
  41. package/lib/versions.json +6 -6
  42. package/package.json +1 -1
@@ -536,7 +536,7 @@ Core.prototype._groupDefs = null;
536
536
  * @return {string}
537
537
  */
538
538
  Core.getVersion = function () {
539
- return "5.1.39";
539
+ return "5.1.41";
540
540
  };
541
541
  /** {@link ElementWrapper#dispose}
542
542
  * @override
@@ -4062,47 +4062,72 @@ Core.prototype._updateColumnBounds = function () {
4062
4062
  return;
4063
4063
  }
4064
4064
 
4065
+ var sectCount = this._settings.length;
4066
+ if(!sectCount) {
4067
+ return;
4068
+ }
4069
+
4070
+ // Collecting column selection and selection ranges
4071
+ var selRanges = [];
4072
+ var pair = null;
4065
4073
  var colCount = this.getColumnCount();
4066
- var colIndices = [];
4074
+ var selIndices = [];
4067
4075
  var i;
4068
4076
  for(i = 0; i < colCount; i++) {
4069
4077
  if(this.isSelectedColumn(i)) {
4070
- colIndices.push(i);
4078
+ selIndices.push(i);
4079
+ if(!pair) {
4080
+ pair = [i, -1];
4081
+ }
4082
+ } else if(pair) {
4083
+ pair[1] = i - 1;
4084
+ selRanges.push(pair);
4085
+ pair = null;
4071
4086
  }
4072
4087
  }
4088
+ if(pair) {
4089
+ pair[1] = colCount - 1;
4090
+ selRanges.push(pair);
4091
+ pair = null;
4092
+ }
4093
+
4073
4094
  var arg = {
4074
- selectedColumns: colIndices
4095
+ "selectedColumns": selIndices,
4096
+ "selectionRanges": selRanges
4075
4097
  };
4076
4098
  this._dispatch("beforeColumnBoundUpdate", arg);
4077
4099
 
4078
- var len = this.getColumnCount();
4079
- var lftIdx = -1;
4080
- var rgtIdx = -1;
4081
- for(i = 0; i < len; ++i) {
4082
- if(this.isSelectedColumn(i)) {
4083
- rgtIdx = i;
4084
- if(lftIdx < 0) {
4085
- lftIdx = i;
4086
- }
4087
- }
4088
- }
4089
- var sectCount = this._settings.length;
4090
- if(sectCount) {
4091
- var sectionSetting = this._settings[0];
4092
- var section = sectionSetting.getSection();
4100
+ // Calculate position from ranges
4101
+ var rangeCount = selRanges.length;
4102
+ var posAry = [];
4103
+ var noBorderAry = [];
4104
+ var topSectionSettings = this._settings[0];
4105
+ var section = topSectionSettings.getSection();
4106
+ for(i = 0; i < rangeCount; ++i) {
4107
+ pair = selRanges[i];
4093
4108
  var positions = [0, 0];
4094
4109
  var noBorders = [false, false];
4095
- section.calculateColumnBounds(lftIdx, rgtIdx, positions, noBorders);
4096
- var topPx = 0;
4097
- if(sectionSetting.getType() === "title" && arg.topBoundRowIndex != null) {
4098
- topPx = this._layoutY.getLaneStart(arg.topBoundRowIndex);
4099
- }
4100
- section.updateColumnBounds(positions, noBorders, topPx);
4101
- for(i = 1; i < sectCount; i++) {
4102
- section = this._settings[i].getSection();
4103
- section.updateColumnBounds(positions, noBorders);
4110
+ section.calculateColumnBounds(pair[0], pair[1], positions, noBorders);
4111
+ if(positions[0] < positions[1]) {
4112
+ posAry.push(positions);
4113
+ noBorderAry.push(noBorders);
4104
4114
  }
4105
4115
  }
4116
+
4117
+ // Render column bounds
4118
+ var topPx = 0;
4119
+ var topBoundIdx = -1;
4120
+ if(arg["topBoundRowIndex"] != null) {
4121
+ topBoundIdx = +arg["topBoundRowIndex"];
4122
+ }
4123
+ if(topBoundIdx >= 0 && topSectionSettings.getType() === "title") {
4124
+ topPx = this._layoutY.getLaneStart(topBoundIdx);
4125
+ }
4126
+ section.updateColumnBounds(posAry, noBorderAry, topPx);
4127
+ for(i = 1; i < sectCount; i++) {
4128
+ section = this._settings[i].getSection();
4129
+ section.updateColumnBounds(posAry, noBorderAry);
4130
+ }
4106
4131
  };
4107
4132
 
4108
4133
  /** @public
@@ -692,11 +692,11 @@ ILayoutGrid.prototype.getHorizontalLayout = function () {};
692
692
  ILayoutGrid.prototype.calculateColumnBounds = function (lftIdx, rgtIdx, outPositions, outNoBorders) {};
693
693
  /** @public
694
694
  * @ignore
695
- * @param {!Array.<number>} positions Left and right bound positions in pixel
696
- * @param {!Array.<boolean>} noBorders Boolean values indicating existence of left and right CSS borders
695
+ * @param {!Array.<Array>} posAry Left and right bound positions in pixel
696
+ * @param {!Array.<Array>} noBorderAry Boolean values indicating existence of left and right CSS borders
697
697
  * @param {number=} topPx Top position of bound
698
698
  */
699
- ILayoutGrid.prototype.updateColumnBounds = function (positions, noBorders, topPx) {};
699
+ ILayoutGrid.prototype.updateColumnBounds = function (posAry, noBorderAry, topPx) {};
700
700
 
701
701
  export default ILayoutGrid;
702
702
  export { ILayoutGrid };
@@ -253,10 +253,18 @@ LayoutGrid.prototype._ctxRows;
253
253
  * @private
254
254
  */
255
255
  LayoutGrid.prototype._boundLayer = null;
256
- /** @type {Element}
256
+ /** @type {Array.<Element>}
257
+ * @private
258
+ */
259
+ LayoutGrid.prototype._colBounds = null;
260
+ /** @type {Array.<Element>}
261
+ * @private
262
+ */
263
+ LayoutGrid.prototype._colBoundCache = null;
264
+ /** @type {boolean}
257
265
  * @private
258
266
  */
259
- LayoutGrid.prototype._columnBound = null;
267
+ LayoutGrid.prototype._colSelDirty = false;
260
268
  /** @type {HScrollbar}
261
269
  * @private
262
270
  */
@@ -280,6 +288,8 @@ LayoutGrid.prototype.dispose = function () {
280
288
  }
281
289
 
282
290
  this._colCount = this._rowCount = this._activeRowEnd = this._availableRowCount = 0;
291
+ this._colBounds = this._colBoundCache = null;
292
+ this._colSelDirty = false;
283
293
 
284
294
  this._highlightedCells.length = 0;
285
295
  this._ctx = null;
@@ -2221,16 +2231,15 @@ LayoutGrid.prototype.getContextRow = function (rowIndex) {
2221
2231
  LayoutGrid.prototype.selectColumn = function (colIndex, selected) {
2222
2232
  this.enableColumnClass(colIndex, "selected-column", selected);
2223
2233
 
2224
- var columnBound = this._columnBound;
2225
- if(!columnBound) {
2226
- columnBound = this._columnBound = document.createElement("div");
2227
- columnBound.className = "selection-bound column-bound";
2228
- }
2229
- var boundLayer = this._boundLayer;
2230
- if(!boundLayer) {
2231
- boundLayer = this._boundLayer = document.createElement("div");
2232
- boundLayer.className = "cover-layer";
2233
- this._updateLayers();
2234
+ if(selected) {
2235
+ this._colSelDirty = true;
2236
+
2237
+ var boundLayer = this._boundLayer;
2238
+ if(!boundLayer) {
2239
+ boundLayer = this._boundLayer = document.createElement("div");
2240
+ boundLayer.className = "cover-layer";
2241
+ this._updateLayers();
2242
+ }
2234
2243
  }
2235
2244
  };
2236
2245
  /** @public
@@ -2333,24 +2342,56 @@ LayoutGrid.prototype.calculateColumnBounds = function (lftIdx, rgtIdx, outPositi
2333
2342
  };
2334
2343
  /** @public
2335
2344
  * @ignore
2336
- * @param {!Array.<number>} positions Left and right bound positions in pixel
2337
- * @param {!Array.<boolean>} noBorders Boolean values indicating existence of left and right CSS borders
2345
+ * @param {!Array.<Array>} posAry Left and right bound positions in pixel
2346
+ * @param {!Array.<Array>} noBorderAry Boolean values indicating existence of left and right CSS borders
2338
2347
  * @param {number=} topPx Top position of bound
2339
2348
  */
2340
- LayoutGrid.prototype.updateColumnBounds = function (positions, noBorders, topPx) {
2341
- var columnBound = this._columnBound;
2342
- if(!columnBound) {
2349
+ LayoutGrid.prototype.updateColumnBounds = function (posAry, noBorderAry, topPx) {
2350
+ if(!this._colSelDirty) {
2343
2351
  return;
2344
2352
  }
2345
2353
 
2346
- var lftPx = positions[0];
2347
- var rgtPx = positions[1];
2348
- if(lftPx >= rgtPx) {
2349
- var pn = columnBound.parentNode;
2354
+ var cbs = this._colBounds;
2355
+ var cbc = this._colBoundCache;
2356
+ if(!cbs) {
2357
+ cbs = this._colBounds = [];
2358
+ }
2359
+ if(!cbc) {
2360
+ cbc = this._colBoundCache = [];
2361
+ }
2362
+
2363
+ var rangeCount = posAry.length;
2364
+ var i;
2365
+ var pn = null; // parentNode
2366
+ var columnBound = null;
2367
+
2368
+ // Remove unused bounds from document
2369
+ var activeCount = cbs.length;
2370
+ for(i = rangeCount; i < activeCount; ++i) {
2371
+ columnBound = cbs[i];
2372
+ pn = columnBound.parentNode;
2350
2373
  if(pn) {
2351
2374
  pn.removeChild(columnBound);
2352
2375
  }
2353
- } else {
2376
+ }
2377
+ cbs.length = activeCount = rangeCount;
2378
+
2379
+ if(!rangeCount) {
2380
+ this._colSelDirty = false;
2381
+ return;
2382
+ }
2383
+
2384
+ for(i = 0; i < rangeCount; ++i) {
2385
+ var positions = posAry[i];
2386
+ var noBorders = noBorderAry[i];
2387
+ var lftPx = /** @type{number} */(positions[0]);
2388
+ var rgtPx = /** @type{number} */(positions[1]);
2389
+
2390
+ columnBound = cbc[i];
2391
+ if(!columnBound) {
2392
+ columnBound = cbc[i] = document.createElement("div");
2393
+ columnBound.className = "selection-bound column-bound";
2394
+ }
2354
2395
  columnBound.style.left = lftPx + "px";
2355
2396
  columnBound.style.width = (rgtPx - lftPx) + "px";
2356
2397
 
@@ -2360,7 +2401,10 @@ LayoutGrid.prototype.updateColumnBounds = function (positions, noBorders, topPx)
2360
2401
  columnBound.classList.toggle("no-left-bound", noBorders[0]);
2361
2402
  columnBound.classList.toggle("no-right-bound", noBorders[1]);
2362
2403
  if(this._boundLayer) {
2363
- this._boundLayer.appendChild(columnBound);
2404
+ if(!cbs[i]) {
2405
+ cbs[i] = columnBound;
2406
+ this._boundLayer.appendChild(columnBound);
2407
+ }
2364
2408
  }
2365
2409
  }
2366
2410
  };
@@ -97,10 +97,18 @@ VirtualizedLayoutGrid.prototype._selectionList = null;
97
97
  * @private
98
98
  */
99
99
  VirtualizedLayoutGrid.prototype._reverter = null;
100
- /** @type {Element}
100
+ /** @type {Array.<Element>}
101
+ * @private
102
+ */
103
+ VirtualizedLayoutGrid.prototype._rowBounds = null;
104
+ /** @type {Array.<Element>}
105
+ * @private
106
+ */
107
+ VirtualizedLayoutGrid.prototype._rowBoundCache = null;
108
+ /** @type {boolean}
101
109
  * @private
102
110
  */
103
- VirtualizedLayoutGrid.prototype._rowBound = null;
111
+ VirtualizedLayoutGrid.prototype._rowSelDirty = false;
104
112
  /** @type {Element}
105
113
  * @private
106
114
  */
@@ -166,6 +174,9 @@ VirtualizedLayoutGrid.prototype.dispose = function () {
166
174
  this._grid.dispose();
167
175
  this._dispose();
168
176
  this._reverter.dispose();
177
+
178
+ this._rowBounds = this._rowBoundCache = null;
179
+ this._rowSelDirty = false;
169
180
  if(this._rowBoundTimer) {
170
181
  clearTimeout(this._rowBoundTimer);
171
182
  this._rowBoundTimer = 0;
@@ -617,9 +628,10 @@ VirtualizedLayoutGrid.prototype.setSelectedRow = function (rowIndex, opt_selecte
617
628
  this._grid.setSelectedRow(rowIndex - this._firstIndex, selected);
618
629
 
619
630
  if(selected) {
620
- this._initRowBounds();
631
+ this._rowSelDirty = true;
632
+ this._initBoundLayer();
621
633
  }
622
- this._updateRowBounds();
634
+ this._requestUpdatingRowBounds();
623
635
  };
624
636
 
625
637
  /** @inheritDoc */
@@ -632,14 +644,15 @@ VirtualizedLayoutGrid.prototype.selectSingleRow = function (rowIndex) {
632
644
  VirtualizedLayoutGrid.prototype.selectRowRange = function (rowIndex, length) {
633
645
  this._selectionList.selectRange(rowIndex, length);
634
646
  this._updateRowSelection();
635
- this._initRowBounds();
636
- this._updateRowBounds();
647
+ this._rowSelDirty = true;
648
+ this._initBoundLayer();
649
+ this._requestUpdatingRowBounds();
637
650
  };
638
651
  /** @inheritDoc */
639
652
  VirtualizedLayoutGrid.prototype.clearSelectedRows = function () {
640
653
  var count = this._selectionList.clearAllSelections();
641
654
  this._grid.clearSelectedRows();
642
- this._updateRowBounds();
655
+ this._requestUpdatingRowBounds(); // WARNING: Row bounds are not removed from the document immediately
643
656
  return count;
644
657
  };
645
658
  /** @inheritDoc */
@@ -1081,12 +1094,12 @@ VirtualizedLayoutGrid.prototype._updateCellBounds = function () {
1081
1094
  };
1082
1095
  /** @public
1083
1096
  * @ignore
1084
- * @param {!Array.<number>} positions Left and right bound positions in pixel
1085
- * @param {!Array.<boolean>} noBorders Boolean values indicating existence of left and right CSS borders
1097
+ * @param {!Array.<Array>} posAry Left and right bound positions in pixel
1098
+ * @param {!Array.<Array>} noBorderAry Boolean values indicating existence of left and right CSS borders
1086
1099
  * @param {number=} topPx Top position of bound
1087
1100
  */
1088
- VirtualizedLayoutGrid.prototype.updateColumnBounds = function (positions, noBorders, topPx) {
1089
- this._grid.updateColumnBounds(positions, noBorders, topPx);
1101
+ VirtualizedLayoutGrid.prototype.updateColumnBounds = function (posAry, noBorderAry, topPx) {
1102
+ this._grid.updateColumnBounds(posAry, noBorderAry, topPx);
1090
1103
  this._updateRowBounds();
1091
1104
  };
1092
1105
  /** @private
@@ -1101,16 +1114,6 @@ VirtualizedLayoutGrid.prototype._initBoundLayer = function () {
1101
1114
  };
1102
1115
  /** @private
1103
1116
  */
1104
- VirtualizedLayoutGrid.prototype._initRowBounds = function () {
1105
- var rowBound = this._rowBound;
1106
- if(!rowBound) {
1107
- rowBound = this._rowBound = document.createElement("div");
1108
- rowBound.className = "selection-bound";
1109
- }
1110
- this._initBoundLayer();
1111
- };
1112
- /** @private
1113
- */
1114
1117
  VirtualizedLayoutGrid.prototype._requestUpdatingRowBounds = function () {
1115
1118
  if(!this._rowBoundTimer) {
1116
1119
  this._rowBoundTimer = setTimeout(this._updateRowBounds, 10);
@@ -1122,54 +1125,88 @@ VirtualizedLayoutGrid.prototype._updateRowBounds = function () {
1122
1125
  this._rowBoundTimer = 0;
1123
1126
  this._updateCellBounds();
1124
1127
 
1125
- var rowBound = this._rowBound;
1126
- if(!rowBound) {
1128
+ if(!this._rowSelDirty) {
1127
1129
  return;
1128
1130
  }
1129
- var topIdx = this.getFirstSelectedRow();
1130
- var btmIdx = -1; // Inclusive
1131
- var topPx = 0;
1132
- var btmPx = 0;
1133
- var rowCount = this._layoutY.getLaneCount();
1134
- if(topIdx >= rowCount) {
1135
- topIdx = rowCount - 1;
1131
+ var rbs = this._rowBounds;
1132
+ var rbc = this._rowBoundCache;
1133
+ if(!rbs) {
1134
+ rbs = this._rowBounds = [];
1136
1135
  }
1137
- if(topIdx >= 0) {
1138
- btmIdx = this.getLastSelectedRow();
1139
- if(btmIdx >= rowCount) {
1140
- btmIdx = rowCount - 1;
1141
- }
1142
- topPx = this._layoutY.getLaneStart(topIdx);
1143
- btmPx = this._layoutY.getLaneEnd(btmIdx);
1136
+ if(!rbc) {
1137
+ rbc = this._rowBoundCache = [];
1144
1138
  }
1145
1139
 
1146
- if(topPx >= btmPx) {
1147
- var pn = rowBound.parentNode;
1140
+ var selList = this._selectionList;
1141
+ var rowCount = this._layoutY.getLaneCount();
1142
+ selList.deselectFrom(rowCount); // TODO: move this to setRowCount
1143
+
1144
+ var selRanges = selList.getConnectedRanges();
1145
+ var rangeCount = selRanges.length;
1146
+ var i;
1147
+ var pn = null; // parentNode
1148
+ var rowBound = null;
1149
+
1150
+ // Remove unused bounds from document
1151
+ var activeCount = rbs.length;
1152
+ for(i = rangeCount; i < activeCount; ++i) {
1153
+ rowBound = rbs[i];
1154
+ pn = rowBound.parentNode;
1148
1155
  if(pn) {
1149
1156
  pn.removeChild(rowBound);
1150
1157
  }
1151
- } else {
1158
+ }
1159
+ rbs.length = activeCount = rangeCount;
1160
+
1161
+ if(!rangeCount) {
1162
+ var selCount = selList.getSelectionCount();
1163
+ if(!selCount) {
1164
+ this._rowSelDirty = false;
1165
+ }
1166
+ return;
1167
+ }
1168
+
1169
+ // Prepare shared parameters
1170
+ var scrollLeft = 0;
1171
+ var pinnedLftCount = 0;
1172
+ var pinnedRgtCount = 0;
1173
+ var endOfScroll = false;
1174
+ if(this._hscrollbar) {
1175
+ scrollLeft = this._hscrollbar.getScrollLeft();
1176
+ pinnedLftCount = this._hscrollbar.getPinnedLeftColumnCount();
1177
+ pinnedRgtCount = this._hscrollbar.getPinnedRightColumnCount();
1178
+ endOfScroll = this._hscrollbar.isEndOfHorizontalScroll();
1179
+ }
1180
+ var noLeftBound = !pinnedLftCount && scrollLeft > 0;
1181
+ var noRightBound = !pinnedRgtCount && !endOfScroll;
1182
+ var boundWidth = this._grid._getViewSize();
1183
+
1184
+ // Create row bound elements based on number of selection ranges
1185
+ for(i = 0; i < rangeCount; ++i) {
1186
+ var pair = selRanges[i];
1187
+ var topIdx = pair[0];
1188
+ var btmIdx = pair[1]; // Inclusive
1189
+ var topPx = this._layoutY.getLaneStart(topIdx);
1190
+ var btmPx = this._layoutY.getLaneEnd(btmIdx);
1191
+
1192
+ rowBound = rbc[i];
1193
+ if(!rowBound) {
1194
+ rowBound = rbc[i] = document.createElement("div");
1195
+ rowBound.className = "selection-bound";
1196
+ }
1197
+
1152
1198
  rowBound.style.top = topPx + "px";
1153
1199
  rowBound.style.height = (btmPx - topPx) + "px";
1154
-
1155
- var boundWidth = this._grid._getViewSize();
1156
1200
  rowBound.style.width = boundWidth + "px";
1157
1201
 
1158
- var scrollLeft = 0;
1159
- var pinnedLftCount = 0;
1160
- var pinnedRgtCount = 0;
1161
- var endOfScroll = false;
1162
- if(this._hscrollbar) {
1163
- scrollLeft = this._hscrollbar.getScrollLeft();
1164
- pinnedLftCount = this._hscrollbar.getPinnedLeftColumnCount();
1165
- pinnedRgtCount = this._hscrollbar.getPinnedRightColumnCount();
1166
- endOfScroll = this._hscrollbar.isEndOfHorizontalScroll();
1167
- }
1168
- rowBound.classList.toggle("no-left-bound", !pinnedLftCount && scrollLeft > 0);
1169
- rowBound.classList.toggle("no-right-bound", !pinnedRgtCount && !endOfScroll);
1202
+ rowBound.classList.toggle("no-left-bound", noLeftBound);
1203
+ rowBound.classList.toggle("no-right-bound", noRightBound);
1170
1204
 
1171
1205
  if(this._boundLayer) {
1172
- this._boundLayer.appendChild(rowBound);
1206
+ if(!rbs[i]) {
1207
+ rbs[i] = rowBound;
1208
+ this._boundLayer.appendChild(rowBound);
1209
+ }
1173
1210
  }
1174
1211
  }
1175
1212
  };
@@ -51,7 +51,8 @@ var Scrollbar = function () {
51
51
  t._element.appendChild(t._trackContent); // WARNING: trackContent is not registered as ElementWrapper's content
52
52
 
53
53
  t.disableKeyboardInput(false); // Enable keyboard input by default
54
- Scrollbar._queryNativeTrackThickness(t._dispatch.bind(t, "thicknessChanged"));
54
+ t._onThicknessChanged = t._dispatch.bind(t, "thicknessChanged");
55
+ Scrollbar._queryNativeTrackThickness(t._onThicknessChanged);
55
56
 
56
57
  if(!t._updateEffectiveArea()) {
57
58
  t.listen("thicknessChanged", t._updateEffectiveArea);
@@ -221,6 +222,12 @@ Scrollbar.prototype._wheelScrolling = "";
221
222
  * @ignore
222
223
  */
223
224
  Scrollbar.prototype._mouseWheelLogic = null;
225
+ /** @type {Function}
226
+ * @private
227
+ * @ignore
228
+ */
229
+ Scrollbar.prototype._onThicknessChanged = null;
230
+
224
231
 
225
232
  /** @type {number}
226
233
  * @private
@@ -328,6 +335,7 @@ Scrollbar._retrieveNativeTrackThinkness = function () {
328
335
  outer.style.overflow = 'scroll';
329
336
  var w2 = inner.offsetWidth;
330
337
 
338
+ // Sometimes, w1 may be equal to w2 on certain browsers or devices, such as a Macbook when opened on the built-in screen. In such cases, the outer.style.overflow scroll may not change the offsetWidth, and the outer.clientWidth will be the same as the inner offsetWidth. As a result, the native track thickness may not be found
331
339
  if(w1 == w2) {
332
340
  w2 = outer.clientWidth;
333
341
  }
@@ -367,6 +375,16 @@ Scrollbar.updateTrackThickness = function () {
367
375
 
368
376
  /** @override */
369
377
  Scrollbar.prototype.dispose = function () {
378
+
379
+ var sbListeners = Scrollbar._listeners;
380
+ if(sbListeners) {
381
+ var idx = sbListeners.indexOf(this._onThicknessChanged);
382
+ if(idx >= 0 ) {
383
+ sbListeners.splice(idx, 1);
384
+ this._onThicknessChanged = null;
385
+ }
386
+ }
387
+
370
388
  this.unlistenAll();
371
389
  if(this._smoothingId) {
372
390
  clearInterval(this._smoothingId);
@@ -12,6 +12,8 @@ declare class SelectionList {
12
12
 
13
13
  public deselect(at: number): boolean;
14
14
 
15
+ public deselectFrom(at: number): boolean;
16
+
15
17
  public toggleSelection(at: number): void;
16
18
 
17
19
  public singularlySelect(at: number): boolean;
@@ -36,13 +38,15 @@ declare class SelectionList {
36
38
 
37
39
  public getLastSelectedIndex(): number;
38
40
 
39
- public getAllSelections(): (number)[]|null;
41
+ public getAllSelections(): (number)[];
42
+
43
+ public getConnectedRanges(from?: number|null, to?: number|null): (number)[][];
40
44
 
41
45
  public getSelectionMap(): (boolean)[]|null;
42
46
 
43
47
  public clearAllSelections(): number;
44
48
 
45
- public copyFrom(srcSelections: SelectionList|null, fromSrcIndex: number, offsetIndex: number, forLength: number): void;
49
+ public copyFrom(srcSelections: SelectionList|null, fromSrcIndex: number, offset: number, forLength: number): void;
46
50
 
47
51
  }
48
52
 
@@ -72,6 +72,34 @@ SelectionList.prototype.deselect = function (at) {
72
72
  }
73
73
  return false;
74
74
  };
75
+ /** Deselect all selections starting from the specified index
76
+ * @public
77
+ * @param {number} at
78
+ * @return {boolean}
79
+ */
80
+ SelectionList.prototype.deselectFrom = function (at) {
81
+ if(this._lastIndex < at) {
82
+ return false;
83
+ }
84
+ if(this._firstIndex >= at) {
85
+ this.clearAllSelections();
86
+ return true;
87
+ }
88
+
89
+ var lastIndex = this._lastIndex;
90
+ var sels = this._selections;
91
+ for(var i = at; i <= lastIndex; ++i) {
92
+ if (sels[i]) {
93
+ sels[at] = false;
94
+ --this._count;
95
+ }
96
+ }
97
+ if (this._anchor >= at) {
98
+ this._anchor = -1; // No anchor
99
+ }
100
+ this._lastIndex = this._findPrevSelection(at);
101
+ return true;
102
+ };
75
103
  /** @public
76
104
  * @param {number} at
77
105
  */
@@ -268,14 +296,16 @@ SelectionList.prototype.getLastSelectedIndex = function() {
268
296
 
269
297
  /** WARNING: It will creates a new(EXPENSIVE) defragmented array of selected Index. The selected indices will always be sorted in ascending order
270
298
  * @public
271
- * @return {Array.<number>}
299
+ * @return {!Array.<number>}
272
300
  */
273
301
  SelectionList.prototype.getAllSelections = function() {
274
302
  if(this._count > 0) {
275
303
  var ary = new Array(this._count); // Fastest way to create an array
276
304
  var count = 0;
277
- for(var i = this._firstIndex; i <= this._lastIndex; ++i) {
278
- if(this._selections[i]) {
305
+ var sels = this._selections;
306
+ var lastIdx = this._lastIndex;
307
+ for(var i = this._firstIndex; i <= lastIdx; ++i) {
308
+ if(sels[i]) {
279
309
  ary[count++] = i;
280
310
  }
281
311
  }
@@ -283,6 +313,45 @@ SelectionList.prototype.getAllSelections = function() {
283
313
  }
284
314
  return [];
285
315
  };
316
+ /** Get array of connected selection ranges. For intances, if indices 1, 2, 5, and 5 are selected, array of [1, 2] and [5, 5] are returned.
317
+ * @public
318
+ * @param {number=} from
319
+ * @param {number=} to EXCLUSIVE
320
+ * @return {!Array.<Array.<number>>}
321
+ */
322
+ SelectionList.prototype.getConnectedRanges = function(from, to) {
323
+ if(this._count > 0) {
324
+ var ary = [];
325
+ if(from == null || from < this._firstIndex) {
326
+ from = this._firstIndex;
327
+ }
328
+ if(to == null || to > this._lastIndex) {
329
+ to = this._lastIndex + 1;
330
+ }
331
+
332
+ var pair = null;
333
+ for(var i = from; i < to; ++i) {
334
+ if(this._selections[i]) {
335
+ if(!pair) {
336
+ pair = [i, -1];
337
+ }
338
+ } else if(pair) {
339
+ pair[1] = i - 1;
340
+ ary.push(pair);
341
+ pair = null;
342
+ }
343
+ }
344
+
345
+ if(pair) {
346
+ pair[1] = this._lastIndex;
347
+ ary.push(pair);
348
+ pair = null;
349
+ }
350
+ return ary;
351
+ }
352
+ return [];
353
+ };
354
+
286
355
  /**
287
356
  * @public
288
357
  * @return {Array.<boolean>}
@@ -310,13 +379,13 @@ SelectionList.prototype.clearAllSelections = function() {
310
379
  * @public
311
380
  * @param {SelectionList} srcSelections
312
381
  * @param {number} fromSrcIndex
313
- * @param {number} offsetIndex
382
+ * @param {number} offset Offset from the source index to map to destination index of this SelectionList. Use 0 if there is no shifting.
314
383
  * @param {number} forLength Positive value only. negative valie is not allowed
315
384
  */
316
- SelectionList.prototype.copyFrom = function (srcSelections, fromSrcIndex, offsetIndex, forLength) {
385
+ SelectionList.prototype.copyFrom = function (srcSelections, fromSrcIndex, offset, forLength) {
317
386
  if (forLength <= 0) { return; }
318
387
 
319
- var toThisIndex = fromSrcIndex + offsetIndex;
388
+ var toThisIndex = fromSrcIndex + offset;
320
389
  if (srcSelections == null) {
321
390
  this.deselectRange(toThisIndex, forLength);
322
391
  return;
@@ -337,7 +406,7 @@ SelectionList.prototype.copyFrom = function (srcSelections, fromSrcIndex, offset
337
406
  this._anchor = -1;
338
407
  if (srcSelections._anchor >= 0) {
339
408
  if (srcSelections._anchor >= fromSrcIndex && srcSelections._anchor < (fromSrcIndex + forLength)) {
340
- this._anchor = srcSelections._anchor + offsetIndex;
409
+ this._anchor = srcSelections._anchor + offset;
341
410
  }
342
411
  }
343
412
  };