@refinitiv-ui/efx-grid 6.0.58 → 6.0.59

Sign up to get free protection for your applications and to get access to all the features.
@@ -181,9 +181,9 @@ declare class Grid extends EventDispatcher {
181
181
 
182
182
  public _initDuplicateRicData(rowDef: RowDefinition|null): void;
183
183
 
184
- public insertRow(rowOption?: any, rowRef?: Grid.RowReference|null): any;
184
+ public insertRow(rowOption?: (RowDefinition.Options|string)|null, rowRef?: Grid.RowReference|null): RowDefinition|null;
185
185
 
186
- public insertRows(rowOptions: (any)[]|null, rowRef?: Grid.RowReference|null, opt_fields?: (string)[]|null): void;
186
+ public insertRows(rowOptions: (RowDefinition.Options|string)[]|null, rowRef?: Grid.RowReference|null, opt_fields?: (string)[]|null): void;
187
187
 
188
188
  public addStaticDataRows(dataRows: any[]|null, fields?: (string)[]|null): void;
189
189
 
@@ -323,7 +323,7 @@ declare class Grid extends EventDispatcher {
323
323
 
324
324
  public logDV(opt_options?: any): void;
325
325
 
326
- public replaceRow(rowRef: Grid.RowReference|null, rowOption?: any): any;
326
+ public replaceRow(rowRef: Grid.RowReference|null, rowOption?: (RowDefinition.Options|string)|null): RowDefinition|null;
327
327
 
328
328
  public scrollToColumn(colIndex: number, leftOfView?: boolean|null): boolean;
329
329
 
@@ -1581,16 +1581,16 @@ Grid.prototype.replaceColumn = function (columnOption, colRef) {
1581
1581
  }
1582
1582
 
1583
1583
  var colDef = this.getColumnDefinition(colIndex);
1584
- if(colDef.getChildren()) { // Parent time series field doesn't provide hidden property
1584
+ if(colDef && colDef.getChildren().length > 0) { // Parent time series field doesn't provide hidden property
1585
1585
  colConfig["hidden"] = false;
1586
1586
  }
1587
1587
 
1588
- if(colConfig.id == null) {
1588
+ if(colDef && colConfig.id == null) {
1589
1589
  colConfig.id = colDef.getId(); // retain ID
1590
1590
  }
1591
1591
  this._grid.startBatch("reset");
1592
+ this.removeColumn(colIndex);
1592
1593
  this.insertColumn(colConfig, colIndex);
1593
- this.removeColumn(colIndex + 1); // remove existing column after insert
1594
1594
  this._grid.stopBatch("reset");
1595
1595
  };
1596
1596
 
@@ -2295,9 +2295,9 @@ Grid.prototype._initDuplicateRicData = function(rowDef) {
2295
2295
  }
2296
2296
  };
2297
2297
  /** @public
2298
- * @param {Object=} rowOption
2298
+ * @param {(RowDefinition~Options|string)=} rowOption
2299
2299
  * @param {Grid~RowReference=} rowRef Reference (i.e. row index, row id, or row definition) of the insert position
2300
- * @returns {Object}
2300
+ * @returns {RowDefinition}
2301
2301
  * @example
2302
2302
  * var grid = new rt.Grid(grid_div, options);
2303
2303
  * grid.insertRow({"ric": "RIC"}, 0); // A new row is added at the top
@@ -2328,7 +2328,7 @@ Grid.prototype.insertRow = function(rowOption, rowRef) {
2328
2328
  return rowDef;
2329
2329
  };
2330
2330
  /** @public
2331
- * @param {Array.<Object>} rowOptions Array of row option object
2331
+ * @param {Array.<RowDefinition~Options|string>} rowOptions Array of row option object
2332
2332
  * @param {Grid~RowReference=} rowRef Reference (i.e. row index, row id, or row definition) of the insert position
2333
2333
  * @param {Array.<string>=} opt_fields
2334
2334
  * @example
@@ -3978,8 +3978,8 @@ Grid.prototype._logData = function(rowDefs, options) {
3978
3978
  /** @public
3979
3979
  * @description Replace existing row with a new row. Row ID would be changed, after row is replaced.
3980
3980
  * @param {Grid~RowReference} rowRef Reference (i.e. row index, row id, or row definition) of the insert position
3981
- * @param {Object=} rowOption
3982
- * @returns {Object}
3981
+ * @param {(RowDefinition~Options|string)=} rowOption
3982
+ * @returns {RowDefinition}
3983
3983
  */
3984
3984
  Grid.prototype.replaceRow = function(rowRef, rowOption) {
3985
3985
  var rowId = this._getRowId(rowRef);
@@ -14,7 +14,9 @@ declare namespace ColumnStackPlugin {
14
14
  fields: (string)[]|null,
15
15
  stacks: (ColumnStackPlugin.StackDefinition)[]|null,
16
16
  autoStacking?: boolean|null,
17
- clicked?: ((...params: any[]) => any)|null
17
+ clicked?: ((...params: any[]) => any)|null,
18
+ menuElement?: Element|null,
19
+ menuItemClicked?: ((...params: any[]) => any)|null
18
20
  };
19
21
 
20
22
  type ColumnOptions = {
@@ -10,10 +10,12 @@ import { preventDefault } from "../../tr-grid-util/es6/EventDispatcher.js";
10
10
 
11
11
  /** @typedef {Object} ColumnStackPlugin~Options
12
12
  * @description Available options describing `columnStack` object specified in grid's option
13
- * @property {Array.<string>} fields Fields for stacking. The minimum is 2 fields.
13
+ * @property {Array.<string>} fields Fields for stacking. The minimum is 2 fields
14
14
  * @property {Array.<ColumnStackPlugin~StackDefinition>} stacks List of stacking configuration
15
15
  * @property {boolean=} autoStacking Deprecated. Stacks are automatically maintained as long as its members exist
16
16
  * @property {Function=} clicked=null Event handler when user clicks on stack/expanding icon
17
+ * @property {Element=} menuElement Element used to display a column selection list
18
+ * @property {Function=} menuItemClicked When the menuElement is specified and menu item is clicked, an event handler is called
17
19
  */
18
20
 
19
21
  /** @typedef {Object} ColumnStackPlugin~ColumnOptions
@@ -122,6 +124,7 @@ var ColumnStackPlugin = function () {
122
124
  this._onBeforeBatchOperation = this._onBeforeBatchOperation.bind(this);
123
125
  this._onAfterBatchOperation = this._onAfterBatchOperation.bind(this);
124
126
  this._onPinningChanged = this._onPinningChanged.bind(this);
127
+ this._onMenuItemClicked = this._onMenuItemClicked.bind(this);
125
128
 
126
129
  this._onStackButtonClicked = this._onStackButtonClicked.bind(this);
127
130
  this._updateUI = this._updateUI.bind(this);
@@ -165,6 +168,14 @@ ColumnStackPlugin.prototype._inResetting = false;
165
168
  * @private
166
169
  */
167
170
  ColumnStackPlugin.prototype._inPinning = false;
171
+ /** @type {Element}
172
+ * @private
173
+ */
174
+ ColumnStackPlugin.prototype._menuElement = null;
175
+ /** @type {Function}
176
+ * @private
177
+ */
178
+ ColumnStackPlugin.prototype._menuItemClicked = null;
168
179
 
169
180
  /** @type {number}
170
181
  * @private
@@ -201,12 +212,28 @@ ColumnStackPlugin.prototype.initialize = function (host, options) {
201
212
  if(this._hosts.indexOf(host) >= 0) { return; }
202
213
  this._hosts.push(host);
203
214
 
204
- if(options && options["icons"]) {
205
- var icons = options["icons"]["columnStack"];
206
- if(icons) {
207
- this._stackIconName = icons["stack"];
208
- this._expandIconName = icons["expand"];
209
- this._collapseIconName = icons["collapse"];
215
+ if(options) {
216
+ if(options["icons"]) {
217
+ var icons = options["icons"]["columnStack"];
218
+ if(icons) {
219
+ this._stackIconName = icons["stack"];
220
+ this._expandIconName = icons["expand"];
221
+ this._collapseIconName = icons["collapse"];
222
+ }
223
+ }
224
+
225
+ var opt = options["columnStack"];
226
+ if(opt && this._hosts.length === 1) {
227
+ var menuElement = opt["menuElement"];
228
+ if(menuElement && typeof menuElement === "object" && menuElement.nodeType === 1) {
229
+ menuElement.addEventListener("item-trigger", this._onMenuItemClicked);
230
+ this._menuElement = menuElement;
231
+ }
232
+
233
+ var menuItemClicked = opt["menuItemClicked"];
234
+ if(typeof menuItemClicked === "function") {
235
+ this._menuItemClicked = menuItemClicked;
236
+ }
210
237
  }
211
238
  }
212
239
 
@@ -242,6 +269,10 @@ ColumnStackPlugin.prototype.unload = function (host) {
242
269
  host.unlisten("preSectionRender", this._onPreSectionRender);
243
270
  host.unlisten("pinningChanged", this._onPinningChanged);
244
271
 
272
+ if(this._menuElement) {
273
+ this._menuElement.removeEventListener("item-trigger", this._onMenuItemClicked);
274
+ }
275
+
245
276
  if(this._hosts.length <= 0) {
246
277
  this._conflator.reset();
247
278
  this._groupDefs.removeAllGroups();
@@ -1608,7 +1639,8 @@ ColumnStackPlugin.prototype._onStackButtonClicked = function(e) {
1608
1639
  value: i,
1609
1640
  selected: i === activeIndex,
1610
1641
  label: this.getColumnName(colIndices[i]),
1611
- field: this.getColumnField(colIndices[i])
1642
+ field: this.getColumnField(colIndices[i]),
1643
+ colId: children[i]
1612
1644
  };
1613
1645
  }
1614
1646
  pos["menuData"] = menuData;
@@ -1622,9 +1654,33 @@ ColumnStackPlugin.prototype._onStackButtonClicked = function(e) {
1622
1654
  pos["stackId"] = colData.id;
1623
1655
 
1624
1656
  elem.focus();
1657
+
1658
+ if(this._menuElement && !colData.spreading) {
1659
+ // Populate stack member
1660
+ var button = pos.event.srcElement;
1661
+ var el = this._menuElement;
1662
+ el.data = pos["menuData"];
1663
+ el.positionTarget = button;
1664
+ el.opened = true;
1665
+ }
1625
1666
  this._dispatch("clicked", pos);
1626
1667
  };
1668
+ /** @private
1669
+ * @param {Object} e
1670
+ */
1671
+ ColumnStackPlugin.prototype._onMenuItemClicked = function(e) {
1672
+ if(e.detail) {
1673
+ var selectedIndex = parseInt(e.detail.value, 0);
1674
+ var menuData = this._menuElement.data[selectedIndex];
1675
+ var colId = menuData.colId;
1676
+ this.setActiveColumn(colId);
1677
+ }
1678
+ this._menuElement.opened = false;
1627
1679
 
1680
+ if(this._menuItemClicked) {
1681
+ this._menuItemClicked(e);
1682
+ }
1683
+ };
1628
1684
  /** @public
1629
1685
  * @description Get member column indices in a stack
1630
1686
  * @param {string} stackId
@@ -38,7 +38,8 @@ ElementObserver._getNewId = function () {
38
38
  return id;
39
39
  };
40
40
 
41
- /** @private Observe any element
41
+ /** Observe any element
42
+ * @private
42
43
  * @param {Element} elem
43
44
  * @param {Function} listener
44
45
  * @param {Object=} opt_option
@@ -101,7 +102,8 @@ ElementObserver.addLanguageListener = function(element) {
101
102
  ElementObserver._addObserver(document.documentElement, _onLanguageMutated.bind(null, element));
102
103
  };
103
104
 
104
- /** @public Add a listener to a html attribute
105
+ /** Add a listener to a html attribute
106
+ * @public
105
107
  * @param {Element} element An element within the DOM tree to watch for changes
106
108
  * @param {Function} listener A function which will be called on each attribute change
107
109
  * @param {string=} attributeName If not specified, listener will be called on every attribute change
@@ -63,7 +63,8 @@ ElementWrapper.prototype.getMousePosition = function(e, retObj) {
63
63
  */
64
64
  ElementWrapper.prototype.addEventListener = function(type, listener) {
65
65
  ElementWrapper.base(this, "addEventListener", type, listener);
66
- if(this._elem["on" + type] != null) { // TODO: This won't work with Custom Element
66
+ // TODO: This won't work with Custom Element
67
+ if(this._elem["on" + type] !== undefined) { // eslint-disable-line
67
68
  this._elem.addEventListener(type, listener, false);
68
69
  }
69
70
  };
@@ -74,7 +75,7 @@ ElementWrapper.prototype.addEventListener = function(type, listener) {
74
75
  */
75
76
  ElementWrapper.prototype.removeEventListener = function(type, listener) {
76
77
  ElementWrapper.base(this, "removeEventListener", type, listener);
77
- if(this._elem["on" + type] != null) {
78
+ if(this._elem["on" + type] !== undefined) { // eslint-disable-line
78
79
  this._elem.removeEventListener(type, listener, false);
79
80
  }
80
81
  };
@@ -259,6 +259,11 @@ GridPlugin.prototype.getColumnIndex = function (colRef) {
259
259
  * @return {Array.<number>} column indices
260
260
  */
261
261
  GridPlugin.prototype.getColumnIndices = function (colRefs) {
262
+ var api = this.getGridApi();
263
+ if(api.getColumnIndices) {
264
+ return api.getColumnIndices(colRefs);
265
+ }
266
+
262
267
  // TODO: Unify the below logics
263
268
  if(this._compositeGrid) {
264
269
  var allFields = this._compositeGrid.getColumnFields();
@@ -30,9 +30,9 @@ declare class SubTable extends ElementWrapper {
30
30
 
31
31
  public getAllCells(): (Element)[];
32
32
 
33
- public getAllRows(): HTMLCollection|null;
33
+ public getAllRows(): (Element)[];
34
34
 
35
- public getRows(): HTMLCollection|null;
35
+ public getRows(): (Element)[];
36
36
 
37
37
  public getRow(r: number): Element|null;
38
38
 
@@ -70,5 +70,7 @@ declare class SubTable extends ElementWrapper {
70
70
 
71
71
  declare function tdElem(e: Event|null): number;
72
72
 
73
+ declare function trElem(colIndex: number, rowIndex?: number|null): number;
74
+
73
75
  export default SubTable;
74
76
  export { SubTable };
@@ -49,7 +49,7 @@ SubTable.prototype.addColumns = function(opt_count) {
49
49
 
50
50
  this._colCount += opt_count;
51
51
 
52
- var rows = this.getRows(); // TODO: Must include all suspended rows
52
+ var rows = this._elem.children; // TODO: Must include all suspended rows
53
53
  for(var r = rows.length; --r >= 0;) {
54
54
  var tr = rows[r];
55
55
  for(var c = 0; c < opt_count; ++c) {
@@ -68,7 +68,7 @@ SubTable.prototype.removeColumns = function(opt_count) {
68
68
  if(opt_count <= 0) { return; }
69
69
 
70
70
  this._colCount -= opt_count;
71
- var rows = this.getRows(); // TODO: Must include all suspended rows
71
+ var rows = this._elem.children; // TODO: Must include all suspended rows
72
72
  for(var r = rows.length; --r >= 0;) {
73
73
  Dom.removeChildren(rows[r], opt_count);
74
74
  }
@@ -158,7 +158,7 @@ SubTable.prototype.setDefaultRowHeight = function(val) {
158
158
  this._defaultRowHeight = val;
159
159
  var minHeight = (this._defaultRowHeight != null) ? this._defaultRowHeight + "px" : "";
160
160
 
161
- this._applyDefaultRowHeight(this.getRows(), minHeight);
161
+ this._applyDefaultRowHeight(this._elem.children, minHeight);
162
162
  };
163
163
 
164
164
  /** @public
@@ -194,7 +194,7 @@ SubTable.prototype.getCellsInColumn = function(c) {
194
194
  if(c < 0 || c >= this._colCount) {
195
195
  return null;
196
196
  }
197
- var rows = this.getRows();
197
+ var rows = this._elem.children;
198
198
  var rowCount = rows.length;
199
199
  var ary = new Array(rowCount);
200
200
  for(var r = 0; r < rowCount; ++r) {
@@ -209,13 +209,22 @@ SubTable.prototype.getCellsInColumn = function(c) {
209
209
  */
210
210
  SubTable.prototype.getCellsInRow = function(r) {
211
211
  var tr = this.getRow(r);
212
- return (tr) ? tr.cells : null;
212
+ if(tr) {
213
+ var cells = tr.cells;
214
+ var len = cells.length;
215
+ var ary = new Array(len);
216
+ for(var i = 0; i < len; ++i) {
217
+ ary[i] = cells[i];
218
+ }
219
+ return ary;
220
+ }
221
+ return null;
213
222
  };
214
223
  /** @public
215
224
  * @return {!Array.<Element>} Array of td (HTMLTableCellElement) elements
216
225
  */
217
226
  SubTable.prototype.getAllCells = function() {
218
- var rows = this.getRows();
227
+ var rows = this._elem.children;
219
228
  var rowCount = rows.length;
220
229
  var colCount = this._colCount;
221
230
  var cellCount = 0;
@@ -229,14 +238,20 @@ SubTable.prototype.getAllCells = function() {
229
238
  return ary;
230
239
  };
231
240
  /** @public
232
- * @return {HTMLCollection} Array like collection of tr (HTMLTableRowElement) elements
241
+ * @return {!Array.<Element>} Array of tr (HTMLTableRowElement) elements
233
242
  */
234
243
  SubTable.prototype.getAllRows = function() {
235
- return this._elem.children;
244
+ var chdr = this._elem.children;
245
+ var len = chdr ? chdr.length : 0;
246
+ var ary = new Array(len);
247
+ for(var i = 0; i < len; ++i) {
248
+ ary[i] = chdr[i];
249
+ }
250
+ return ary;
236
251
  };
237
252
  /** @public
238
253
  * @function
239
- * @return {HTMLCollection} Array like collection of tr (HTMLTableRowElement) elements
254
+ * @return {!Array.<Element>} Array of tr (HTMLTableRowElement) elements
240
255
  */
241
256
  SubTable.prototype.getRows = SubTable.prototype.getAllRows;
242
257
  /** @public
@@ -251,7 +266,7 @@ SubTable.prototype.getRow = function(r) {
251
266
  * @return {!Array.<Array.<string>>}
252
267
  */
253
268
  SubTable.prototype.getTextContents = function() {
254
- var rows = this.getRows();
269
+ var rows = this._elem.children;
255
270
  var rowCount = rows.length;
256
271
  var rowContents = new Array(rowCount);
257
272
  for(var r = 0; r < rowCount; ++r) {
@@ -281,7 +296,7 @@ SubTable.prototype.getCellTextContent = function(c, r) {
281
296
  * @return {string}
282
297
  */
283
298
  SubTable.prototype.getColumnTextContent = function(c) { // New-line delimited
284
- var rows = this.getRows();
299
+ var rows = this._elem.children;
285
300
  var rowCount = rows.length;
286
301
  if(c >= 0 && c < this._colCount && rowCount > 0) {
287
302
  var str = rows[0].cells[c].textContent;
@@ -311,7 +326,7 @@ SubTable.prototype.getRowTextContent = function(r) { // Tab delimited
311
326
  * @return {string}
312
327
  */
313
328
  SubTable.prototype.getTableTextContent = function() { // Tab delimited
314
- var rowCount = this.getRows().length;
329
+ var rowCount = this._elem.children.length;
315
330
  if(rowCount > 0) {
316
331
  var str = this.getRowTextContent(0);
317
332
  for(var r = 1; r < rowCount; ++r) {
@@ -326,9 +341,10 @@ SubTable.prototype.getTableTextContent = function() { // Tab delimited
326
341
  */
327
342
  SubTable.prototype.toString = function() {
328
343
  var str = this.getElement().outerHTML;
344
+ str = str.replace(/>\s+</g, "><");
329
345
  str = str.replace(/><tr/g, ">\n\t<tr");
330
346
  str = str.replace(/><td/g, ">\n\t\t<td");
331
- str = str.replace(/><td/g, ">\n\t\t<th"); // TH can occur in thead
347
+ str = str.replace(/><th/g, ">\n\t\t<th"); // TH can occur in thead
332
348
  str = str.replace(/><\/tr/g, ">\n\t</tr");
333
349
  var tagName = this.getElement().tagName.toLowerCase();
334
350
  str = str.replace("</" + tagName, "\n</" + tagName);
@@ -340,39 +356,40 @@ SubTable.prototype.toString = function() {
340
356
  * @return {number}
341
357
  */
342
358
  SubTable.prototype.getColumnIndex = function(e) {
343
- if(e) {
344
- var len, i;
345
- var tgt = /** @type{Node} */(e.target);
346
- if(this._elem.contains(tgt)) {
347
- var tdElem = Dom.closestTagName(tgt, "TD");
348
- if(!tdElem) {
349
- tdElem = Dom.closestTagName(tgt, "TH");
359
+ if(!e) {
360
+ return -1;
361
+ }
362
+ var len, i;
363
+ var tgt = /** @type{Node} */(e.target); // TODO: Support Shadow Root
364
+ if(this._elem.contains(tgt)) {
365
+ var tdElem = Dom.closestTagName(tgt, "TD");
366
+ if(!tdElem) {
367
+ tdElem = Dom.closestTagName(tgt, "TH");
368
+ }
369
+ if(tdElem) {
370
+ var chdr = tdElem.parentElement.children;
371
+ len = chdr.length;
372
+ for(i = 0; i < len; ++i) {
373
+ if(tdElem === chdr[i]) {
374
+ return i;
375
+ }
350
376
  }
351
- if(tdElem) {
352
- var chdr = tdElem.parentElement.children;
353
- len = chdr.length;
377
+ }
378
+ } else { // In case of the target is not a child of this element
379
+ var rows = this._elem.children;
380
+ var cells = (rows[0]) ? rows[0].cells : null;
381
+ if(cells) {
382
+ var pos = Dom.getRelativePosition(e, this._elem);
383
+ var x = pos["x"];
384
+ if(x >= 0) {
385
+ len = cells.length;
354
386
  for(i = 0; i < len; ++i) {
355
- if(tdElem === chdr[i]) {
387
+ x -= cells[i].offsetWidth;
388
+ if(x < 0) { // Not include the right border
356
389
  return i;
357
390
  }
358
391
  }
359
392
  }
360
- } else { // In case of the target is not a child of this element
361
- var rows = this.getRows();
362
- var cells = (rows[0]) ? rows[0].cells : null;
363
- if(cells) {
364
- var pos = Dom.getRelativePosition(e, this._elem);
365
- var x = pos["x"];
366
- if(x >= 0) {
367
- len = cells.length;
368
- for(i = 0; i < len; ++i) {
369
- x -= cells[i].offsetWidth;
370
- if(x < 0) { // Not include the right border
371
- return i;
372
- }
373
- }
374
- }
375
- }
376
393
  }
377
394
  }
378
395
  return -1;
@@ -382,27 +399,36 @@ SubTable.prototype.getColumnIndex = function(e) {
382
399
  * @return {number}
383
400
  */
384
401
  SubTable.prototype.getRowIndex = function(e) {
385
- var rows = this.getRows();
386
- if(rows && e) {
387
- var jLen = rows.length;
388
- var tgtElem = e.target; // TODO: Support Shadow Root
389
- for(var j = 0; j < jLen; ++j) {
390
- var row = rows[j];
391
- if(row === tgtElem) {
392
- return j;
402
+ if(!e) {
403
+ return -1;
404
+ }
405
+ var rows = this._elem.children; // This return HTML collection
406
+ var rowCount = rows ? rows.length : 0;
407
+ if(!rowCount) {
408
+ return -1;
409
+ }
410
+ var i;
411
+ var tgt = /** @type{Node} */(e.target); // TODO: Support Shadow Root
412
+ if(this._elem.contains(tgt)) {
413
+ var trElem = Dom.closestTagName(tgt, "TR");
414
+ if(trElem) {
415
+ for(i = 0; i < rowCount; ++i) {
416
+ var row = rows[i];
417
+ if(row === trElem) {
418
+ return i;
419
+ }
393
420
  }
394
421
  }
422
+ }
395
423
 
396
- // In case of the target is not a child of this element
397
- var pos = Dom.getRelativePosition(e, this._elem);
398
- var y = pos["y"];
399
- if(y >= 0) {
400
- var len = rows.length;
401
- for(var i = 0; i < len; ++i) {
402
- y -= rows[i].offsetHeight;
403
- if(y < 0) { // Not include the right border
404
- return i;
405
- }
424
+ // In case of the target is not a child of this element
425
+ var pos = Dom.getRelativePosition(e, this._elem);
426
+ var y = pos["y"];
427
+ if(y >= 0) {
428
+ for(i = 0; i < rowCount; ++i) {
429
+ y -= rows[i].offsetHeight;
430
+ if(y < 0) { // Not include the right border
431
+ return i;
406
432
  }
407
433
  }
408
434
  }
@@ -458,31 +484,69 @@ SubTable.prototype.setCellRenderer = function(func) {
458
484
  * @param {string=} opt_elementType
459
485
  */
460
486
  SubTable.prototype.cloak = function(elem, opt_elementType) {
461
- SubTable["base"](this, "cloak", elem, "tr");
462
- var rows = elem.getElementsByTagName("TR");
463
- this._colCount = rows[0] ? rows[0].children.length : 0;
487
+ if(elem) {
488
+ this._elem = elem;
489
+ var rows = elem.getElementsByTagName("TR");
490
+ this._colCount = rows[0] ? rows[0].children.length : 0;
491
+ }
464
492
  };
465
493
 
466
494
  /** @public
467
- * @param {number} c1
468
- * @param {number} c2
469
- * @param {number} r1
470
- * @param {number} r2
495
+ * @param {number} c1 Starting column index
496
+ * @param {number} c2 Destination column index
497
+ * @param {number} r1 Starting row index
498
+ * @param {number} r2 Destination row index
471
499
  * @return {Element} Top left cell element
472
500
  */
473
501
  SubTable.prototype.spanBlock = function (c1, c2, r1, r2) { // WARNING: It's c c r r
474
- var cell = null;
475
- for(var c = c2; c >= c1; --c) {
476
- for(var r = r2; r >= r1; --r) {
502
+ var cell = this.getCell(c1, r1);
503
+ if(!cell) {
504
+ return null;
505
+ }
506
+ if(c2 < c1) {
507
+ c2 = c1;
508
+ }
509
+ if(r2 < r1) {
510
+ r2 = r1;
511
+ }
512
+ var curColSpan = +cell.getAttribute("colspan");
513
+ var curRowSpan = +cell.getAttribute("rowspan");
514
+ var c3 = (curColSpan) ? c1 + curColSpan - 1 : c1;
515
+ var r3 = (curRowSpan) ? r1 + curRowSpan - 1 : r1;
516
+
517
+ // TODO: Optimize below logics
518
+ var c, r;
519
+ for(c = c3; c >= c1; --c) {
520
+ for(r = r3; r >= r1; --r) {
521
+ cell = this.getCell(c, r);
522
+ if(cell) {
523
+ cell.style.display = "";
524
+ }
525
+ }
526
+ }
527
+
528
+ for(c = c2; c >= c1; --c) {
529
+ for(r = r2; r >= r1; --r) {
477
530
  cell = this.getCell(c, r);
478
531
  if(cell) {
479
532
  cell.style.display = "none";
480
533
  }
481
534
  }
482
535
  }
483
- if(cell) {
484
- cell.setAttribute("colspan", (c2 - c1 + 1));
485
- cell.setAttribute("rowspan", (r2 - r1 + 1));
536
+
537
+ if(cell) { // The last cell from the loop is the top left cell
538
+ var colSpan = (c2 - c1 + 1);
539
+ var rowSpan = (r2 - r1 + 1);
540
+ if(colSpan > 1) {
541
+ cell.setAttribute("colspan", colSpan);
542
+ } else {
543
+ cell.removeAttribute("colspan");
544
+ }
545
+ if(colSpan > 1) {
546
+ cell.setAttribute("rowspan", rowSpan);
547
+ } else {
548
+ cell.removeAttribute("rowspan");
549
+ }
486
550
  cell.style.display = "";
487
551
  }
488
552
  return cell;
@@ -526,7 +590,7 @@ SubTable.parseTableContent = function(tbl) {
526
590
  };
527
591
 
528
592
  /** @private
529
- * @param {!NodeList<!Element>|Array.<Element>} rows
593
+ * @param {!(NodeList<!Element>|Array.<Element>)} rows
530
594
  * @param {string} minHeight
531
595
  */
532
596
  SubTable.prototype._applyDefaultRowHeight = function(rows, minHeight) {