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