1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies Inc.
  5  Copyright (c) 2010 Sangwoo Im
  6 
  7  http://www.cocos2d-x.org
  8 
  9  Permission is hereby granted, free of charge, to any person obtaining a copy
 10  of this software and associated documentation files (the "Software"), to deal
 11  in the Software without restriction, including without limitation the rights
 12  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 13  copies of the Software, and to permit persons to whom the Software is
 14  furnished to do so, subject to the following conditions:
 15 
 16  The above copyright notice and this permission notice shall be included in
 17  all copies or substantial portions of the Software.
 18 
 19  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 20  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 21  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 22  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 23  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 24  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 25  THE SOFTWARE.
 26  ****************************************************************************/
 27 
 28 /**
 29  * The constant value of the fill style from top to bottom for cc.TableView
 30  * @constant
 31  * @type {number}
 32  */
 33 cc.TABLEVIEW_FILL_TOPDOWN = 0;
 34 
 35 /**
 36  * The constant value of the fill style from bottom to top for cc.TableView
 37  * @constant
 38  * @type {number}
 39  */
 40 cc.TABLEVIEW_FILL_BOTTOMUP = 1;
 41 
 42 /**
 43  * Abstract class for SWTableView cell node
 44  * @class
 45  * @abstract
 46  * @extend cc.Node
 47  *
 48  * @property {Number}   objectId    - The index used internally by SWTableView and its subclasses
 49  */
 50 cc.TableViewCell = cc.Node.extend(/** @lends cc.TableViewCell# */{
 51     _idx:0,
 52     _className:"TableViewCell",
 53 
 54     /**
 55      * The index used internally by SWTableView and its subclasses
 56      */
 57     getIdx:function () {
 58         return this._idx;
 59     },
 60     setIdx:function (idx) {
 61         this._idx = idx;
 62     },
 63 
 64     /**
 65      * Cleans up any resources linked to this cell and resets <code>idx</code> property.
 66      */
 67     reset:function () {
 68         this._idx = cc.INVALID_INDEX;
 69     },
 70 
 71     setObjectID:function (idx) {
 72         this._idx = idx;
 73     },
 74     getObjectID:function () {
 75         return this._idx;
 76     }
 77 });
 78 
 79 var _p = cc.TableViewCell.prototype;
 80 
 81 /** @expose */
 82 _p.objectId;
 83 cc.defineGetterSetter(_p, "objectId", _p.getObjectID, _p.setObjectID);
 84 
 85 _p = null;
 86 
 87 /**
 88  * Sole purpose of this delegate is to single touch event in this version.
 89  */
 90 cc.TableViewDelegate = cc.ScrollViewDelegate.extend(/** @lends cc.TableViewDelegate# */{
 91     /**
 92      * Delegate to respond touch event
 93      *
 94      * @param {cc.TableView} table table contains the given cell
 95      * @param {cc.TableViewCell} cell  cell that is touched
 96      */
 97     tableCellTouched:function (table, cell) {
 98     },
 99 
100     /**
101      * Delegate to respond a table cell press event.
102      *
103      * @param {cc.TableView} table table contains the given cell
104      * @param {cc.TableViewCell} cell  cell that is pressed
105      */
106     tableCellHighlight:function(table, cell){
107     },
108 
109     /**
110      * Delegate to respond a table cell release event
111      *
112      * @param {cc.TableView} table table contains the given cell
113      * @param {cc.TableViewCell} cell  cell that is pressed
114      */
115     tableCellUnhighlight:function(table, cell){
116 
117     },
118 
119     /**
120      * <p>
121      * Delegate called when the cell is about to be recycled. Immediately                     <br/>
122      * after this call the cell will be removed from the scene graph and                      <br/>
123      * recycled.
124      * </p>
125      * @param table table contains the given cell
126      * @param cell  cell that is pressed
127      */
128     tableCellWillRecycle:function(table, cell){
129 
130     }
131 });
132 
133 /**
134  * Data source that governs table backend data.
135  */
136 cc.TableViewDataSource = cc.Class.extend(/** @lends cc.TableViewDataSource# */{
137     /**
138      * cell size for a given index
139      * @param {cc.TableView} table table to hold the instances of Class
140      * @param {Number} idx the index of a cell to get a size
141      * @return {cc.Size} size of a cell at given index
142      */
143     tableCellSizeForIndex:function(table, idx){
144         return this.cellSizeForTable(table);
145     },
146     /**
147      * cell height for a given table.
148      *
149      * @param {cc.TableView} table table to hold the instances of Class
150      * @return {cc.Size} cell size
151      */
152     cellSizeForTable:function (table) {
153         return cc.size(0,0);
154     },
155 
156     /**
157      * a cell instance at a given index
158      * @param {cc.TableView} table table to hold the instances of Class
159      * @param idx index to search for a cell
160      * @return {cc.TableView} cell found at idx
161      */
162     tableCellAtIndex:function (table, idx) {
163         return null;
164     },
165 
166     /**
167      * Returns number of cells in a given table view.
168      * @param {cc.TableView} table table to hold the instances of Class
169      * @return {Number} number of cells
170      */
171     numberOfCellsInTableView:function (table) {
172         return 0;
173     }
174 });
175 
176 /**
177  * UITableView counterpart for cocos2d for iphone.
178  * this is a very basic, minimal implementation to bring UITableView-like component into cocos2d world.
179  *
180  * @class
181  * @extend cc.ScrollView
182  *
183  * @property {cc.TableViewDataSource}   dataSource          - The data source of the table view
184  * @property {cc.TableViewDelegate}     delegate            - The event delegate of the table view
185  * @property {Number}                   verticalFillOrder   - The index to determine how cell is ordered and filled in the view
186  *
187  */
188 cc.TableView = cc.ScrollView.extend(/** @lends cc.TableView# */{
189     _vOrdering:null,
190     _indices:null,
191     _cellsFreed:null,
192     _dataSource:null,
193     _tableViewDelegate:null,
194     _oldDirection:null,
195     _cellsPositions:null,                       //vector with all cell positions
196     _touchedCell:null,
197 
198     ctor:function () {
199         cc.ScrollView.prototype.ctor.call(this);
200         this._oldDirection = cc.SCROLLVIEW_DIRECTION_NONE;
201         this._cellsPositions = [];
202     },
203 
204     __indexFromOffset:function (offset) {
205         var low = 0;
206         var high = this._dataSource.numberOfCellsInTableView(this) - 1;
207         var search;
208         switch (this.getDirection()) {
209             case cc.SCROLLVIEW_DIRECTION_HORIZONTAL:
210                 search = offset.x;
211                 break;
212             default:
213                 search = offset.y;
214                 break;
215         }
216 
217         var locCellsPositions = this._cellsPositions;
218         while (high >= low){
219             var index = 0|(low + (high - low) / 2);
220             var cellStart = locCellsPositions[index];
221             var cellEnd = locCellsPositions[index + 1];
222 
223             if (search >= cellStart && search <= cellEnd){
224                 return index;
225             } else if (search < cellStart){
226                 high = index - 1;
227             }else {
228                 low = index + 1;
229             }
230         }
231 
232         if (low <= 0)
233             return 0;
234         return -1;
235     },
236 
237     _indexFromOffset:function (offset) {
238         var locOffset = {x: offset.x, y: offset.y};
239         var locDataSource = this._dataSource;
240         var maxIdx = locDataSource.numberOfCellsInTableView(this) - 1;
241 
242         if (this._vOrdering === cc.TABLEVIEW_FILL_TOPDOWN)
243             locOffset.y = this.getContainer().getContentSize().height - locOffset.y;
244 
245         var index = this.__indexFromOffset(locOffset);
246         if (index != -1) {
247             index = Math.max(0, index);
248             if (index > maxIdx)
249                 index = cc.INVALID_INDEX;
250         }
251         return index;
252     },
253 
254     __offsetFromIndex:function (index) {
255         var offset;
256         switch (this.getDirection()) {
257             case cc.SCROLLVIEW_DIRECTION_HORIZONTAL:
258                 offset = cc.p(this._cellsPositions[index], 0);
259                 break;
260             default:
261                 offset = cc.p(0, this._cellsPositions[index]);
262                 break;
263         }
264 
265         return offset;
266     },
267 
268     _offsetFromIndex:function (index) {
269         var offset = this.__offsetFromIndex(index);
270 
271         var cellSize = this._dataSource.tableCellSizeForIndex(this, index);
272         if (this._vOrdering === cc.TABLEVIEW_FILL_TOPDOWN)
273             offset.y = this.getContainer().getContentSize().height - offset.y - cellSize.height;
274 
275         return offset;
276     },
277 
278     _updateCellPositions:function(){
279         var cellsCount = this._dataSource.numberOfCellsInTableView(this);
280         var locCellsPositions = this._cellsPositions;
281 
282         if (cellsCount > 0){
283             var currentPos = 0;
284             var cellSize, locDataSource = this._dataSource;
285             for (var i=0; i < cellsCount; i++) {
286                 locCellsPositions[i] = currentPos;
287                 cellSize = locDataSource.tableCellSizeForIndex(this, i);
288                 switch (this.getDirection()) {
289                     case cc.SCROLLVIEW_DIRECTION_HORIZONTAL:
290                         currentPos += cellSize.width;
291                         break;
292                     default:
293                         currentPos += cellSize.height;
294                         break;
295                 }
296             }
297             this._cellsPositions[cellsCount] = currentPos;//1 extra value allows us to get right/bottom of the last cell
298         }
299     },
300 
301     _updateContentSize:function () {
302         var size = cc.size(0, 0);
303 
304         var cellsCount = this._dataSource.numberOfCellsInTableView(this);
305 
306         if(cellsCount > 0){
307             var maxPosition = this._cellsPositions[cellsCount];
308             switch (this.getDirection()) {
309                 case cc.SCROLLVIEW_DIRECTION_HORIZONTAL:
310                     size = cc.size(maxPosition, this._viewSize.height);
311                     break;
312                 default:
313                     size = cc.size(this._viewSize.width, maxPosition);
314                     break;
315             }
316         }
317 
318         this.setContentSize(size);
319 
320         if (this._oldDirection != this._direction) {
321             if (this._direction == cc.SCROLLVIEW_DIRECTION_HORIZONTAL) {
322                 this.setContentOffset(cc.p(0, 0));
323             } else {
324                 this.setContentOffset(cc.p(0, this.minContainerOffset().y));
325             }
326             this._oldDirection = this._direction;
327         }
328     },
329 
330     _moveCellOutOfSight:function (cell) {
331         if(this._tableViewDelegate && this._tableViewDelegate.tableCellWillRecycle)
332             this._tableViewDelegate.tableCellWillRecycle(this, cell);
333 
334         this._cellsFreed.addObject(cell);
335         this._cellsUsed.removeSortedObject(cell);
336         cc.arrayRemoveObject(this._indices, cell.getIdx());
337 
338         cell.reset();
339         if (cell.getParent() == this.getContainer()) {
340             this.getContainer().removeChild(cell, true);
341         }
342     },
343 
344     _setIndexForCell:function (index, cell) {
345         cell.setAnchorPoint(0, 0);
346         cell.setPosition(this._offsetFromIndex(index));
347         cell.setIdx(index);
348     },
349 
350     _addCellIfNecessary:function (cell) {
351         if (cell.getParent() != this.getContainer()) {
352             this.getContainer().addChild(cell);
353         }
354         this._cellsUsed.insertSortedObject(cell);
355         var locIndices = this._indices, addIdx = cell.getIdx();
356         if(locIndices.indexOf(addIdx) == -1){
357             locIndices.push(addIdx);
358             //sort
359             locIndices.sort(function(a,b){return a-b;});
360         }
361     },
362 
363     /**
364      * data source
365      */
366     getDataSource:function () {
367         return this._dataSource;
368     },
369     setDataSource:function (source) {
370         this._dataSource = source;
371     },
372 
373     /**
374      * delegate
375      */
376     getDelegate:function () {
377         return this._tableViewDelegate;
378     },
379 
380     setDelegate:function (delegate) {
381         this._tableViewDelegate = delegate;
382     },
383 
384     /**
385      * determines how cell is ordered and filled in the view.
386      */
387     setVerticalFillOrder:function (fillOrder) {
388         if (this._vOrdering != fillOrder) {
389             this._vOrdering = fillOrder;
390             if (this._cellsUsed.count() > 0) {
391                 this.reloadData();
392             }
393         }
394     },
395     getVerticalFillOrder:function () {
396         return this._vOrdering;
397     },
398 
399     initWithViewSize:function (size, container) {
400         if (cc.ScrollView.prototype.initWithViewSize.call(this, size, container)) {
401             this._cellsUsed = new cc.ArrayForObjectSorting();
402             this._cellsFreed = new cc.ArrayForObjectSorting();
403             this._indices = [];
404             this._tableViewDelegate = null;
405             this._vOrdering = cc.TABLEVIEW_FILL_BOTTOMUP;
406             this.setDirection(cc.SCROLLVIEW_DIRECTION_VERTICAL);
407 
408             cc.ScrollView.prototype.setDelegate.call(this, this);
409             return true;
410         }
411         return false;
412     },
413 
414     /**
415      * Updates the content of the cell at a given index.
416      *
417      * @param idx index to find a cell
418      */
419     updateCellAtIndex:function (idx) {
420         if (idx == cc.INVALID_INDEX || idx > this._dataSource.numberOfCellsInTableView(this) - 1)
421             return;
422 
423         var cell = this.cellAtIndex(idx);
424         if (cell)
425             this._moveCellOutOfSight(cell);
426 
427         cell = this._dataSource.tableCellAtIndex(this, idx);
428         this._setIndexForCell(idx, cell);
429         this._addCellIfNecessary(cell);
430     },
431 
432     /**
433      * Inserts a new cell at a given index
434      *
435      * @param idx location to insert
436      */
437     insertCellAtIndex:function (idx) {
438         if (idx == cc.INVALID_INDEX || idx > this._dataSource.numberOfCellsInTableView(this) - 1)
439             return;
440 
441         var newIdx, locCellsUsed = this._cellsUsed;
442         var cell = locCellsUsed.objectWithObjectID(idx);
443         if (cell) {
444             newIdx = locCellsUsed.indexOfSortedObject(cell);
445             for (var i = newIdx; i < locCellsUsed.count(); i++) {
446                 cell = locCellsUsed.objectAtIndex(i);
447                 this._setIndexForCell(cell.getIdx() + 1, cell);
448             }
449         }
450 
451         //insert a new cell
452         cell = this._dataSource.tableCellAtIndex(this, idx);
453         this._setIndexForCell(idx, cell);
454         this._addCellIfNecessary(cell);
455 
456         this._updateCellPositions();
457         this._updateContentSize();
458     },
459 
460     /**
461      * Removes a cell at a given index
462      *
463      * @param idx index to find a cell
464      */
465     removeCellAtIndex:function (idx) {
466         if (idx == cc.INVALID_INDEX || idx > this._dataSource.numberOfCellsInTableView(this) - 1)
467             return;
468 
469         var cell = this.cellAtIndex(idx);
470         if (!cell)
471             return;
472 
473         var locCellsUsed = this._cellsUsed;
474         var newIdx = locCellsUsed.indexOfSortedObject(cell);
475 
476         //remove first
477         this._moveCellOutOfSight(cell);
478         cc.arrayRemoveObject(this._indices, idx);
479         this._updateCellPositions();
480 
481         for (var i = locCellsUsed.count() - 1; i > newIdx; i--) {
482             cell = locCellsUsed.objectAtIndex(i);
483             this._setIndexForCell(cell.getIdx() - 1, cell);
484         }
485     },
486 
487     /**
488      * reloads data from data source.  the view will be refreshed.
489      */
490     reloadData:function () {
491         this._oldDirection = cc.SCROLLVIEW_DIRECTION_NONE;
492         var locCellsUsed = this._cellsUsed, locCellsFreed = this._cellsFreed, locContainer = this.getContainer();
493         for (var i = 0, len = locCellsUsed.count(); i < len; i++) {
494             var cell = locCellsUsed.objectAtIndex(i);
495 
496             if(this._tableViewDelegate && this._tableViewDelegate.tableCellWillRecycle)
497                 this._tableViewDelegate.tableCellWillRecycle(this, cell);
498 
499             locCellsFreed.addObject(cell);
500             cell.reset();
501             if (cell.getParent() == locContainer)
502                 locContainer.removeChild(cell, true);
503         }
504 
505         this._indices = [];
506         this._cellsUsed = new cc.ArrayForObjectSorting();
507 
508         this._updateCellPositions();
509         this._updateContentSize();
510         if (this._dataSource.numberOfCellsInTableView(this) > 0)
511             this.scrollViewDidScroll(this);
512     },
513 
514     /**
515      * Dequeues a free cell if available. nil if not.
516      *
517      * @return {TableViewCell} free cell
518      */
519     dequeueCell:function () {
520         if (this._cellsFreed.count() === 0) {
521             return null;
522         } else {
523             var cell = this._cellsFreed.objectAtIndex(0);
524             this._cellsFreed.removeObjectAtIndex(0);
525             return cell;
526         }
527     },
528 
529     /**
530      * Returns an existing cell at a given index. Returns nil if a cell is nonexistent at the moment of query.
531      *
532      * @param idx index
533      * @return {cc.TableViewCell} a cell at a given index
534      */
535     cellAtIndex:function (idx) {
536         var i = this._indices.indexOf(idx);
537         if (i == -1)
538             return null;
539         return this._cellsUsed.objectWithObjectID(idx);
540     },
541 
542     scrollViewDidScroll:function (view) {
543         var locDataSource = this._dataSource;
544         var countOfItems = locDataSource.numberOfCellsInTableView(this);
545         if (0 === countOfItems)
546             return;
547 
548         if (this._tableViewDelegate != null && this._tableViewDelegate.scrollViewDidScroll)
549             this._tableViewDelegate.scrollViewDidScroll(this);
550 
551         var  idx = 0, locViewSize = this._viewSize, locContainer = this.getContainer();
552         var offset = this.getContentOffset();
553         offset.x *= -1;
554         offset.y *= -1;
555 
556         var maxIdx = Math.max(countOfItems-1, 0);
557 
558         if (this._vOrdering === cc.TABLEVIEW_FILL_TOPDOWN)
559             offset.y = offset.y + locViewSize.height/locContainer.getScaleY();
560         var startIdx = this._indexFromOffset(offset);
561         if (startIdx === cc.INVALID_INDEX)
562             startIdx = countOfItems - 1;
563 
564         if (this._vOrdering === cc.TABLEVIEW_FILL_TOPDOWN)
565             offset.y -= locViewSize.height/locContainer.getScaleY();
566         else
567             offset.y += locViewSize.height/locContainer.getScaleY();
568         offset.x += locViewSize.width/locContainer.getScaleX();
569 
570         var endIdx = this._indexFromOffset(offset);
571         if (endIdx === cc.INVALID_INDEX)
572             endIdx = countOfItems - 1;
573 
574         var cell, locCellsUsed = this._cellsUsed;
575         if (locCellsUsed.count() > 0) {
576             cell = locCellsUsed.objectAtIndex(0);
577             idx = cell.getIdx();
578             while (idx < startIdx) {
579                 this._moveCellOutOfSight(cell);
580                 if (locCellsUsed.count() > 0) {
581                     cell = locCellsUsed.objectAtIndex(0);
582                     idx = cell.getIdx();
583                 } else
584                     break;
585             }
586         }
587 
588         if (locCellsUsed.count() > 0) {
589             cell = locCellsUsed.lastObject();
590             idx = cell.getIdx();
591             while (idx <= maxIdx && idx > endIdx) {
592                 this._moveCellOutOfSight(cell);
593                 if (locCellsUsed.count() > 0) {
594                     cell = locCellsUsed.lastObject();
595                     idx = cell.getIdx();
596                 } else
597                     break;
598             }
599         }
600 
601         var locIndices = this._indices;
602         for (var i = startIdx; i <= endIdx; i++) {
603             if (locIndices.indexOf(i) != -1)
604                 continue;
605             this.updateCellAtIndex(i);
606         }
607     },
608 
609     scrollViewDidZoom:function (view) {
610     },
611 
612     onTouchEnded:function (touch, event) {
613         if (!this.isVisible())
614             return;
615 
616         if (this._touchedCell){
617             var bb = this.getBoundingBox();
618             var tmpOrigin = cc.p(bb.x, bb.y);
619             tmpOrigin = this._parent.convertToWorldSpace(tmpOrigin);
620             bb.x = tmpOrigin.x;
621             bb.y = tmpOrigin.y;
622             var locTableViewDelegate = this._tableViewDelegate;
623             if (cc.rectContainsPoint(bb, touch.getLocation()) && locTableViewDelegate != null){
624                 if(locTableViewDelegate.tableCellUnhighlight)
625                     locTableViewDelegate.tableCellUnhighlight(this, this._touchedCell);
626                 if(locTableViewDelegate.tableCellTouched)
627                     locTableViewDelegate.tableCellTouched(this, this._touchedCell);
628             }
629             this._touchedCell = null;
630         }
631         cc.ScrollView.prototype.onTouchEnded.call(this, touch, event);
632     },
633 
634     onTouchBegan:function(touch, event){
635         if (!this.isVisible())
636             return false;
637 
638         var touchResult = cc.ScrollView.prototype.onTouchBegan.call(this, touch, event);
639 
640         if(this._touches.length === 1) {
641             var index, point;
642 
643             point = this.getContainer().convertTouchToNodeSpace(touch);
644 
645             index = this._indexFromOffset(point);
646             if (index === cc.INVALID_INDEX)
647                 this._touchedCell = null;
648             else
649                 this._touchedCell  = this.cellAtIndex(index);
650 
651             if (this._touchedCell && this._tableViewDelegate != null && this._tableViewDelegate.tableCellHighlight)
652                 this._tableViewDelegate.tableCellHighlight(this, this._touchedCell);
653         } else if(this._touchedCell) {
654             if(this._tableViewDelegate != null && this._tableViewDelegate.tableCellUnhighlight)
655                 this._tableViewDelegate.tableCellUnhighlight(this, this._touchedCell);
656             this._touchedCell = null;
657         }
658 
659         return touchResult;
660     },
661 
662     onTouchMoved: function(touch, event){
663         cc.ScrollView.prototype.onTouchMoved.call(this, touch, event);
664 
665         if (this._touchedCell && this.isTouchMoved()) {
666             if(this._tableViewDelegate != null && this._tableViewDelegate.tableCellUnhighlight)
667                 this._tableViewDelegate.tableCellUnhighlight(this, this._touchedCell);
668             this._touchedCell = null;
669         }
670     },
671 
672     onTouchCancelled: function(touch, event){
673         cc.ScrollView.prototype.onTouchCancelled.call(this, touch, event);
674 
675         if (this._touchedCell) {
676             if(this._tableViewDelegate != null && this._tableViewDelegate.tableCellUnhighlight)
677                 this._tableViewDelegate.tableCellUnhighlight(this, this._touchedCell);
678             this._touchedCell = null;
679         }
680     }
681 });
682 
683 var _p = cc.TableView.prototype;
684 
685 /** @expose */
686 _p.dataSource;
687 cc.defineGetterSetter(_p, "dataSource", _p.getDataSource, _p.setDataSource);
688 /** @expose */
689 _p.delegate;
690 cc.defineGetterSetter(_p, "delegate", _p.getDelegate, _p.setDelegate);
691 /** @expose */
692 _p.verticalFillOrder;
693 cc.defineGetterSetter(_p, "verticalFillOrder", _p.getVerticalFillOrder, _p.setVerticalFillOrder);
694 
695 _p = null;
696 
697 /**
698  * An initialized table view object
699  *
700  * @param {cc.TableViewDataSource} dataSource data source;
701  * @param {cc.Size} size view size
702  * @param {cc.Node} [container] parent object for cells
703  * @return {cc.TableView} table view
704  */
705 cc.TableView.create = function (dataSource, size, container) {
706     var table = new cc.TableView();
707     table.initWithViewSize(size, container);
708     table.setDataSource(dataSource);
709     table._updateCellPositions();
710     table._updateContentSize();
711     return table;
712 };
713