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
19 startRenderIndex : 0,
\r
20 numItemsParPage : 0,
\r
24 itemHeightLastEM : 0,
\r
26 Constructor : function( user, dataSource, itemRenderer, attr ){
\r
27 this.Super( user, null, [ attr ] );
\r
28 this.dataSource = dataSource;
\r
29 this.itemRenderer = itemRenderer;
\r
30 this.itemNodes = [];
\r
31 this.__item__ = X_Pair_get( itemRenderer );
\r
34 initialize : function(){
\r
35 XUI_AbstractUINode.prototype.initialize.apply( this, arguments );
\r
37 this.parent[ 'listen' ]( XUI_Event.SCROLL_END, this );
\r
43 calculate : function( isNeedsDetection, x, y, allowedW, allowedH ){
\r
44 var dataSource = this[ 'dataSource' ];
\r
46 if( allowedW + allowedH === XUI_Attr_AUTO ) return false;
\r
48 this.scrollPortWidth = allowedW;
\r
49 this.scrollPortHeight = allowedH;
\r
51 this.preMesure( allowedW, allowedH );
\r
53 if( dataSource && dataSource.length ){
\r
54 this.updateItemRenderer( this.contentWidth, allowedH );
\r
56 if( this.contentHeight === XUI_Attr_AUTO ){
\r
57 this.contentHeight = this.contentHeightMin !== XUI_Attr_AUTO ? this.contentHeightMin : 0;
\r
62 if( !isNeedsDetection ){
\r
69 handleEvent : function( e ){
\r
70 var scrollBox, scrollY, dataSource, offsetY, startIndex, maxIndex, offset, itemNodes, ary, i, l;
\r
73 case XUI_Event.SCROLL_END :
\r
74 scrollBox = this.parentData;
\r
75 scrollY = - scrollBox.scrollY;
\r
76 dataSource = this[ 'dataSource' ];
\r
77 itemNodes = this.itemNodes;
\r
78 itemH = this.itemHeightLast;
\r
80 // transition Y を 0 付近に。
\r
84 startIndex = scrollY / itemH | 0;
\r
86 /*maxIndex = dataSource.length <= this.numItems ? 0 : dataSource.length - this.numItems;
\r
87 console.log( ' >>> ' + startIndex + ' ' + maxIndex );
\r
90 startIndex < 0 ? 0 :
\r
91 maxIndex < startIndex ? maxIndex : startIndex; */
\r
92 // アイテムの座標の修正とレンジ外のアイテムを配列内で再配置
\r
93 offset = startIndex - this.startIndex; // visible な stratIndex renderStartIndex
\r
94 this.startIndex = startIndex;
\r
96 console.log( this.numItemsPrev + ' oo ' + offset )
\r
99 itemNodes.push.apply( itemNodes, itemNodes.splice( 0, offset ) );
\r
101 if( offset < - 0 ){
\r
102 itemNodes.unshift.apply( itemNodes, itemNodes.splice( itemNodes.length + offset ) );
\r
105 // 再配置されたアイテムにitemData のセット
\r
106 this.updateItemRenderer( this.contentWidth, this.scrollPortHeight );
\r
110 offsetY = scrollY % itemH;
\r
111 offsetY = offsetY === 0 ? 0 : ( offsetY - itemH );
\r
112 offsetY += ( this.startRenderIndex - this.startIndex ) * itemH;
\r
113 //console.log( ' ====> ' + this.startRenderIndex + ' -> ' + this.startIndex + ' scrollY:' + offsetY );
\r
115 //scrollBox.scrollTo( 0, - scrollY, 0, '', 0 ); // anime無し
\r
116 //console.log( ' <==== ' );
\r
121 updateItemRenderer : function( _w, _h ){
\r
122 var itemNodes = this.itemNodes,
\r
123 attrs = this.attrObject || this.attrClass.prototype,
\r
124 gapY = XUI_AbstractUINode_calcValue( attrs[ this.usableAttrs.gapY.No ], _w ),
\r
125 dataSource = this[ 'dataSource' ],
\r
126 render = this[ 'itemRenderer' ],
\r
127 l = dataSource.length,
\r
128 start = this.startIndex - this.numItemsPrev,
\r
129 itemH = this.itemHeightLastEM,
\r
130 i, data, node, _y = 0, last, n;
\r
132 i = this.startRenderIndex = start = start < 0 ? 0 : start;
\r
134 _y = ( itemH + gapY ) * i;
\r
136 for( ; i < l; ++i ){
\r
137 if( !( data = itemNodes[ i ] ) ){
\r
138 node = render.clone( true );
\r
139 this.addAt( i, [ node ] );
\r
140 data = itemNodes[ i ] = X_Pair_get( node );
\r
141 // init -> addToParent -> creationComplete
\r
143 data.setItemData( dataSource[ i ] );
\r
145 data.calculate( false, 0, _y, _w, _h );
\r
146 _y += ( itemH || data.boxHeight ) + gapY;
\r
148 // 一番最初のループ。ここでページあたりのアイテム数を計算
\r
149 if( !itemH && i === start ){
\r
151 this.itemHeightLastEM = itemH;
\r
152 this.itemHeightLast = itemH * X_ViewPort_baseFontSize,
\r
153 // scroller の miniHeight は(例えば)親の高さの300% そこにいくつのアイテムを並べることが出来るか?端数切り上げ
\r
154 this.numItemsParPage = _h / itemH + 0.999 | 0;
\r
155 n = this.numItems = ( _h * 3 ) / itemH + 0.999 | 0; // TODO boxHeight
\r
156 this.numItemsPrev = ( this.numItems - this.numItemsParPage ) / 2 | 0;
\r
158 // データの最後まで、または、開始位置から 3ページ分を生成する
\r
159 l = last < l ? last : l;
\r
163 for( l = itemNodes.length; i < l; ++i ){
\r
164 // itemNodes[ i ] hide
\r
167 // TODO contentHeight は attr を無視する -> 未表示領域につくるアイテム数 GPU の有無で変わる
\r
168 this.contentHeight = dataSource.length * ( itemH + gapY ) - gapY;
\r
171 onPropertyChange : function( name, newValue ){
\r
172 var itemNodes, i, l, uinode, dataList, from;
\r
175 case 'itemRenderer' :
\r
176 for( itemNodes = this.itemNodes, i = itemNodes && itemNodes.length; i; ){
\r
177 itemNodes[ --i ][ 'kill' ]();
\r
180 case 'dataSource' :
\r
181 if( itemNodes = this.itemNodes ){
\r
182 i = itemNodes.length;
\r
183 l = this[ 'dataSource' ].length;
\r
185 itemNodes[ --i ][ 'kill' ]();
\r
186 itemNodes.length = i;
\r
197 X.UI.Repeater = X.UI.Box.inherits(
\r
201 Constructor : function( dataSource, itemRenderer ){
\r
204 if( XUI_Repeater.prototype.usableAttrs === XUI_Box.prototype.usableAttrs ){
\r
205 supports = XUI_Attr_createAttrDef( XUI_Attr_Support, X_UI_Repeater_SUPPORT_ATTRS );
\r
206 XUI_Repeater.prototype.usableAttrs = supports = XUI_Attr_createAttrDef( supports, XUI_Layout_Vertical.overrideAttrsForSelf );
\r
208 XUI_Repeater.prototype.attrClass = XUI_Attr_preset( XUI_Box.prototype.attrClass, supports );
\r
212 // itemBase parent に追加されている uinode は不可
\r
213 // minHeight=300% height=auto
\r
214 X_Pair_create( this,
\r
217 dataSource, itemRenderer,
\r
219 name : 'ScrollBox-Scroller',
\r
220 role : 'container',
\r
224 minHeight : '100%',
\r
225 borderColor : 0x252527,
\r
226 borderWidth : [ 0.15, 0, 0 ],
\r
227 borderStyle : 'solid',
\r
229 bgColor : 0x444643,
\r
234 getItemDataAt : function(){
\r
238 add : function( /* node, node, node ... */ ){
\r
240 addAt : function( index /* , node , node, node ... */ ){
\r
242 remove : function( /* node, node, node ... */ ){
\r
244 removeAt : function( from, length ){
\r
246 getNodesByClass : function( klass ){
\r
248 getFirstChild : function(){
\r
250 getLastChild : function(){
\r
252 getNodeAt : function( index ){
\r
254 numNodes : function(){
\r
255 var uinodes = X_Pair_get( this ).uinodes;
\r
256 return uinodes && uinodes.length || 0;
\r