virtualgrid: adjusts VI effect for an overflow action
[platform/framework/web/web-ui-fw.git] / src / js / widgets / jquery.mobile.tizen.virtualgrid.js
1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2 //>>description: Shows grid swapping its contents automatically
3 //>>label: Virtual grid
4 //>>group: Tizen:Widgets
5
6 define( [ '../jquery.mobile.tizen.core', '../jquery.mobile.tizen.scrollview' ], function ( ) {
7 //>>excludeEnd("jqmBuildExclude");
8
9 /* ***************************************************************************
10  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in
20  * all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  * ***************************************************************************
30  *
31  *      Author: Kangsik Kim <kangsik81.kim@samsung.com>
32  *                      Youmin Ha <youmin.ha@samsung.com>
33 */
34
35 /**
36  * In the web environment, it is challenging to display a large amount of data in a grid.
37  * When an application needs to show, for example, image gallery with over 1,000 images,
38  * the same enormous data must be inserted into a HTML document.
39  * It takes a long time to display the data and manipulating DOM is complex.
40  * The virtual grid widget supports storing unlimited data without performance issues
41  * by reusing a limited number of grid elements.
42  * The virtual grid widget is based on the jQuery.template plug-in 
43  * For more information, see jQuery.template.
44  *
45  * HTML Attributes:
46  *
47  *              data-role:  virtualgrid
48  *              data-template : Has the ID of the jQuery.template element.
49  *                                              jQuery.template for a virtual grid must be defined.
50  *                                              Style for template would use rem unit to support scalability.
51  *              data-direction : This option define the direction of the scroll.
52  *                                              You must choose one of the 'x' and 'y' (Default : y)
53  *              data-rotation : This option defines whether or not the circulation of the data.
54  *                                              If option is 'true' and scroll is reached the last data,
55  *                                              Widget will present the first data on the screen.
56  *                                              If option is ‘false’, Widget will operate like a scrollview.
57  *
58  *              ID : <DIV> element that has "data-role=virtualgrid" must have ID attribute.
59  *
60  * APIs:
61  *
62  *              create ( {
63  *                              itemData: function ( idx ) { return json_obj; },
64  *                              numItemData: number or function () { return number; },
65  *                              cacheItemData: function ( minIdx, maxIdx ) {}
66  *                              } )
67  *                      : Create VirtualGrid widget. At this moment, _create method is called.
68  *                      args : A collection of options
69  *                              itemData: A function that returns JSON object for given index. Mandatory.
70  *                              numItemData: Total number of itemData. Mandatory.
71  *                              cacheItemData: Virtuallist will ask itemData between minIdx and maxIdx.
72  *                              Developers can implement this function for preparing data.
73  *                              Optional.
74  *
75  *              centerTo ( String )
76  *                      : Find a DOM Element with the given class name.
77  *                      This element will be centered on the screen.
78  *                      Serveral elements were found, the first element is displayed.
79  *
80  * Events:
81  *              scrollstart : : This event triggers when a user begin to move the scroll on VirtualGrid.
82  *              scrollupdate : : This event triggers while a user moves the scroll on VirtualGrid.
83  *              scrollstop : This event triggers when a user stop the scroll on VirtualGrid.
84  *              select : This event triggers when a cell is selected.
85  *
86  * Examples:
87  *
88  *                      <script id="tizen-demo-namecard" type="text/x-jquery-tmpl">
89  *                              <div class="ui-demo-namecard">
90  *                                      <div class="ui-demo-namecard-pic">
91  *                                              <img class="ui-demo-namecard-pic-img" src="${TEAM_LOGO}" />
92  *                                      </div>
93  *                                      <div class="ui-demo-namecard-contents">
94  *                                              <span class="name ui-li-text-main">${NAME}</span>
95  *                                              <span class="active ui-li-text-sub">${ACTIVE}</span>
96  *                                              <span class="from ui-li-text-sub">${FROM}</span>
97  *                                      </div>
98  *                              </div>
99  *                      </script>
100  *                      <div id="virtualgrid-demo" data-role="virtualgrid" data-template="tizen-demo-namecard" >
101  *                      </div>
102  *
103  */
104
105 // most of following codes are derived from jquery.mobile.scrollview.js
106
107 /**
108         @class VirtualGrid
109         In the Web environment, it is challenging to display large amount of data in a list, such as displaying a contact list of over 1000 list items. It takes time to display the entire list in HTML and the DOM manipulation is complex.
110
111         The virtual grid widget is used to display a list of unlimited data elements on the screen for better performance. This widget displays the data in the grid format by reusing the existing grid control space. Virtual grids are based on the jQuery.template plugin as described in the jQuery documentation for jQuery.template plugin.
112
113         To add a virtual grid widget to the application, use the following code:
114
115                 <script id="tizen-demo-namecard" type="text/x-jquery-tmpl">
116                         <div class="ui-demo-namecard">
117                                 <div class="ui-demo-namecard-pic">
118                                         <img class="ui-demo-namecard-pic-img" src="${TEAM_LOGO}" />
119                                 </div>
120                                 <div class="ui-demo-namecard-contents">
121                                 <span class="name ui-li-text-main">${NAME}</span>
122                                 </div>
123                         </div>
124                 </script>
125                 <div id="virtualgrid-demo" data-role="virtualgrid" data-template="tizen-demo-namecard">
126                 </div>
127 */
128 /**
129         @property {String} data-template
130         Specifies the jQuery.template element ID.
131         The jQuery.template must be defined. The template style can use rem units to support scalability.
132 */
133 /**
134         @property {String} data-direction
135         Defines the scroll direction. The direction options are x (horizontal) and y (vertical).
136         The default value is y.
137 */
138 /**
139         @property {Boolean} data-rotation
140         Defines whether the data elements are displayed from the beginning of the list again once the end of file is reached.
141         The default value is false.
142 */
143 /**
144         @event scrollstart
145         The scrollstart event is fired when the user starts scrolling through the grid:
146
147                 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
148                 // Option 01
149                 $(".selector").virtualgrid
150                 ({
151                         scrollstart: function(event, ui)
152                         {
153                         // Handle the scrollstart event
154                         }
155                 });
156                 // Option 02
157                 $(".selector").bind("scrollstart", function(event, ui)
158                 {
159                 // Handle the scrollstart event
160                 });
161 */
162 /**
163         @event scrollupdate
164         The scrollupdate event is fired when the user moves the scroll bar in the grid:
165
166                 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
167                 // Option 01
168                 $(".selector").virtualgrid
169                 ({
170                         scrollupdate: function(event, ui)
171                         {
172                         // Handle the scrollupdate event
173                         }
174                 });
175                 // Option 02
176                 $(".selector").bind("scrollupdate", function(event, ui)
177                 {
178                 // Handle the scrollupdate event
179                 });
180 */
181 /**
182         @event scrollstop
183         The scrollstop event is fired when the user stops scrolling:
184
185                 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
186                 // Option 01
187                 $(".selector").virtualgrid
188                 ({
189                         scrollstop: function(event, ui)
190                         {
191                         // Handle the scrollstop event
192                         }
193                 });
194                 // Option 02
195                 $(".selector").bind("scrollstop", function(event, ui)
196                 {
197                 // Handle the scrollstop event
198                 });
199 */
200 /**
201         @event select
202         The select event is fired when a virtual grid cell is selected:
203
204                 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
205                 // Option 01
206                 $(".selector").virtualgrid
207                 ({
208                         select: function(event, ui)
209                         {
210                         // Handle the select event
211                         }
212                 });
213                 // Option 02
214                 $(".selector").bind("select", function(event, ui)
215                 {
216                 // Handle the select event
217                 });
218 */
219 /**
220         @method create
221         @param {function} itemData(index)
222         @param {Number} numItemData
223         @param {function} cacheItemData(minIndex, maxIndex)
224         The create method is used to call the jQuery _create method. In the method parameters:
225
226         function itemData(index) returns the JSON object matched with the given index. The index value is between 0 and numItemData-1.<br/>
227         number numItemData or function numItemData() defines or returns a static number of items.<br/>
228         function cacheItemData(minIndex, maxIndex) prepares the JSON data. This method is called before calling the itemData() method with index values between minIndex and maxIndex.<br/>
229
230                 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
231                         function itemData(idx)
232                         {
233                                 return DATA[idx];
234                         }
235                         function cacheItemData(minIdx, maxIdx)
236                         {
237                         // Prepare JSON data between minIdx and maxIdx
238                         }
239                         var numItemData = DATA.length;
240                         $(".selector").virtualgrid("create",
241                         {
242                                 itemData, numItemData, cacheItemData
243                         });
244 */
245 /**
246         @method centerTo
247         The centerTo method is used to search for the DOM element with a specified class name. The retrieved element is placed at the center of the virtual grid. If multiple elements are retrieved, the first element from the result list is placed at the center of the virtual grid.
248
249                 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
250                 $(".selector").virtualgrid("centerTo", "selector");
251 */
252 /**
253         @method resize
254         The resize method is used to rearrange the DOM elements to fit a new screen size when the screen is resized:
255
256                 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
257                 ".selector").virtualgrid("resize");
258
259         @since Tizen2.0
260 */
261
262 ( function ( $, window, document, undefined ) {
263
264         function circularNum ( num, total ) {
265                 var n = num % total;
266                 if ( n < 0 ) {
267                         n = total + n;
268                 }
269                 return n;
270         }
271
272         function MomentumTracker ( options ) {
273                 this.options = $.extend( {}, options );
274                 this.easing = "easeOutQuad";
275                 this.reset();
276         }
277
278         var tstates = {
279                         scrolling : 0,
280                         done : 1
281                 },
282                 _OVERFLOW_DIR_NONE = 0,         /* ENUM */
283                 _OVERFLOW_DIR_UP = 1,           /* ENUM */
284                 _OVERFLOW_DIR_DOWN = -1;        /* ENUM */
285
286         function getCurrentTime () {
287                 return Date.now();
288         }
289
290         $.extend( MomentumTracker.prototype, {
291                 start : function ( pos, speed, duration ) {
292                         this.state = ( speed !== 0 ) ? tstates.scrolling : tstates.done;
293                         this.pos = pos;
294                         this.speed = speed;
295                         this.duration = duration;
296
297                         this.fromPos = 0;
298                         this.toPos = 0;
299
300                         this.startTime = getCurrentTime();
301                 },
302
303                 reset : function () {
304                         this.state = tstates.done;
305                         this.pos = 0;
306                         this.speed = 0;
307                         this.duration = 0;
308                 },
309
310                 update : function () {
311                         var state = this.state, duration, elapsed, dx, x;
312
313                         if ( state == tstates.done ) {
314                                 return this.pos;
315                         }
316                         duration = this.duration;
317                         elapsed = getCurrentTime () - this.startTime;
318                         elapsed = elapsed > duration ? duration : elapsed;
319                         dx = this.speed * ( 1 - $.easing[this.easing]( elapsed / duration, elapsed, 0, 1, duration ) );
320                         x = this.pos + dx;
321                         this.pos = x;
322
323                         if ( elapsed >= duration ) {
324                                 this.state = tstates.done;
325                         }
326                         return this.pos;
327                 },
328
329                 done : function () {
330                         return this.state == tstates.done;
331                 },
332
333                 getPosition : function () {
334                         return this.pos;
335                 }
336         });
337
338         jQuery.widget ( "mobile.virtualgrid", jQuery.mobile.widget, {
339                 options : {
340                         // virtualgrid option
341                         template : "",
342                         direction : "y",
343                         rotation : false
344                 },
345
346                 create : function () {
347                         this._create.apply( this, arguments );
348                 },
349
350                 _create : function ( args ) {
351                         $.extend( this, {
352                                 // view
353                                 _$view : null,
354                                 _$clip : null,
355                                 _$rows : null,
356                                 _tracker : null,
357                                 _viewSize : 0,
358                                 _clipSize : 0,
359                                 _cellSize : undefined,
360                                 _currentItemCount : 0,
361                                 _itemCount : 1,
362                                 _inheritedSize : null,
363
364                                 // timer
365                                 _timerInterval : 0,
366                                 _timerID : 0,
367                                 _timerCB : null,
368                                 _lastMove : null,
369
370                                 // Data
371                                 _itemData : function ( idx ) { return null; },
372                                 _numItemData : 0,
373                                 _cacheItemData : function ( minIdx, maxIdx ) { },
374                                 _totalRowCnt : 0,
375                                 _template : null,
376                                 _maxViewSize : 0,
377                                 _modifyViewPos : 0,
378                                 _maxSizeExceptClip : 0,
379                                 _maxSize : 0,
380
381                                 // axis - ( true : x , false : y )
382                                 _direction : false,
383                                 _didDrag : true,
384                                 _reservedPos : 0,
385                                 _scalableSize : 0,
386                                 _eventPos : 0,
387                                 _nextPos : 0,
388                                 _movePos : 0,
389                                 _lastY : 0,
390                                 _speedY : 0,
391                                 _lastX : 0,
392                                 _speedX : 0,
393                                 _rowsPerView : 0,
394                                 _fragment : null,
395
396                                 _filterRatio : 0.9,
397
398                                 _overflowStartPos : 0,
399                                 _overflowDir : 0,
400                                 _overflowMaxDragDist : 100
401                         });
402
403                         var self = this,
404                                 $dom = $( self.element ),
405                                 opts = self.options,
406                                 $item = null;
407
408                         // itemData
409                         // If mandatory options are not given, Do nothing.
410                         if ( !args ) {
411                                 return ;
412                         }
413
414                         if ( !self._loadData( args ) ) {
415                                 return;
416                         }
417
418                         // make a fragment.
419                         self._fragment = document.createDocumentFragment();
420
421                         // read defined properties(width and height) from dom element.
422                         self._inheritedSize = self._getinheritedSize( self.element );
423
424                         // set a scroll direction.
425                         self._direction = opts.direction === 'x' ? true : false;
426
427                         // make view layer
428                         self._$clip = $( self.element ).addClass( "ui-scrollview-clip" ).addClass( "ui-virtualgrid-view" );
429                         $item = $( document.createElement( "div" ) ).addClass( "ui-scrollview-view" );
430                         self._clipSize =  self._calculateClipSize();
431                         self._$clip.append( $item );
432                         self._$view = $item;
433                         self._$clip.css( "overflow", "hidden" );
434                         self._$view.css( "overflow", "hidden" );
435
436                         // inherit from scrollview widget.
437                         self._scrollView = $.tizen.scrollview.prototype;
438                         self._initScrollView();
439
440                         // create tracker.
441                         self._createTracker();
442                         self._makePositioned( self._$clip );
443                         self._timerInterval = 1000 / self.options.fps;
444
445                         self._timerID = 0;
446                         self._timerCB = function () {
447                                 self._handleMomentumScroll();
448                         };
449                         $dom.closest( ".ui-content" ).addClass( "ui-virtualgrid-content" ).css( "overflow", "hidden" );
450
451                         // add event handler.
452                         self._addBehaviors();
453
454                         self._currentItemCount = 0;
455                         self._createOverflowArea();
456                         self._createScrollBar();
457                         self.refresh();
458                 },
459
460                 // The argument is checked for compliance with the specified format.
461                 // @param args   : Object
462                 // @return boolean
463                 _loadData : function ( args ) {
464                         var self = this;
465
466                         if ( args.itemData && typeof args.itemData == 'function'  ) {
467                                 self._itemData = args.itemData;
468                         } else {
469                                 return false;
470                         }
471                         if ( args.numItemData ) {
472                                 if ( typeof args.numItemData == 'function' ) {
473                                         self._numItemData = args.numItemData( );
474                                 } else if ( typeof args.numItemData == 'number' ) {
475                                         self._numItemData = args.numItemData;
476                                 } else {
477                                         return false;
478                                 }
479                         } else {
480                                 return false;
481                         }
482                         self._getObjectNames( self._itemData( 0 ) );
483                         return true;
484                 },
485
486                 // Make up the first screen.
487                 _initLayout: function () {
488                         var self = this,
489                                 opts = self.options,
490                                 i,
491                                 $row;
492
493                         for ( i = -1; i < self._rowsPerView + 1; i += 1 ) {
494                                 $row = self._$rows[ circularNum( i, self._$rows.length ) ];
495                                 self._$view.append( $row );
496                         }
497                         self._setElementTransform( -self._cellSize );
498
499                         self._replaceRow( self._$view.children().first(), self._totalRowCnt - 1 );
500                         if ( opts.rotation && self._rowsPerView >= self._totalRowCnt ) {
501                                 self._replaceRow( self._$view.children().last(), 0 );
502                         }
503                         self._setViewSize();
504                 },
505
506                 _setViewSize : function () {
507                         var self = this,
508                                 height = 0,
509                                 width = 0;
510
511                         if ( self._direction ) {
512                                 width = self._cellSize * ( self._rowsPerView + 2 );
513                                 width = parseInt( width, 10 ) + 1;
514                                 self._$view.width( width );
515                                 self._viewSize = self._$view.width();
516                         } else {
517                                 self._$view.height( self._cellSize * ( self._rowsPerView + 2 ) );
518                                 self._$clip.height( self._clipSize );
519                                 self._viewSize = self._$view.height();
520                         }
521                 },
522
523                 _getViewWidth : function () {
524                         var self = this;
525                         return self._maxSize;
526                 },
527
528                 _getViewHeight : function () {
529                         var self = this;
530                         return self._maxSize;
531                 },
532
533                 refresh : function () {
534                         var self = this,
535                                 opts = self.options,
536                                 width = 0,
537                                 height = 0;
538
539                         self._template = $( "#" + opts.template );
540                         if ( !self._template ) {
541                                 return ;
542                         }
543
544                         width = self._calculateClipWidth();
545                         height = self._calculateClipHeight();
546                         self._$view.width( width ).height( height );
547                         self._$clip.width( width ).height( height );
548
549                         self._clipSize = self._calculateClipSize();
550                         self._calculateColumnSize();
551                         self._initPageProperty();
552                         self._setScrollBarSize();
553                 },
554
555                 _initPageProperty : function () {
556                         var self = this,
557                                 rowsPerView = 0,
558                                 $child,
559                                 columnCount = 0,
560                                 totalRowCnt = 0,
561                                 attributeName = self._direction ? "width" : "height";
562
563                         columnCount = self._calculateColumnCount();
564
565                         totalRowCnt = parseInt( self._numItemData / columnCount, 10 );
566                         self._totalRowCnt = self._numItemData % columnCount === 0 ? totalRowCnt : totalRowCnt + 1;
567                         self._itemCount = columnCount;
568
569                         if ( self._cellSize <= 0 ) {
570                                 return ;
571                         }
572
573                         rowsPerView = self._clipSize / self._cellSize;
574                         rowsPerView = Math.ceil( rowsPerView );
575                         self._rowsPerView = parseInt( rowsPerView, 10 );
576
577                         $child = self._makeRows( rowsPerView + 2 );
578                         $( self._$view ).append( $child.children() );
579                         self._$view.children().css( attributeName, self._cellSize + "px" );
580                         self._$rows = self._$view.children().detach();
581
582                         self._reservedPos = -self._cellSize;
583                         self._scalableSize = -self._cellSize;
584
585                         self._initLayout();
586
587                         self._blockScroll = self._rowsPerView > self._totalRowCnt;
588                         self._maxSizeExceptClip = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
589                         self._maxSize = self._totalRowCnt * self._cellSize;
590                         self._maxViewSize = ( self._rowsPerView ) * self._cellSize;
591                         self._modifyViewPos = -self._cellSize;
592                         if ( self._clipSize < self._maxViewSize ) {
593                                 self._modifyViewPos = ( -self._cellSize ) + ( self._clipSize - self._maxViewSize );
594                         }
595                 },
596
597                 _getinheritedSize : function ( elem ) {
598                         var $target = $( elem ),
599                                 height,
600                                 width,
601                                 NODETYPE = { ELEMENT_NODE : 1, TEXT_NODE : 3 },
602                                 ret = {
603                                         isDefinedWidth : false,
604                                         isDefinedHeight : false,
605                                         width : 0,
606                                         height : 0
607                                 };
608
609                         while ( $target[0].nodeType === NODETYPE.ELEMENT_NODE && ( ret.isDefinedWidth === false || ret.isHeightDefined === false ) ) {
610                                 height = $target[0].style.height;
611                                 width = $target[0].style.width;
612
613                                 if ( ret.isDefinedHeight === false && height !== "" ) {
614                                         // Size was defined
615                                         ret.isDefinedHeight = true;
616                                         ret.height = parseInt( height, 10 );
617                                 }
618
619                                 if ( ret.isDefinedWidth === false && width !== "" ) {
620                                         // Size was defined
621                                         ret.isDefinedWidth = true;
622                                         ret.width = parseInt( width, 10 );
623                                 }
624                                 $target = $target.parent();
625                                 if ( $target.hasClass( "ui-content" ) ) {
626                                         break;
627                                 }
628                         }
629                         return ret;
630                 },
631
632                 resize : function ( ) {
633                         var self = this,
634                                 ret = null,
635                                 rowsPerView = 0,
636                                 itemCount = 0,
637                                 totalRowCnt = 0,
638                                 diffRowCnt = 0,
639                                 clipSize = 0,
640                                 prevcnt = 0,
641                                 clipPosition = 0;
642
643                         itemCount = self._calculateColumnCount();
644                         if ( itemCount != self._itemCount ) {
645                                 totalRowCnt = parseInt( self._numItemData / itemCount, 10 );
646                                 self._totalRowCnt = self._numItemData % itemCount === 0 ? totalRowCnt : totalRowCnt + 1;
647                                 prevcnt = self._itemCount;
648                                 self._itemCount = itemCount;
649                                 clipPosition = self._getClipPosition();
650                                 self._$view.hide();
651
652                                 diffRowCnt = self._replaceRows(itemCount, prevcnt, self._totalRowCnt, clipPosition);
653                                 self._maxSizeExceptClip = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
654                                 self._maxSize = self._totalRowCnt * self._cellSize;
655                                 self._scalableSize += (-diffRowCnt) * self._cellSize;
656                                 self._reservedPos  += (-diffRowCnt) * self._cellSize;
657                                 self._setScrollBarSize();
658                                 self._setScrollBarPosition( diffRowCnt );
659
660                                 self._$view.show();
661                         }
662
663                         clipSize = self._calculateClipSize();
664                         if ( clipSize !== self._clipSize ) {
665                                 rowsPerView = clipSize / self._cellSize;
666                                 rowsPerView = parseInt( Math.ceil( rowsPerView ), 10 );
667
668                                 if ( rowsPerView > self._rowsPerView ) {
669                                         // increase row.
670                                         self._increaseRow( rowsPerView - self._rowsPerView );
671                                 } else if ( rowsPerView < self._rowsPerView ) {
672                                         // decrease row.
673                                         self._decreaseRow( self._rowsPerView - rowsPerView );
674                                 }
675                                 self._rowsPerView = rowsPerView;
676                                 self._clipSize = clipSize;
677                                 self._blockScroll = self._rowsPerView > self._totalRowCnt;
678                                 self._maxSizeExceptClip = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
679                                 self._maxSize = self._totalRowCnt * self._cellSize;
680                                 self._maxViewSize = ( self._rowsPerView ) * self._cellSize;
681                                 if ( self._clipSize < self._maxViewSize ) {
682                                         self._modifyViewPos = ( -self._cellSize ) + ( self._clipSize - self._maxViewSize );
683                                 }
684                                 if ( self._direction ) {
685                                         self._$clip.width( self._clipSize );
686                                 } else {
687                                         self._$clip.height( self._clipSize );
688                                 }
689                                 self._setScrollBarSize();
690                                 self._setScrollBarPosition( 0 );
691                                 self._setViewSize();
692                         }
693                 },
694
695                 _initScrollView : function () {
696                         var self = this;
697                         $.extend( self.options, self._scrollView.options );
698                         self.options.moveThreshold = 10;
699                         self.options.showScrollBars = false;
700                         self._getScrollHierarchy = self._scrollView._getScrollHierarchy;
701                         self._makePositioned =  self._scrollView._makePositioned;
702                         self._set_scrollbar_size = self._scrollView._set_scrollbar_size;
703                         self._setStyleTransform = self._scrollView._setElementTransform;
704                         self._hideOverflowIndicator = self._scrollView._hideOverflowIndicator;
705                         self._showOverflowIndicator = self._scrollView._showOverflowIndicator;
706                         self._setGestureScroll = self._scrollView._setGestureScroll;
707                 },
708
709                 _createTracker : function () {
710                         var self = this;
711
712                         self._tracker = new MomentumTracker( self.options );
713                         if ( self._direction ) {
714                                 self._hTracker = self._tracker;
715                                 self._$clip.width( self._clipSize );
716                         } else {
717                                 self._vTracker = self._tracker;
718                                 self._$clip.height( self._clipSize );
719                         }
720                 },
721
722                 //----------------------------------------------------//
723                 //              Overflow effect
724                 //----------------------------------------------------//
725                 _createOverflowArea : function () {
726                         var self = this,
727                                 prefix = "<div class=\"ui-virtualgrid-overflow-indicator-",
728                                 suffixTop = "-top\"></div>",
729                                 suffixBottom = "-bottom\"></div>";
730
731                         if ( self.options.rotation ) {
732                                 return;
733                         }
734
735                         if ( self._direction ) {
736                                 self._overflowTop = $( prefix + "x" + suffixTop );
737                                 self._overflowBottom = $( prefix + "x" + suffixBottom );
738                         } else {
739                                 self._overflowTop = $( prefix + "y" + suffixTop );
740                                 self._overflowBottom = $( prefix + "y" + suffixBottom );
741                         }
742
743                         self._$clip.append( self._overflowTop );
744                         self._$clip.append( self._overflowBottom );
745                         self._overflowDisplayed = false;
746                 },
747
748                 _hideVGOverflowIndicator : function () {
749                         if ( this._overflowDisplayed === false ) {
750                                 return;
751                         }
752
753                         this._overflowTop.animate( { opacity: 0 }, 300 );
754                         this._overflowBottom.animate( { opacity: 0 }, 300 );
755                         this._overflowDisplayed = false;
756                 },
757
758                 //----------------------------------------------------//
759                 //              Scrollbar               //
760                 //----------------------------------------------------//
761                 _createScrollBar : function () {
762                         var self = this,
763                                 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
764                                 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
765
766                         if ( self.options.rotation ) {
767                                 return ;
768                         }
769
770                         if ( self._direction ) {
771                                 self._$clip.append( prefix + "x" + suffix );
772                                 self._hScrollBar = $( self._$clip.children( ".ui-scrollbar-x" ) );
773                                 self._hScrollBar.find( ".ui-scrollbar-thumb" ).addClass( "ui-scrollbar-thumb-x" );
774                         } else {
775                                 self._$clip.append( prefix + "y" + suffix );
776                                 self._vScrollBar = $( self._$clip.children( ".ui-scrollbar-y" ) );
777                                 self._vScrollBar.find( ".ui-scrollbar-thumb" ).addClass( "ui-scrollbar-thumb-y" );
778                         }
779                 },
780
781                 _setScrollBarSize: function () {
782                         var self = this,
783                                 scrollBarSize = 0,
784                                 currentSize = 0,
785                                 $scrollBar,
786                                 attrName,
787                                 className;
788
789                         if ( self.options.rotation ) {
790                                 return ;
791                         }
792
793                         scrollBarSize = parseInt( self._maxViewSize / self._clipSize, 10 );
794                         if ( self._direction ) {
795                                 $scrollBar = self._hScrollBar.find( ".ui-scrollbar-thumb" );
796                                 attrName = "width";
797                                 currentSize = $scrollBar.width();
798                                 className = "ui-scrollbar-thumb-x";
799                                 self._hScrollBar.css( "width", self._clipSize );
800                         } else {
801                                 $scrollBar = self._vScrollBar.find( ".ui-scrollbar-thumb" );
802                                 attrName = "height";
803                                 className = "ui-scrollbar-thumb-y";
804                                 currentSize = $scrollBar.height();
805                                 self._vScrollBar.css( "height", self._clipSize );
806                         }
807
808                         if ( scrollBarSize > currentSize ) {
809                                 $scrollBar.removeClass( className );
810                                 $scrollBar.css( attrName, scrollBarSize );
811                         } else {
812                                 scrollBarSize = currentSize;
813                         }
814
815                         self._itemScrollSize = parseFloat( ( self._clipSize - scrollBarSize ) / ( self._totalRowCnt - self._rowsPerView ) );
816                         self._itemScrollSize = Math.round( self._itemScrollSize * 100 ) / 100;
817                 },
818
819                 _setScrollBarPosition : function ( di, duration ) {
820                         var self = this,
821                                 $sbt = null,
822                                 x = "0px",
823                                 y = "0px";
824
825                         if ( self.options.rotation ) {
826                                 return ;
827                         }
828
829                         self._currentItemCount = self._currentItemCount + di;
830                         if ( self._vScrollBar ) {
831                                 $sbt = self._vScrollBar .find( ".ui-scrollbar-thumb" );
832                                 y = ( self._currentItemCount * self._itemScrollSize ) + "px";
833                         } else {
834                                 $sbt = self._hScrollBar .find( ".ui-scrollbar-thumb" );
835                                 x = ( self._currentItemCount * self._itemScrollSize ) + "px";
836                         }
837                         self._setStyleTransform( $sbt, x, y, duration );
838                 },
839
840                 _hideScrollBars : function () {
841                         var self = this,
842                                 vclass = "ui-scrollbar-visible";
843
844                         if ( self.options.rotation ) {
845                                 return ;
846                         }
847
848                         if ( self._vScrollBar ) {
849                                 self._vScrollBar.removeClass( vclass );
850                         } else {
851                                 self._hScrollBar.removeClass( vclass );
852                         }
853                 },
854
855                 _showScrollBars : function () {
856                         var self = this,
857                                 vclass = "ui-scrollbar-visible";
858
859                         if ( self.options.rotation ) {
860                                 return ;
861                         }
862
863                         if ( self._vScrollBar ) {
864                                 self._vScrollBar.addClass( vclass );
865                         } else {
866                                 self._hScrollBar.addClass( vclass );
867                         }
868                 },
869
870                 //----------------------------------------------------//
871                 //              scroll process          //
872                 //----------------------------------------------------//
873                 centerTo: function ( selector ) {
874                         var self = this,
875                                 i,
876                                 newX = 0,
877                                 newY = 0;
878
879                         if ( !self.options.rotation ) {
880                                 return;
881                         }
882
883                         for ( i = 0; i < self._$rows.length; i++ ) {
884                                 if ( $( self._$rows[i] ).hasClass( selector ) ) {
885                                         if ( self._direction ) {
886                                                 newX = -( i * self._cellSize - self._clipSize / 2 + self._cellSize * 2 );
887                                         } else {
888                                                 newY = -( i * self._cellSize - self._clipSize / 2 + self._cellSize * 2 );
889                                         }
890                                         self.scrollTo( newX, newY );
891                                         return;
892                                 }
893                         }
894                 },
895
896                 scrollTo: function ( x, y, duration ) {
897                         var self = this;
898                         if ( self._direction ) {
899                                 x -= self._cellSize;
900                                 self._sx = self._reservedPos;
901                                 self._reservedPos = x;
902                         } else {
903                                 y -= self._cellSize;
904                                 self._sy = self._reservedPos;
905                                 self._reservedPos = y;
906                         }
907                         self._scrollView.scrollTo.apply( this, [ x, y, duration ] );
908                 },
909
910                 getScrollPosition: function () {
911                         if ( this.direction ) {
912                                 return { x: -this._ry, y: 0 };
913                         }
914                         return { x: 0, y: -this._ry };
915                 },
916
917                 _setScrollPosition: function ( x, y ) {
918                         var self = this,
919                                 sy = self._scalableSize,
920                                 distance = self._direction ? x : y,
921                                 dy = distance - sy,
922                                 di = parseInt( dy / self._cellSize, 10 ),
923                                 i = 0,
924                                 idx = 0,
925                                 replaceStartIdx = 0,
926                                 realRowCount = self._rowsPerView + 2,
927                                 $row = null;
928
929                         if ( self._blockScroll ) {
930                                 if ( dy > 0 && distance >= -self._cellSize && self._scalableSize >= -self._cellSize ) {
931                                         self._overflowDir = _OVERFLOW_DIR_UP;
932                                 }
933                                 if ( (dy < 0 && self._scalableSize <= -(self._maxSize + self._cellSize) )) {
934                                         self._overflowDir = _OVERFLOW_DIR_DOWN;
935                                 }
936                                 return;
937                         }
938
939                         if ( ! self.options.rotation ) {
940                                 if ( dy > 0 && distance >= -self._cellSize && self._scalableSize >= -self._cellSize ) {
941                                         // top
942                                         self._stopMScroll();
943                                         self._scalableSize = -self._cellSize;
944                                         self._setElementTransform( -self._cellSize );
945                                         if ( self._overflowDir === _OVERFLOW_DIR_NONE ) {
946                                                 self._overflowDir = _OVERFLOW_DIR_UP;
947                                         }
948                                         return;
949                                 }
950                                 if ( ( dy < 0 && self._scalableSize <= -( self._maxSize + self._cellSize ) ) ) {
951                                         // bottom
952                                         self._stopMScroll();
953                                         self._scalableSize = -(self._maxSizeExceptClip + self._cellSize);
954                                         self._setElementTransform( self._modifyViewPos );
955                                         if ( self._overflowDir === _OVERFLOW_DIR_NONE ) {
956                                                 self._overflowDir = _OVERFLOW_DIR_DOWN;
957                                         }
958                                         return;
959                                 }
960                         }
961
962                         replaceStartIdx = ( Math.abs( di ) < realRowCount ) ? 0 : ( di > 0 ) ? di - realRowCount : di + realRowCount;
963                         if ( di > 0 ) { // scroll up
964                                 for ( i = replaceStartIdx; i < di; i++ ) {
965                                         idx = -parseInt( ( sy / self._cellSize ) + i + 3, 10 );
966                                         $row = self._$view.children().last().detach();
967                                         self._replaceRow( $row, circularNum( idx, self._totalRowCnt ) );
968                                         self._$view.prepend( $row );
969                                 }
970                         } else if ( di < 0 ) { // scroll down
971                                 for ( i = replaceStartIdx; i > di; i-- ) {
972                                         idx = self._rowsPerView - parseInt( ( sy / self._cellSize ) + i, 10 );
973                                         $row = self._$view.children().first().detach();
974                                         self._replaceRow( $row, circularNum( idx, self._totalRowCnt ) );
975                                         self._$view.append( $row );
976                                 }
977                         }
978                         self._setScrollBarPosition( -di );
979                         self._scalableSize += di * self._cellSize;
980                         self._setElementTransform( distance - self._scalableSize - self._cellSize );
981                 },
982
983                 _setElementTransform : function ( value ) {
984                         var self = this,
985                                 x = 0,
986                                 y = 0;
987
988                         if ( self._direction ) {
989                                 x = value + "px";
990                         } else {
991                                 y = value + "px";
992                         }
993                         self._setStyleTransform( self._$view, x, y );
994                 },
995
996                 //----------------------------------------------------//
997                 //              Event handler           //
998                 //----------------------------------------------------//
999                 _handleMomentumScroll: function () {
1000                         var self = this,
1001                                 opts = self.options,
1002                                 keepGoing = false,
1003                                 v = this._$view,
1004                                 x = 0,
1005                                 y = 0,
1006                                 t = self._tracker;
1007
1008                         if ( t ) {
1009                                 t.update();
1010                                 if ( self._direction ) {
1011                                         x = t.getPosition();
1012                                 } else {
1013                                         y = t.getPosition();
1014                                 }
1015                                 keepGoing = !t.done();
1016                         }
1017
1018                         self._setScrollPosition( x, y );
1019                         if ( !opts.rotation ) {
1020                                 keepGoing = !t.done();
1021                                 self._reservedPos = self._direction ? x : y;
1022                                 // bottom
1023                                 self._reservedPos = self._reservedPos <= (-(self._maxSizeExceptClip - self._modifyViewPos)) ? ( - ( self._maxSizeExceptClip + self._cellSize) ) : self._reservedPos;
1024                                 // top
1025                                 self._reservedPos = self._reservedPos > -self._cellSize ? -self._cellSize : self._reservedPos;
1026                         } else {
1027                                 self._reservedPos = self._direction ? x : y;
1028                         }
1029                         self._$clip.trigger( self.options.updateEventName, [ { x: x, y: y } ] );
1030
1031                         if ( keepGoing ) {
1032                                 self._timerID = setTimeout( self._timerCB, self._timerInterval );
1033                         } else {
1034                                 self._stopMScroll();
1035                         }
1036                 },
1037
1038                 _startMScroll: function ( speedX, speedY ) {
1039                         var self = this;
1040                         if ( self._direction ) {
1041                                 self._sx = self._reservedPos;
1042                         } else {
1043                                 self._sy = self._reservedPos;
1044                         }
1045                         self._scrollView._startMScroll.apply( self, [ speedX, speedY ] );
1046                 },
1047
1048                 _stopMScroll: function () {
1049                         this._scrollView._stopMScroll.apply( this );
1050                 },
1051
1052                 _enableTracking: function () {
1053                         var self = this;
1054                         self._$view.bind( self._dragMoveEvt, self._dragMoveCB );
1055                         self._$view.bind( self._dragStopEvt, self._dragStopCB );
1056                         self._scrollView._enableTracking.apply( self );
1057                 },
1058
1059                 _disableTracking: function () {
1060                         var self = this;
1061                         self._$view.unbind( self._dragMoveEvt, self._dragMoveCB );
1062                         self._$view.unbind( self._dragStopEvt, self._dragStopCB );
1063                         self._scrollView._disableTracking.apply( self );
1064                 },
1065
1066                 _handleDragStart: function ( e, ex, ey ) {
1067                         var self = this;
1068                         self._scrollView._handleDragStart.apply( this, [ e, ex, ey ] );
1069                         self._eventPos = self._direction ? ex : ey;
1070                         self._nextPos = self._reservedPos;
1071                 },
1072
1073                 _handleDragMove: function ( e, ex, ey ) {
1074                         var self = this,
1075                                 dx = ex - self._lastX,
1076                                 dy = ey - self._lastY,
1077                                 x = 0,
1078                                 y = 0,
1079                                 diffFromStartPos = 0,
1080                                 diffFromLastPos = 0,
1081                                 opacity = 0,
1082                                 overflowPos = 0,
1083                                 overFlowTarget = null;
1084
1085                         self._lastMove = getCurrentTime();
1086                         self._speedX = dx;
1087                         self._speedY = dy;
1088
1089                         self._didDrag = true;
1090
1091                         self._lastX = ex;
1092                         self._lastY = ey;
1093
1094                         if ( self._direction ) {
1095                                 self._movePos = ex - self._eventPos;
1096                                 x = self._nextPos + self._movePos;
1097                                 overflowPos = ex;
1098                         } else {
1099                                 self._movePos = ey - self._eventPos;
1100                                 y = self._nextPos + self._movePos;
1101                                 overflowPos = ey;
1102                         }
1103                         self._showScrollBars();
1104                         self._setScrollPosition( x, y );
1105                         if ( self._overflowDir !== _OVERFLOW_DIR_NONE ) {
1106                                 overFlowTarget = ( self._overflowDir === _OVERFLOW_DIR_UP ) ? self._overflowTop : self._overflowBottom;
1107                                 if ( !self._overflowDisplayed ) {
1108                                         self._overflowDisplayed = true;
1109                                         self._overflowStartPos = overflowPos;
1110                                 }
1111                                 diffFromStartPos = ( overflowPos - self._overflowStartPos ) * self._overflowDir;
1112                                 opacity = ( diffFromStartPos < 0 ) ?
1113                                                         0 : ( diffFromStartPos > self._overflowMaxDragDist ) ?
1114                                                                 1 : ( diffFromStartPos / self._overflowMaxDragDist );
1115                                 overFlowTarget.css( "opacity", opacity );
1116                         }
1117
1118                         return false;
1119                 },
1120
1121                 _handleDragStop: function ( e ) {
1122                         var self = this;
1123
1124                         self._reservedPos = self._movePos ? self._nextPos + self._movePos : self._reservedPos;
1125                         self._scrollView._handleDragStop.apply( this, [ e ] );
1126                         if ( self._overflowDir !== _OVERFLOW_DIR_NONE ) {
1127                                 self._overflowDir = _OVERFLOW_DIR_NONE;
1128                                 self._hideVGOverflowIndicator();
1129                         }
1130                         return self._didDrag ? false : undefined;
1131                 },
1132
1133                 _addBehaviors: function () {
1134                         var self = this;
1135
1136                         // scroll event handler.
1137                         if ( self.options.eventType === "mouse" ) {
1138                                 self._dragStartEvt = "mousedown";
1139                                 self._dragStartCB = function ( e ) {
1140                                         return self._handleDragStart( e, e.clientX, e.clientY );
1141                                 };
1142
1143                                 self._dragMoveEvt = "mousemove";
1144                                 self._dragMoveCB = function ( e ) {
1145                                         return self._handleDragMove( e, e.clientX, e.clientY );
1146                                 };
1147
1148                                 self._dragStopEvt = "mouseup";
1149                                 self._dragStopCB = function ( e ) {
1150                                         return self._handleDragStop( e, e.clientX, e.clientY );
1151                                 };
1152
1153                                 self._$view.bind( "vclick", function ( e ) {
1154                                         return !self._didDrag;
1155                                 } );
1156                         } else { //touch
1157                                 self._dragStartEvt = "touchstart";
1158                                 self._dragStartCB = function ( e ) {
1159                                         var t = e.originalEvent.targetTouches[0];
1160                                         return self._handleDragStart( e, t.pageX, t.pageY );
1161                                 };
1162
1163                                 self._dragMoveEvt = "touchmove";
1164                                 self._dragMoveCB = function ( e ) {
1165                                         var t = e.originalEvent.targetTouches[0];
1166                                         return self._handleDragMove( e, t.pageX, t.pageY );
1167                                 };
1168
1169                                 self._dragStopEvt = "touchend";
1170                                 self._dragStopCB = function ( e ) {
1171                                         return self._handleDragStop( e );
1172                                 };
1173                         }
1174                         self._$view.bind( self._dragStartEvt, self._dragStartCB );
1175
1176                         // other events.
1177                         self._$view.delegate( ".virtualgrid-item", "click", function ( event ) {
1178                                 var $selectedItem = $( this );
1179                                 $selectedItem.trigger( "select", this );
1180                         } );
1181
1182                         $( window ).bind( "resize", function ( e ) {
1183                                 var height = 0,
1184                                         $virtualgrid = $( ".ui-virtualgrid-view" );
1185                                 if ( $virtualgrid.length !== 0 ) {
1186                                         if ( self._direction ) {
1187                                                 height = self._calculateClipHeight();
1188                                                 self._$view.height( height );
1189                                                 self._$clip.height( height );
1190                                         } else {
1191                                                 height = self._calculateClipWidth();
1192                                                 self._$view.width( height );
1193                                                 self._$clip.width( height );
1194                                         }
1195                                         self.resize();
1196                                 }
1197                         } );
1198
1199                         $( document ).one( "pageshow", function ( event ) {
1200                                 var $page = $( self.element ).parents( ".ui-page" ),
1201                                         $header = $page.find( ":jqmData(role='header')" ),
1202                                         $footer = $page.find( ":jqmData(role='footer')" ),
1203                                         $content = $page.find( ":jqmData(role='content')" ),
1204                                         footerHeight = $footer ? $footer.height() : 0,
1205                                         headerHeight = $header ? $header.height() : 0;
1206
1207                                 if ( $page && $content ) {
1208                                         $content.height( window.innerHeight - headerHeight - footerHeight ).css( "overflow", "hidden" );
1209                                         $content.addClass( "ui-virtualgrid-content" );
1210                                 }
1211                         } );
1212                 },
1213
1214                 //----------------------------------------------------//
1215                 //              Calculate size about dom element.               //
1216                 //----------------------------------------------------//
1217                 _calculateClipSize : function () {
1218                         var self = this,
1219                                 clipSize = 0;
1220
1221                         if ( self._direction ) {
1222                                 clipSize = self._calculateClipWidth();
1223                         } else {
1224                                 clipSize = self._calculateClipHeight();
1225                         }
1226                         return clipSize;
1227                 },
1228
1229                 _calculateClipWidth : function () {
1230                         var self = this,
1231                                 view = $( self.element ),
1232                                 $parent = $( self.element ).parent(),
1233                                 paddingValue = 0,
1234                                 clipSize = $( window ).width();
1235
1236                         if ( self._inheritedSize.isDefinedWidth ) {
1237                                 return self._inheritedSize.width;
1238                         }
1239
1240                         if ( $parent.hasClass( "ui-content" ) ) {
1241                                 paddingValue = parseInt( $parent.css( "padding-left" ), 10 );
1242                                 clipSize = clipSize - ( paddingValue || 0 );
1243                                 paddingValue = parseInt( $parent.css( "padding-right" ), 10 );
1244                                 clipSize = clipSize - ( paddingValue || 0 );
1245                         } else {
1246                                 clipSize = view.width();
1247                         }
1248                         return clipSize;
1249                 },
1250
1251                 _calculateClipHeight : function () {
1252                         var self = this,
1253                                 view = $( self.element ),
1254                                 $parent = view.parent(),
1255                                 header = null,
1256                                 footer = null,
1257                                 paddingValue = 0,
1258                                 clipSize = $( window ).height();
1259
1260                         if ( self._inheritedSize.isDefinedHeight ) {
1261                                 return self._inheritedSize.height;
1262                         }
1263
1264                         if ( $parent.hasClass( "ui-content" ) ) {
1265                                 paddingValue = parseInt( $parent.css( "padding-top" ), 10 );
1266                                 clipSize = clipSize - ( paddingValue || 0 );
1267                                 paddingValue = parseInt( $parent.css( "padding-bottom" ), 10 );
1268                                 clipSize = clipSize - ( paddingValue || 0 );
1269                                 header = $parent.siblings( ".ui-header" );
1270                                 footer = $parent.siblings( ".ui-footer" );
1271
1272                                 if ( header ) {
1273                                         if ( header.outerHeight( true ) === null ) {
1274                                                 clipSize = clipSize - ( $( ".ui-header" ).outerHeight() || 0 );
1275                                         } else {
1276                                                 clipSize = clipSize - header.outerHeight( true );
1277                                         }
1278                                 }
1279                                 if ( footer ) {
1280                                         clipSize = clipSize - footer.outerHeight( true );
1281                                 }
1282                         } else {
1283                                 clipSize = view.height();
1284                         }
1285                         return clipSize;
1286                 },
1287
1288                 _calculateColumnSize : function () {
1289                         var self = this,
1290                                 $tempBlock,
1291                                 $cell;
1292
1293                         $tempBlock = self._makeRows( 1 );
1294                         self._$view.append( $tempBlock.children().first() );
1295                         if ( self._direction ) {
1296                                 // x-axis
1297                                 self._viewSize = self._$view.width();
1298                                 $cell = self._$view.children().first().children().first();
1299                                 self._cellSize = $cell.outerWidth( true );
1300                                 self._cellOtherSize = $cell.outerHeight( true );
1301                         } else {
1302                                 // y-axis
1303                                 self._viewSize = self._$view.height();
1304                                 $cell = self._$view.children().first().children().first();
1305                                 self._cellSize = $cell.outerHeight( true );
1306                                 self._cellOtherSize = $cell.outerWidth( true );
1307                         }
1308                         $tempBlock.remove();
1309                         self._$view.children().remove();
1310                 },
1311
1312                 _calculateColumnCount : function ( ) {
1313                         var self = this,
1314                                 $view = $( self.element ),
1315                                 viewSize = self._direction ? $view.innerHeight() : $view.innerWidth(),
1316                                 itemCount = 0 ;
1317
1318                         if ( self._direction ) {
1319                                 viewSize = viewSize - ( parseInt( $view.css( "padding-top" ), 10 ) + parseInt( $view.css( "padding-bottom" ), 10 ) );
1320                         } else {
1321                                 viewSize = viewSize - ( parseInt( $view.css( "padding-left" ), 10 ) + parseInt( $view.css( "padding-right" ), 10 ) );
1322                         }
1323
1324                         itemCount = parseInt( ( viewSize / self._cellOtherSize ), 10 );
1325                         return itemCount > 0 ? itemCount : 1 ;
1326                 },
1327
1328                 // Read the position of clip form property ('webkit-transform').
1329                 // @return : number - position of clip.
1330                 _getClipPosition : function () {
1331                         var self = this,
1332                                 matrix = null,
1333                                 contents = null,
1334                                 result = -self._cellSize,
1335                                 $scrollview = self._$view.closest( ".ui-scrollview-view" );
1336
1337                         if ( $scrollview ) {
1338                                 matrix = $scrollview.css( "-webkit-transform" );
1339                                 contents = matrix.substr( 7 );
1340                                 contents = contents.substr( 0, contents.length - 1 );
1341                                 contents = contents.split( ', ' );
1342                                 result =  Math.abs( contents [5] );
1343                         }
1344                         return result;
1345                 },
1346
1347                 //----------------------------------------------------//
1348                 //              DOM Element handle              //
1349                 //----------------------------------------------------//
1350                 _makeRows : function ( count ) {
1351                         var self = this,
1352                                 opts = self.options,
1353                                 index = 0,
1354                                 $row = null,
1355                                 $wrapper = null;
1356
1357                         $wrapper = $( self._createElement( "div" ) );
1358                         $wrapper.addClass( "ui-scrollview-view" );
1359                         for ( index = 0; index < count ; index += 1 ) {
1360                                 $row = self._makeRow( self._template, index );
1361                                 if ( self._direction ) {
1362                                         $row.css( "top", 0 ).css( "left", ( index * self._cellSize ) );
1363                                 }
1364                                 $wrapper.append( $row );
1365                         }
1366                         return $wrapper;
1367                 },
1368
1369                 // make a single row block
1370                 _makeRow : function ( myTemplate, rowIndex ) {
1371                         var self = this,
1372                                 opts = self.options,
1373                                 index = rowIndex * self._itemCount,
1374                                 htmlData = null,
1375                                 itemData = null,
1376                                 colIndex = 0,
1377                                 attrName = self._direction ? "top" : "left",
1378                                 blockClassName = self._direction ? "ui-virtualgrid-wrapblock-x" : "ui-virtualgrid-wrapblock-y",
1379                                 blockAttrName = self._direction ? "top" : "left",
1380                                 wrapBlock = $( self._createElement( "div" ) );
1381
1382                         wrapBlock.addClass( blockClassName ).attr( "row-index", rowIndex );
1383                         for ( colIndex = 0; colIndex < self._itemCount; colIndex++ ) {
1384                                 htmlData = self._makeHtmlData( myTemplate, index, colIndex );
1385                                 if ( htmlData ) {
1386                                         wrapBlock.append( htmlData );
1387                                 }
1388                                 index += 1;
1389                         }
1390                         return wrapBlock;
1391                 },
1392
1393                 _makeHtmlData : function ( myTemplate, dataIndex, colIndex ) {
1394                         var self = this,
1395                                 htmlData = null,
1396                                 itemData = null,
1397                                 attrName = self._direction ? "top" : "left";
1398
1399                         itemData = self._itemData( dataIndex );
1400                         if ( itemData ) {
1401                                 htmlData = self._tmpl( itemData );
1402                                 htmlData.css( attrName, ( colIndex * self._cellOtherSize ) ).addClass( "virtualgrid-item" );
1403                         }
1404                         return htmlData;
1405                 },
1406
1407                 _increaseRow : function ( num ) {
1408                         var self = this,
1409                                 rotation = self.options.rotation,
1410                                 $row = null,
1411                                 headItemIndex = 0,
1412                                 tailItemIndex = 0,
1413                                 itemIndex = 0,
1414                                 size = self._scalableSize,
1415                                 idx = 0;
1416
1417                         headItemIndex = parseInt( $( self._$view.children().first() ).attr( "row-index" ), 10 ) - 1;
1418                         tailItemIndex = parseInt( $( self._$view.children()[self._rowsPerView] ).attr( "row-index" ), 10 ) + 1;
1419
1420                         for ( idx = 1 ; idx <= num ; idx++ ) {
1421                                 if ( tailItemIndex + idx  >= self._totalRowCnt ) {
1422                                         $row = self._makeRow( self._template, headItemIndex );
1423                                         self._$view.prepend( $row );
1424                                         headItemIndex -= 1;
1425                                 } else {
1426                                         $row = self._makeRow( self._template, tailItemIndex + idx );
1427                                         self._$view.append( $row );
1428                                 }
1429                                 if ( self._direction ) {
1430                                         $row.width( self._cellSize );
1431                                 } else {
1432                                         $row.height( self._cellSize );
1433                                 }
1434                         }
1435                 },
1436
1437                 _decreaseRow : function ( num ) {
1438                         var self = this,
1439                                 idx = 0;
1440
1441                         for ( idx = 0 ; idx < num ; idx++ ) {
1442                                 self._$view.children().last().remove();
1443                         }
1444                 },
1445
1446                 _replaceRows : function ( curCnt, prevCnt, maxCnt, clipPosition ) {
1447                         var self = this,
1448                                 $rows = self._$view.children(),
1449                                 prevRowIndex = 0,
1450                                 rowIndex = 0,
1451                                 diffRowCnt = 0,
1452                                 targetCnt = 1,
1453                                 filterCondition = ( self._filterRatio * self._cellSize ) + self._cellSize,
1454                                 idx = 0;
1455
1456                         if ( filterCondition < clipPosition ) {
1457                                 targetCnt += 1;
1458                         }
1459
1460                         prevRowIndex = parseInt( $( $rows[targetCnt] ).attr( "row-index" ), 10 );
1461                         if ( prevRowIndex === 0 ) {
1462                                 // only top.
1463                                 rowIndex = maxCnt - targetCnt;
1464                         } else {
1465                                 rowIndex = Math.round( ( prevRowIndex * prevCnt ) / curCnt );
1466                                 if ( rowIndex + self._rowsPerView >= maxCnt ) {
1467                                         // only bottom.
1468                                         rowIndex = maxCnt - self._rowsPerView;
1469                                 }
1470                                 diffRowCnt = prevRowIndex - rowIndex;
1471                                 rowIndex -= targetCnt;
1472                         }
1473
1474                         for ( idx = 0 ; idx < $rows.length ; idx += 1 ) {
1475                                 self._replaceRow( $rows[idx], circularNum( rowIndex, self._totalRowCnt ) );
1476                                 rowIndex++;
1477                         }
1478                         return -diffRowCnt;
1479                 },
1480
1481                 _replaceRow : function ( block, index ) {
1482                         var self = this,
1483                                 opts = self.options,
1484                                 $columns = null,
1485                                 $column = null,
1486                                 $block = block.attr ? block : $( block ),
1487                                 data = null,
1488                                 htmlData = null,
1489                                 myTemplate = null,
1490                                 idx = 0,
1491                                 dataIdx = 0,
1492                                 tempBlocks = null;
1493
1494                         $columns = $block.attr( "row-index", index ).children();
1495                         if ( $columns.length !== self._itemCount ) {
1496                                 $block.children().remove();
1497                                 tempBlocks = $( self._makeRow( self._template, index ) );
1498                                 $block.append( tempBlocks.children() );
1499                                 tempBlocks.remove();
1500                                 return ;
1501                         }
1502
1503                         dataIdx = index * self._itemCount;
1504                         for ( idx = 0; idx < self._itemCount ; idx += 1 ) {
1505                                 $column = $columns.eq( idx );
1506                                 data = self._itemData( dataIdx );
1507                                 if ( $column && data ) {
1508                                         htmlData = self._tmpl( data );
1509                                         self._replace( $column, htmlData, false );
1510                                         htmlData.remove();      // Clear temporary htmlData to free cache
1511                                         dataIdx ++;
1512                                 } else if ( $column && !data ) {
1513                                         $column.remove();
1514                                 }
1515                         }
1516                 },
1517
1518                 _createElement : function ( tag ) {
1519                         var element = document.createElement( tag );
1520
1521                         this._fragment.appendChild( element );
1522                         return element;
1523                 },
1524                 _getObjectNames : function ( obj ) {
1525                         var properties = [],
1526                                 name = "";
1527
1528                         for ( name in obj ) {
1529                                 properties.push( name );
1530                         }
1531                         this._properties = properties;
1532                 },
1533
1534                 _tmpl : function ( data ) {
1535                         var self = this,
1536                                 idx = 0,
1537                                 plainMsg,
1538                                 ret;
1539                         if ( !data ) {
1540                                 return ;
1541                         }
1542
1543                         plainMsg = self._template.text();
1544                         for ( idx = 0 ; idx < self._properties.length ; idx++ ) {
1545                                 plainMsg = self._strReplace( plainMsg, "${" + self._properties[ idx ] + "}" , data[ self._properties[ idx ] ] );
1546                         }
1547                         ret = $( plainMsg );
1548                         return ret;
1549                 },
1550
1551                 _strReplace : function ( plainMsg, stringToFind, stringToReplace ) {
1552                         var temp = plainMsg,
1553                                 index = plainMsg.indexOf( stringToFind );
1554                         while ( index !== -1 ) {
1555                                 temp = temp.replace( stringToFind, stringToReplace );
1556                                 index = temp.indexOf( stringToFind );
1557                         }
1558                         return temp;
1559                 },
1560
1561                 /* Text & image src replace function */
1562                 // @param oldItem   : prev HtmlDivElement
1563                 // @param newItem   : new HtmlDivElement for replace
1564                 // @param key       :
1565                 _replace : function ( oldItem, newItem, key ) {
1566                         var NODETYPE = { ELEMENT_NODE : 1, TEXT_NODE : 3 };
1567
1568                         $( oldItem ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).each( function ( index ) {
1569                                 var oldObj = $( this ),
1570                                         newText = $( newItem ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).eq( index ).text();
1571
1572                                 $( oldObj ).contents().filter( function () {
1573                                         return ( this.nodeType == NODETYPE.TEXT_NODE );
1574                                 }).get( 0 ).data = newText;
1575                         });
1576
1577                         $( oldItem ).find( "img" ).each( function ( imgIndex ) {
1578                                 var oldObj = $( this ),
1579                                         newImg = $( newItem ).find( "img" ).eq( imgIndex ).attr( "src" );
1580
1581                                 $( oldObj ).attr( "src", newImg );
1582                         });
1583                         $( oldItem ).removeData();
1584                         if ( key ) {
1585                                 $( oldItem ).data( key, $( newItem ).data( key ) );
1586                         }
1587                 }
1588         } );
1589
1590         $( document ).bind( "pagecreate create", function ( e ) {
1591                 $( ":jqmData(role='virtualgrid')" ).virtualgrid();
1592         } );
1593 } ( jQuery, window, document ) );