1 var X_UI_Repeater_SUPPORT_ATTRS = {
\r
2 dataSource : [ null, XUI_Dirty.LAYOUT, XUI_Attr_USER.UINODE, XUI_Attr_Type.OBJECT ],
\r
3 itemRenderer : [ null, XUI_Dirty.LAYOUT, XUI_Attr_USER.UINODE, XUI_Attr_Type.OBJECT ]
\r
6 var XUI_Repeater = XUI_Box.inherits(
\r
10 layout : XUI_Layout_Vertical,
\r
12 dataSource : null, // Array.<object>, Array.<ItemData>
\r
14 itemRenderer : null,
\r
18 startRenderIndex : 0,
\r
19 numItemsParPage : 0,
\r
22 Constructor : function( user, dataSource, itemRenderer, attr ){
\r
23 this.Super( user, null, [ attr ] );
\r
24 this.dataSource = dataSource;
\r
25 this.itemRenderer = itemRenderer;
\r
31 calculate : function( isNeedsDetection, x, y, allowedW, allowedH ){
\r
32 var dataSource = this[ 'dataSource' ];
\r
34 if( allowedW + allowedH === XUI_Attr_AUTO ) return false;
\r
36 this.scrollPortWidth = allowedW;
\r
37 this.scrollPortHeight = allowedH;
\r
39 this.preMesure( allowedW, allowedH );
\r
41 if( dataSource && dataSource.length ){
\r
42 this.updateItemRenderer( this.contentWidth, allowedH );
\r
44 if( this.contentHeight === XUI_Attr_AUTO ){
\r
45 this.contentHeight = this.contentHeightMin !== XUI_Attr_AUTO ? this.contentHeightMin : 0;
\r
50 if( !isNeedsDetection ){
\r
57 handleEvent : function( e ){
\r
58 var scrollBox, scrollY, dataSource, offsetY, startIndex, maxIndex, offset, uinodes;
\r
61 case XUI_Event.SCROLL_END :
\r
62 scrollBox = this.parentData;
\r
63 scrollY = scrollBox.scrollY;
\r
64 dataSource = this[ 'dataSource' ];
\r
65 uinodes = this.uinodes;
\r
66 // transition Y を 0 付近に。
\r
67 offsetY = scrollY % this.itemHeightLast;
\r
68 scrollBox.scrollTo( 0, offsetY, 0, '', 0 ); // anime無し
\r
70 startIndex = scrollY / this.itemHeightLast | 0;
\r
71 maxIndex = dataSource.length <= this.numItems ? 0 : dataSource.length - this.numItems;
\r
73 startIndex < 0 ? 0 :
\r
74 maxIndex < startIndex ? maxIndex : startIndex;
\r
75 // アイテムの座標の修正とレンジ外のアイテムを配列内で再配置
\r
76 offset = startIndex - this.startIndex; // visible な stratIndex renderStartIndex
\r
79 this.addAt( last, uinodes.splice( 0, offset ) );
\r
82 this.addAt( 0, uinodes.splice( uinodes.length - offset ) );
\r
84 // 再配置されたアイテムにitemData のセット
\r
85 this.updateItemRenderer( this.contentWidth, this.scrollPortHeight );
\r
90 updateItemRenderer : function( _w, _h ){
\r
91 var uinodes = this.uinodes || ( this.uinodes = [] ),
\r
92 attrs = this.attrObject || this.attrClass.prototype,
\r
93 gapY = XUI_AbstractUINode_calcValue( attrs[ this.usableAttrs.gapY.No ], _w ),
\r
94 dataSource = this[ 'dataSource' ],
\r
95 render = this[ 'itemRenderer' ],
\r
96 l = dataSource.length,
\r
97 start = this.startIndex - ( this.numItems - this.numItemsParPage ) / 2 | 0,
\r
98 itemH = this.itemHeightLast,
\r
99 i, node, data, _y = 0, last, n;
\r
101 i = start = start < 0 ? 0 : start;
\r
103 for( ; i < l; ++i ){
\r
104 if( !( node = uinodes[ i ] ) ){
\r
105 node = render.clone( true );
\r
106 this.addAt( i, [ node ] );
\r
107 data = X_Pair_get( node );
\r
108 // init -> addToParent -> creationComplete
\r
110 data.setItemData( dataSource[ i ] );
\r
112 data.calculate( false, 0, _y, _w, _h );
\r
113 _y += ( itemH || data.boxHeight ) + gapY;
\r
115 // 一番最初のループ。ここでページあたりのアイテム数を計算
\r
116 if( !itemH && i === start ){
\r
118 this.itemHeightLast = itemH * X_ViewPort_baseFontSize,
\r
119 // scroller の miniHeight は(例えば)親の高さの300% そこにいくつのアイテムを並べることが出来るか?端数切り上げ
\r
120 this.numItemsParPage = _h / itemH + 0.999 | 0;
\r
121 n = this.numItems = ( _h * 3 ) / itemH + 0.999 | 0; // TODO boxHeight
\r
123 // データの最後まで、または、開始位置から 3ページ分を生成する
\r
124 l = last < l ? last : l;
\r
128 for( l = uinodes.length; i < l; ++i ){
\r
129 // uinodes[ i ] hide
\r
132 // TODO contentHeight は attr を無視する -> 未表示領域につくるアイテム数 GPU の有無で変わる
\r
133 this.contentHeight = _y - gapY;
\r
136 onPropertyChange : function( name, newValue ){
\r
137 var uinodes, i, l, uinode, dataList, from;
\r
140 case 'itemRenderer' :
\r
141 for( uinodes = this.uinodes, i = uinodes && uinodes.length; i; ){
\r
142 uinodes[ --i ][ 'kill' ]();
\r
145 case 'dataSource' :
\r
146 if( uinodes = this.uinodes ){
\r
147 i = uinodes.length;
\r
148 l = this[ 'dataSource' ].length;
\r
150 uinodes[ --i ][ 'kill' ]();
\r
151 uinodes.length = i;
\r
162 X.UI.Repeater = X.UI.Box.inherits(
\r
166 Constructor : function( dataSource, itemRenderer ){
\r
169 if( XUI_Repeater.prototype.usableAttrs === XUI_Box.prototype.usableAttrs ){
\r
170 XUI_Repeater.prototype.usableAttrs = supports = XUI_Attr_createAttrDef( XUI_Attr_Support, XUI_Layout_Vertical.overrideAttrsForSelf );
\r
172 XUI_Repeater.prototype.attrClass = XUI_Attr_preset( XUI_Box.prototype.attrClass, supports );
\r
176 // itemBase parent に追加されている uinode は不可
\r
177 // minHeight=300% height=auto
\r
178 X_Pair_create( this,
\r
181 dataSource, itemRenderer,
\r
183 name : 'ScrollBox-Scroller',
\r
184 role : 'container',
\r
192 getItemDataAt : function(){
\r
196 add : function( /* node, node, node ... */ ){
\r
198 addAt : function( index /* , node , node, node ... */ ){
\r
200 remove : function( /* node, node, node ... */ ){
\r
202 removeAt : function( from, length ){
\r
204 getNodesByClass : function( klass ){
\r
206 getFirstChild : function(){
\r
208 getLastChild : function(){
\r
210 getNodeAt : function( index ){
\r
212 numNodes : function(){
\r
213 var uinodes = X_Pair_get( this ).uinodes;
\r
214 return uinodes && uinodes.length || 0;
\r