Tizen 2.0 Release
[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                                 _fragment : null,
383
384                                 _filterRatio : 0.9
385                         });
386
387                         var self = this,
388                                 $dom = $(self.element),
389                                 opts = self.options,
390                                 $item = null;
391
392                         // itemData
393                         // If mandatory options are not given, Do nothing.
394                         if ( !args ) {
395                                 return ;
396                         }
397
398                         if ( !self._loadData(args) ) {
399                                 return;
400                         }
401
402                         // make a fragment.
403                         self._fragment = document.createDocumentFragment();
404
405                         // read defined properties(width and height) from dom element.
406                         self._inheritedSize = self._getinheritedSize(self.element);
407
408                         // set a scroll direction.
409                         self._direction = opts.direction === 'x' ? true : false;
410
411                         // make view layer
412                         self._$clip = $(self.element).addClass("ui-scrollview-clip").addClass("ui-virtualgrid-view");
413                         $item = $(document.createElement("div")).addClass("ui-scrollview-view");
414                         self._clipSize =  self._calculateClipSize();
415                         self._$clip.append($item);
416                         self._$view = $item;
417                         self._$clip.css("overflow", "hidden");
418                         self._$view.css("overflow", "hidden");
419
420                         // inherit from scrollview widget.
421                         self._scrollView = $.tizen.scrollview.prototype;
422                         self._initScrollView();
423
424                         // create tracker.
425                         self._createTracker();
426                         self._makePositioned(self._$clip);
427                         self._timerInterval = 1000 / self.options.fps;
428
429                         self._timerID = 0;
430                         self._timerCB = function () {
431                                 self._handleMomentumScroll();
432                         };
433                         $dom.closest(".ui-content").addClass("ui-virtualgrid-content").css("overflow", "hidden");
434
435                         // add event handler.
436                         self._addBehaviors();
437
438                         self._currentItemCount = 0;
439                         self._createScrollBar();
440                         self.refresh();
441                 },
442
443                 // The argument is checked for compliance with the specified format.
444                 // @param args   : Object
445                 // @return boolean
446                 _loadData : function ( args ) {
447                         var self = this;
448
449                         if ( args.itemData && typeof args.itemData == 'function'  ) {
450                                 self._itemData = args.itemData;
451                         } else {
452                                 return false;
453                         }
454                         if ( args.numItemData ) {
455                                 if ( typeof args.numItemData == 'function' ) {
456                                         self._numItemData = args.numItemData( );
457                                 } else if ( typeof args.numItemData == 'number' ) {
458                                         self._numItemData = args.numItemData;
459                                 } else {
460                                         return false;
461                                 }
462                         } else {
463                                 return false;
464                         }
465                         return true;
466                 },
467
468                 // Make up the first screen.
469                 _initLayout: function () {
470                         var self = this,
471                                 opts = self.options,
472                                 i,
473                                 $row;
474
475                         for ( i = -1; i < self._rowsPerView + 1; i += 1 ) {
476                                 $row = self._$rows[ circularNum( i, self._$rows.length ) ];
477                                 self._$view.append( $row );
478                         }
479                         self._setElementTransform( -self._cellSize );
480
481                         self._replaceRow(self._$view.children().first(), self._totalRowCnt - 1);
482                         if ( opts.rotation && self._rowsPerView >= self._totalRowCnt ) {
483                                 self._replaceRow(self._$view.children().last(), 0);
484                         }
485                         self._setViewSize();
486                 },
487
488                 _setViewSize : function () {
489                         var self = this,
490                                 height = 0,
491                                 width = 0;
492
493                         if ( self._direction ) {
494                                 width = self._cellSize * ( self._rowsPerView + 2 );
495                                 width = parseInt(width, 10) + 1;
496                                 self._$view.width( width );
497                                 self._viewSize = self._$view.width();
498                         } else {
499                                 self._$view.height( self._cellSize * ( self._rowsPerView + 2 ) );
500                                 self._$clip.height( self._clipSize );
501                                 self._viewSize = self._$view.height();
502                         }
503                 },
504
505                 _getViewHeight : function () {
506                         var self = this;
507                         return self._$view.height();
508                 },
509
510                 refresh : function () {
511                         var self = this,
512                                 opts = self.options,
513                                 width = 0,
514                                 height = 0;
515
516                         self._template = $( "#" + opts.template );
517                         if ( !self._template ) {
518                                 return ;
519                         }
520
521                         width = self._calculateClipWidth();
522                         height = self._calculateClipHeight();
523                         self._$view.width(width).height(height);
524                         self._$clip.width(width).height(height);
525
526                         self._clipSize = self._calculateClipSize();
527                         self._calculateColumnSize();
528                         self._initPageProperty();
529                         self._setScrollBarSize();
530                 },
531
532                 _initPageProperty : function () {
533                         var self = this,
534                                 rowsPerView = 0,
535                                 $child,
536                                 columnCount = 0,
537                                 totalRowCnt = 0,
538                                 attributeName = self._direction ? "width" : "height";
539
540                         columnCount = self._calculateColumnCount();
541
542                         totalRowCnt = parseInt(self._numItemData / columnCount , 10 );
543                         self._totalRowCnt = self._numItemData % columnCount === 0 ? totalRowCnt : totalRowCnt + 1;
544                         self._itemCount = columnCount;
545
546                         if ( self._cellSize <= 0) {
547                                 return ;
548                         }
549
550                         rowsPerView = self._clipSize / self._cellSize;
551                         rowsPerView = Math.ceil( rowsPerView );
552                         self._rowsPerView = parseInt( rowsPerView, 10);
553
554                         $child = self._makeRows( rowsPerView + 2 );
555                         $(self._$view).append($child.children());
556                         self._$view.children().css(attributeName, self._cellSize + "px");
557                         self._$rows = self._$view.children().detach();
558
559                         self._reservedPos = -self._cellSize;
560                         self._scalableSize = -self._cellSize;
561
562                         self._initLayout();
563
564                         self._blockScroll = self._rowsPerView > self._totalRowCnt;
565                         self._maxSize = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
566                         self._maxViewSize = ( self._rowsPerView ) * self._cellSize;
567                         self._modifyViewPos = -self._cellSize;
568                         if ( self._clipSize < self._maxViewSize ) {
569                                 self._modifyViewPos = (-self._cellSize) + ( self._clipSize - self._maxViewSize );
570                         }
571                 },
572
573                 _getinheritedSize : function ( elem ) {
574                         var $target = $(elem),
575                                 height,
576                                 width,
577                                 NODETYPE = { ELEMENT_NODE : 1, TEXT_NODE : 3 },
578                                 ret = {
579                                         isDefinedWidth : false,
580                                         isDefinedHeight : false,
581                                         width : 0,
582                                         height : 0
583                                 };
584
585                         while ( $target[0].nodeType === NODETYPE.ELEMENT_NODE && (ret.isDefinedWidth === false || ret.isHeightDefined === false )) {
586                                 height = $target[0].style.height;
587                                 width = $target[0].style.width;
588
589                                 if (ret.isDefinedHeight === false && height !== "" ) {
590                                         // Size was defined
591                                         ret.isDefinedHeight = true;
592                                         ret.height = parseInt(height, 10);
593                                 }
594
595                                 if ( ret.isDefinedWidth === false && width !== "" ) {
596                                         // Size was defined
597                                         ret.isDefinedWidth = true;
598                                         ret.width = parseInt(width, 10);
599                                 }
600                                 $target = $target.parent();
601                         }
602                         return ret;
603                 },
604
605                 resize : function ( ) {
606                         var self = this,
607                                 ret = null,
608                                 rowsPerView = 0,
609                                 itemCount = 0,
610                                 totalRowCnt = 0,
611                                 diffRowCnt = 0,
612                                 clipSize = 0,
613                                 prevcnt = 0,
614                                 clipPosition = 0;
615
616                         itemCount = self._calculateColumnCount();
617                         if ( itemCount != self._itemCount ) {
618                                 totalRowCnt = parseInt(self._numItemData / itemCount , 10 );
619                                 self._totalRowCnt = self._numItemData % itemCount === 0 ? totalRowCnt : totalRowCnt + 1;
620                                 prevcnt = self._itemCount;
621                                 self._itemCount = itemCount;
622                                 clipPosition = self._getClipPosition();
623                                 self._$view.hide();
624
625                                 diffRowCnt = self._replaceRows(itemCount, prevcnt, self._totalRowCnt, clipPosition);
626                                 self._maxSize = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
627                                 self._scalableSize += (-diffRowCnt) * self._cellSize;
628                                 self._reservedPos  += (-diffRowCnt) * self._cellSize;
629                                 self._setScrollBarSize();
630                                 self._setScrollBarPosition(diffRowCnt);
631
632                                 self._$view.show();
633                         }
634
635                         clipSize = self._calculateClipSize();
636                         if ( clipSize !== self._clipSize ) {
637                                 rowsPerView = clipSize / self._cellSize;
638                                 rowsPerView = parseInt( Math.ceil( rowsPerView ), 10 );
639
640                                 if ( rowsPerView > self._rowsPerView ) {
641                                         // increase row.
642                                         self._increaseRow( rowsPerView - self._rowsPerView );
643                                 } else if ( rowsPerView < self._rowsPerView ) {
644                                         // decrease row.
645                                         self._decreaseRow( self._rowsPerView - rowsPerView );
646                                 }
647                                 self._rowsPerView = rowsPerView;
648                                 self._clipSize = clipSize;
649                                 self._blockScroll = self._rowsPerView > self._totalRowCnt;
650                                 self._maxSize = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
651                                 self._maxViewSize = ( self._rowsPerView ) * self._cellSize;
652                                 if ( self._clipSize < self._maxViewSize ) {
653                                         self._modifyViewPos = (-self._cellSize) + ( self._clipSize - self._maxViewSize );
654                                 }
655                                 if ( self._direction ) {
656                                         self._$clip.width(self._clipSize);
657                                 } else {
658                                         self._$clip.height(self._clipSize);
659                                 }
660                                 self._setScrollBarSize();
661                                 self._setScrollBarPosition(0);
662                                 self._setViewSize();
663                         }
664                 },
665
666                 _initScrollView : function () {
667                         var self = this;
668                         $.extend(self.options, self._scrollView.options);
669                         self.options.moveThreshold = 10;
670                         self.options.showScrollBars = false;
671                         self._getScrollHierarchy = self._scrollView._getScrollHierarchy;
672                         self._makePositioned =  self._scrollView._makePositioned;
673                         self._set_scrollbar_size = self._scrollView._set_scrollbar_size;
674                         self._setStyleTransform = self._scrollView._setElementTransform;
675                         self._hideOverflowIndicator = self._scrollView._hideOverflowIndicator;
676                         self._showOverflowIndicator = self._scrollView._showOverflowIndicator;
677                         self._setGestureScroll = self._scrollView._setGestureScroll;
678                 },
679
680                 _createTracker : function () {
681                         var self = this;
682
683                         self._tracker = new MomentumTracker(self.options);
684                         if ( self._direction ) {
685                                 self._hTracker = self._tracker;
686                                 self._$clip.width(self._clipSize);
687                         } else {
688                                 self._vTracker = self._tracker;
689                                 self._$clip.height(self._clipSize);
690                         }
691                 },
692
693                 //----------------------------------------------------//
694                 //              Scrollbar               //
695                 //----------------------------------------------------//
696                 _createScrollBar : function () {
697                         var self = this,
698                                 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
699                                 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
700
701                         if ( self.options.rotation ) {
702                                 return ;
703                         }
704
705                         if ( self._direction ) {
706                                 self._$clip.append( prefix + "x" + suffix );
707                                 self._hScrollBar = $(self._$clip.children(".ui-scrollbar-x"));
708                                 self._hScrollBar.find(".ui-scrollbar-thumb").addClass("ui-scrollbar-thumb-x");
709                         } else {
710                                 self._$clip.append( prefix + "y" + suffix );
711                                 self._vScrollBar = $(self._$clip.children(".ui-scrollbar-y"));
712                                 self._vScrollBar.find(".ui-scrollbar-thumb").addClass("ui-scrollbar-thumb-y");
713                         }
714                 },
715
716                 _setScrollBarSize: function () {
717                         var self = this,
718                                 scrollBarSize = 0,
719                                 currentSize = 0,
720                                 $scrollBar,
721                                 attrName,
722                                 className;
723
724                         if ( self.options.rotation ) {
725                                 return ;
726                         }
727
728                         scrollBarSize = parseInt( self._maxViewSize / self._clipSize , 10);
729                         if ( self._direction ) {
730                                 $scrollBar = self._hScrollBar.find(".ui-scrollbar-thumb");
731                                 attrName = "width";
732                                 currentSize = $scrollBar.width();
733                                 className = "ui-scrollbar-thumb-x";
734                                 self._hScrollBar.css("width", self._clipSize);
735                         } else {
736                                 $scrollBar = self._vScrollBar.find(".ui-scrollbar-thumb");
737                                 attrName = "height";
738                                 className = "ui-scrollbar-thumb-y";
739                                 currentSize = $scrollBar.height();
740                                 self._vScrollBar.css("height", self._clipSize);
741                         }
742
743                         if ( scrollBarSize > currentSize ) {
744                                 $scrollBar.removeClass(className);
745                                 $scrollBar.css(attrName, scrollBarSize);
746                         } else {
747                                 scrollBarSize = currentSize;
748                         }
749
750                         self._itemScrollSize = parseFloat( ( self._clipSize - scrollBarSize ) / ( self._totalRowCnt - self._rowsPerView ) );
751                         self._itemScrollSize = Math.round(self._itemScrollSize * 100) / 100;
752                 },
753
754                 _setScrollBarPosition : function ( di, duration ) {
755                         var self = this,
756                                 $sbt = null,
757                                 x = "0px",
758                                 y = "0px";
759
760                         if ( self.options.rotation ) {
761                                 return ;
762                         }
763
764                         self._currentItemCount = self._currentItemCount + di;
765                         if ( self._vScrollBar ) {
766                                 $sbt = self._vScrollBar .find(".ui-scrollbar-thumb");
767                                 y = ( self._currentItemCount * self._itemScrollSize ) + "px";
768                         } else {
769                                 $sbt = self._hScrollBar .find(".ui-scrollbar-thumb");
770                                 x = ( self._currentItemCount * self._itemScrollSize ) + "px";
771                         }
772                         self._setStyleTransform( $sbt, x, y, duration );
773                 },
774
775                 _hideScrollBars : function () {
776                         var self = this,
777                                 vclass = "ui-scrollbar-visible";
778
779                         if ( self.options.rotation ) {
780                                 return ;
781                         }
782
783                         if ( self._vScrollBar ) {
784                                 self._vScrollBar.removeClass( vclass );
785                         } else {
786                                 self._hScrollBar.removeClass( vclass );
787                         }
788                 },
789
790                 _showScrollBars : function () {
791                         var self = this,
792                                 vclass = "ui-scrollbar-visible";
793
794                         if ( self.options.rotation ) {
795                                 return ;
796                         }
797
798                         if ( self._vScrollBar ) {
799                                 self._vScrollBar.addClass( vclass );
800                         } else {
801                                 self._hScrollBar.addClass( vclass );
802                         }
803                 },
804
805                 //----------------------------------------------------//
806                 //              scroll process          //
807                 //----------------------------------------------------//
808                 centerTo: function ( selector ) {
809                         var self = this,
810                                 i,
811                                 newX = 0,
812                                 newY = 0;
813
814                         if ( !self.options.rotation ) {
815                                 return;
816                         }
817
818                         for ( i = 0; i < self._$rows.length; i++ ) {
819                                 if ( $( self._$rows[i]).hasClass( selector ) ) {
820                                         if ( self._direction ) {
821                                                 newX = -( i * self._cellSize - self._clipSize / 2 + self._cellSize * 2 );
822                                         } else {
823                                                 newY = -( i * self._cellSize - self._clipSize / 2 + self._cellSize * 2 );
824                                         }
825                                         self.scrollTo( newX, newY );
826                                         return;
827                                 }
828                         }
829                 },
830
831                 scrollTo: function ( x, y, duration ) {
832                         var self = this;
833                         if ( self._direction ) {
834                                 self._sx = self._reservedPos;
835                                 self._reservedPos = x;
836                         } else {
837                                 self._sy = self._reservedPos;
838                                 self._reservedPos = y;
839                         }
840                         self._scrollView.scrollTo.apply( this, [ x, y, duration ] );
841                 },
842
843                 getScrollPosition: function () {
844                         if ( this.direction ) {
845                                 return { x: -this._ry, y: 0 };
846                         }
847                         return { x: 0, y: -this._ry };
848                 },
849
850                 _setScrollPosition: function ( x, y ) {
851                         var self = this,
852                                 sy = self._scalableSize,
853                                 distance = self._direction ? x : y,
854                                 dy = distance - sy,
855                                 di = parseInt( dy / self._cellSize, 10 ),
856                                 i = 0,
857                                 idx = 0,
858                                 $row = null;
859
860                         if ( self._blockScroll ) {
861                                 return ;
862                         }
863
864                         if ( ! self.options.rotation ) {
865                                 if ( dy > 0 && distance >= -self._cellSize && self._scalableSize >= -self._cellSize ) {
866                                         // top
867                                         self._stopMScroll();
868                                         self._scalableSize = -self._cellSize;
869                                         self._setElementTransform( -self._cellSize );
870                                         return;
871                                 }
872                                 if ( (dy < 0 && self._scalableSize <= -(self._maxSize + self._cellSize) )) {
873                                         // bottom
874                                         self._stopMScroll();
875                                         self._scalableSize = -(self._maxSize + self._cellSize);
876                                         self._setElementTransform( self._modifyViewPos );
877                                         return;
878                                 }
879                         }
880
881                         if ( di > 0 ) { // scroll up
882                                 for ( i = 0; i < di; i++ ) {
883                                         idx = -parseInt( ( sy / self._cellSize ) + i + 3, 10 );
884                                         $row = self._$view.children( ).last( ).detach( );
885                                         self._replaceRow( $row, circularNum( idx, self._totalRowCnt ) );
886                                         self._$view.prepend( $row );
887                                         self._setScrollBarPosition(-1);
888                                 }
889                         } else if ( di < 0 ) { // scroll down
890                                 for ( i = 0; i > di; i-- ) {
891                                         idx = self._rowsPerView - parseInt( ( sy / self._cellSize ) + i, 10 );
892                                         $row = self._$view.children().first().detach();
893                                         self._replaceRow($row, circularNum( idx, self._totalRowCnt ) );
894                                         self._$view.append( $row );
895                                         self._setScrollBarPosition(1);
896                                 }
897                         }
898                         self._scalableSize += di * self._cellSize;
899                         self._setElementTransform( distance - self._scalableSize - self._cellSize );
900                 },
901
902                 _setElementTransform : function ( value ) {
903                         var self = this,
904                                 x = 0,
905                                 y = 0;
906
907                         if ( self._direction ) {
908                                 x = value + "px";
909                         } else {
910                                 y = value + "px";
911                         }
912                         self._setStyleTransform(self._$view, x, y );
913                 },
914
915                 //----------------------------------------------------//
916                 //              Event handler           //
917                 //----------------------------------------------------//
918                 _handleMomentumScroll: function () {
919                         var self = this,
920                                 opts = self.options,
921                                 keepGoing = false,
922                                 v = this._$view,
923                                 x = 0,
924                                 y = 0,
925                                 t = self._tracker;
926
927                         if ( t ) {
928                                 t.update();
929                                 if ( self._direction ) {
930                                         x = t.getPosition();
931                                 } else {
932                                         y = t.getPosition();
933                                 }
934                                 keepGoing = !t.done();
935                         }
936
937                         self._setScrollPosition( x, y );
938                         if ( !opts.rotation ) {
939                                 keepGoing = !t.done();
940                                 self._reservedPos = self._direction ? x : y;
941                                 // bottom
942                                 self._reservedPos = self._reservedPos <= (-(self._maxSize - self._modifyViewPos)) ? ( - ( self._maxSize + self._cellSize) ) : self._reservedPos;
943                                 // top
944                                 self._reservedPos = self._reservedPos > -self._cellSize ? -self._cellSize : self._reservedPos;
945                         } else {
946                                 self._reservedPos = self._direction ? x : y;
947                         }
948                         self._$clip.trigger( self.options.updateEventName, [ { x: x, y: y } ] );
949
950                         if ( keepGoing ) {
951                                 self._timerID = setTimeout( self._timerCB, self._timerInterval );
952                         } else {
953                                 self._stopMScroll();
954                         }
955                 },
956
957                 _startMScroll: function ( speedX, speedY ) {
958                         var self = this;
959                         if ( self._direction  ) {
960                                 self._sx = self._reservedPos;
961                         } else {
962                                 self._sy = self._reservedPos;
963                         }
964                         self._scrollView._startMScroll.apply(self, [speedX, speedY]);
965                 },
966
967                 _stopMScroll: function () {
968                         this._scrollView._stopMScroll.apply(this);
969                 },
970
971                 _enableTracking: function () {
972                         var self = this;
973                         self._$view.bind( self._dragMoveEvt, self._dragMoveCB );
974                         self._$view.bind( self._dragStopEvt, self._dragStopCB );
975                         self._scrollView._enableTracking.apply( self );
976                 },
977
978                 _disableTracking: function () {
979                         var self = this;
980                         self._$view.unbind( self._dragMoveEvt, self._dragMoveCB );
981                         self._$view.unbind( self._dragStopEvt, self._dragStopCB );
982                         self._scrollView._disableTracking.apply( self );
983                 },
984
985                 _handleDragStart: function ( e, ex, ey ) {
986                         var self = this;
987                         self._scrollView._handleDragStart.apply( this, [ e, ex, ey ] );
988                         self._eventPos = self._direction ? ex : ey;
989                         self._nextPos = self._reservedPos;
990                 },
991
992                 _handleDragMove: function ( e, ex, ey ) {
993                         var self = this,
994                                 dx = ex - self._lastX,
995                                 dy = ey - self._lastY,
996                                 x = 0,
997                                 y = 0;
998
999                         self._lastMove = getCurrentTime();
1000                         self._speedX = dx;
1001                         self._speedY = dy;
1002
1003                         self._didDrag = true;
1004
1005                         self._lastX = ex;
1006                         self._lastY = ey;
1007
1008                         if ( self._direction ) {
1009                                 self._movePos = ex - self._eventPos;
1010                                 x = self._nextPos + self._movePos;
1011                         } else {
1012                                 self._movePos = ey - self._eventPos;
1013                                 y = self._nextPos + self._movePos;
1014                         }
1015                         self._showScrollBars();
1016                         self._setScrollPosition( x, y );
1017                         return false;
1018                 },
1019
1020                 _handleDragStop: function ( e ) {
1021                         var self = this;
1022
1023                         self._reservedPos = self._movePos ? self._nextPos + self._movePos : self._reservedPos;
1024                         self._scrollView._handleDragStop.apply( this, [ e ] );
1025                         return self._didDrag ? false : undefined;
1026                 },
1027
1028                 _addBehaviors: function () {
1029                         var self = this;
1030
1031                         // scroll event handler.
1032                         if ( self.options.eventType === "mouse" ) {
1033                                 self._dragStartEvt = "mousedown";
1034                                 self._dragStartCB = function ( e ) {
1035                                         return self._handleDragStart( e, e.clientX, e.clientY );
1036                                 };
1037
1038                                 self._dragMoveEvt = "mousemove";
1039                                 self._dragMoveCB = function ( e ) {
1040                                         return self._handleDragMove( e, e.clientX, e.clientY );
1041                                 };
1042
1043                                 self._dragStopEvt = "mouseup";
1044                                 self._dragStopCB = function ( e ) {
1045                                         return self._handleDragStop( e, e.clientX, e.clientY );
1046                                 };
1047
1048                                 self._$view.bind( "vclick", function (e) {
1049                                         return !self._didDrag;
1050                                 } );
1051                         } else { //touch
1052                                 self._dragStartEvt = "touchstart";
1053                                 self._dragStartCB = function ( e ) {
1054                                         var t = e.originalEvent.targetTouches[0];
1055                                         return self._handleDragStart(e, t.pageX, t.pageY );
1056                                 };
1057
1058                                 self._dragMoveEvt = "touchmove";
1059                                 self._dragMoveCB = function ( e ) {
1060                                         var t = e.originalEvent.targetTouches[0];
1061                                         return self._handleDragMove(e, t.pageX, t.pageY );
1062                                 };
1063
1064                                 self._dragStopEvt = "touchend";
1065                                 self._dragStopCB = function ( e ) {
1066                                         return self._handleDragStop( e );
1067                                 };
1068                         }
1069                         self._$view.bind( self._dragStartEvt, self._dragStartCB );
1070
1071                         // other events.
1072                         self._$view.delegate(".virtualgrid-item", "click", function (event) {
1073                                 var $selectedItem = $(this);
1074                                 $selectedItem.trigger("select", this);
1075                         });
1076
1077                         $( window ).bind("resize", function ( e ) {
1078                                 var height = 0,
1079                                         $virtualgrid = $(".ui-virtualgrid-view");
1080                                 if ( $virtualgrid.length !== 0 ) {
1081                                         if ( self._direction ) {
1082                                                 height = self._calculateClipHeight();
1083                                                 self._$view.height(height);
1084                                                 self._$clip.height(height);
1085                                         } else {
1086                                                 height = self._calculateClipWidth();
1087                                                 self._$view.width(height);
1088                                                 self._$clip.width(height);
1089                                         }
1090                                         self.resize( );
1091                                 }
1092                         });
1093
1094                         $(document).one("pageshow", function (event) {
1095                                 var $page = $(self.element).parents(".ui-page"),
1096                                         $header = $page.find( ":jqmData(role='header')" ),
1097                                         $footer = $page.find( ":jqmData(role='footer')" ),
1098                                         $content = $page.find( ":jqmData(role='content')" ),
1099                                         footerHeight = $footer ? $footer.height() : 0,
1100                                         headerHeight = $header ? $header.height() : 0;
1101
1102                                 if ( $page && $content ) {
1103                                         $content.height(window.innerHeight - headerHeight - footerHeight).css("overflow", "hidden");
1104                                         $content.addClass("ui-virtualgrid-content");
1105                                 }
1106                         });
1107                 },
1108
1109                 //----------------------------------------------------//
1110                 //              Calculate size about dom element.               //
1111                 //----------------------------------------------------//
1112                 _calculateClipSize : function () {
1113                         var self = this,
1114                                 clipSize = 0;
1115
1116                         if ( self._direction ) {
1117                                 clipSize = self._calculateClipWidth();
1118                         } else {
1119                                 clipSize = self._calculateClipHeight();
1120                         }
1121                         return clipSize;
1122                 },
1123
1124                 _calculateClipWidth : function () {
1125                         var self = this,
1126                                 view = $(self.element),
1127                                 $parent = $(self.element).parent(),
1128                                 paddingValue = 0,
1129                                 clipSize = $(window).width();
1130
1131                         if ( self._inheritedSize.isDefinedWidth ) {
1132                                 return self._inheritedSize.width;
1133                         }
1134
1135                         if ( $parent.hasClass("ui-content") ) {
1136                                 paddingValue = parseInt($parent.css("padding-left"), 10);
1137                                 clipSize = clipSize - ( paddingValue || 0 );
1138                                 paddingValue = parseInt($parent.css("padding-right"), 10);
1139                                 clipSize = clipSize - ( paddingValue || 0);
1140                         } else {
1141                                 clipSize = view.width();
1142                         }
1143                         return clipSize;
1144                 },
1145
1146                 _calculateClipHeight : function () {
1147                         var self = this,
1148                                 view = $(self.element),
1149                                 $parent = $(self.element).parent(),
1150                                 header = null,
1151                                 footer = null,
1152                                 paddingValue = 0,
1153                                 clipSize = $(window).height();
1154
1155                         if ( self._inheritedSize.isDefinedHeight ) {
1156                                 return self._inheritedSize.height;
1157                         }
1158
1159                         if ( $parent.hasClass("ui-content") ) {
1160                                 paddingValue = parseInt($parent.css("padding-top"), 10);
1161                                 clipSize = clipSize - ( paddingValue || 0 );
1162                                 paddingValue = parseInt($parent.css("padding-bottom"), 10);
1163                                 clipSize = clipSize - ( paddingValue || 0);
1164                                 header = $parent.siblings(".ui-header");
1165                                 footer = $parent.siblings(".ui-footer");
1166
1167                                 if ( header ) {
1168                                         if ( header.outerHeight(true) === null ) {
1169                                                 clipSize = clipSize - ( $(".ui-header").outerHeight() || 0 );
1170                                         } else {
1171                                                 clipSize = clipSize - header.outerHeight(true);
1172                                         }
1173                                 }
1174                                 if ( footer ) {
1175                                         clipSize = clipSize - footer.outerHeight(true);
1176                                 }
1177                         } else {
1178                                 clipSize = view.height();
1179                         }
1180                         return clipSize;
1181                 },
1182
1183                 _calculateColumnSize : function () {
1184                         var self = this,
1185                                 $tempBlock,
1186                                 $cell;
1187
1188                         $tempBlock = self._makeRows( 1 );
1189                         self._$view.append( $tempBlock.children().first() );
1190                         if ( self._direction ) {
1191                                 // x-axis
1192                                 self._viewSize = self._$view.width();
1193                                 $cell = self._$view.children().first().children().first();
1194                                 self._cellSize = $cell.outerWidth(true);
1195                                 self._cellOtherSize = $cell.outerHeight(true);
1196                         } else {
1197                                 // y-axis
1198                                 self._viewSize = self._$view.height();
1199                                 $cell = self._$view.children().first().children().first();
1200                                 self._cellSize = $cell.outerHeight(true);
1201                                 self._cellOtherSize = $cell.outerWidth(true);
1202                         }
1203                         $tempBlock.remove();
1204                         self._$view.children().remove();
1205                 },
1206
1207                 _calculateColumnCount : function ( ) {
1208                         var self = this,
1209                                 $view = $(self.element),
1210                                 viewSize = self._direction ? $view.innerHeight() : $view.innerWidth(),
1211                                 itemCount = 0 ;
1212
1213                         if ( self._direction ) {
1214                                 viewSize = viewSize - ( parseInt( $view.css("padding-top"), 10 ) + parseInt( $view.css("padding-bottom"), 10 ) );
1215                         } else {
1216                                 viewSize = viewSize - ( parseInt( $view.css("padding-left"), 10 ) + parseInt( $view.css("padding-right"), 10 ) );
1217                         }
1218
1219                         itemCount = parseInt( (viewSize / self._cellOtherSize), 10);
1220                         return itemCount > 0 ? itemCount : 1 ;
1221                 },
1222
1223                 // Read the position of clip form property ('webkit-transform').
1224                 // @return : number - position of clip.
1225                 _getClipPosition : function () {
1226                         var self = this,
1227                                 matrix = null,
1228                                 contents = null,
1229                                 result = -self._cellSize,
1230                                 $scrollview = self._$view.closest(".ui-scrollview-view");
1231
1232                         if ( $scrollview ) {
1233                                 matrix = $scrollview.css("-webkit-transform");
1234                                 contents = matrix.substr( 7 );
1235                                 contents = contents.substr( 0, contents.length - 1 );
1236                                 contents = contents.split( ', ' );
1237                                 result =  Math.abs(contents [5]);
1238                         }
1239                         return result;
1240                 },
1241
1242                 //----------------------------------------------------//
1243                 //              DOM Element handle              //
1244                 //----------------------------------------------------//
1245                 _makeRows : function ( count ) {
1246                         var self = this,
1247                                 opts = self.options,
1248                                 index = 0,
1249                                 $row = null,
1250                                 $wrapper = null;
1251
1252                         $wrapper = $( self._createElement( "div" ) );
1253                         $wrapper.addClass("ui-scrollview-view");
1254                         for ( index = 0; index < count ; index += 1 ) {
1255                                 $row = self._makeRow( self._template, index );
1256                                 if ( self._direction ) {
1257                                         $row.css("top", 0).css("left", ( index * self._cellSize ));
1258                                 }
1259                                 $wrapper.append($row);
1260                         }
1261                         return $wrapper;
1262                 },
1263
1264                 // make a single row block
1265                 _makeRow : function ( myTemplate, rowIndex ) {
1266                         var self = this,
1267                                 opts = self.options,
1268                                 index = rowIndex * self._itemCount,
1269                                 htmlData = null,
1270                                 itemData = null,
1271                                 colIndex = 0,
1272                                 attrName = self._direction ? "top" : "left",
1273                                 blockClassName = self._direction ? "ui-virtualgrid-wrapblock-x" : "ui-virtualgrid-wrapblock-y",
1274                                 blockAttrName = self._direction ? "top" : "left",
1275                                 wrapBlock = $( self._createElement( "div" ) );
1276
1277                         wrapBlock.addClass( blockClassName ).attr("row-index", rowIndex);
1278                         for ( colIndex = 0; colIndex < self._itemCount; colIndex++ ) {
1279                                 htmlData = self._makeHtmlData( myTemplate, index, colIndex);
1280                                 if ( htmlData ) {
1281                                         wrapBlock.append( htmlData );
1282                                 }
1283                                 index += 1;
1284                         }
1285                         return wrapBlock;
1286                 },
1287
1288                 _makeHtmlData : function ( myTemplate, dataIndex, colIndex ) {
1289                         var self = this,
1290                                 htmlData = null,
1291                                 itemData = null,
1292                                 attrName = self._direction ? "top" : "left";
1293
1294                         itemData = self._itemData( dataIndex );
1295                         if ( itemData ) {
1296                                 htmlData = myTemplate.tmpl( itemData );
1297                                 $(htmlData).css(attrName, ( colIndex * self._cellOtherSize )).addClass("virtualgrid-item");
1298                         }
1299                         return htmlData;
1300                 },
1301
1302                 _increaseRow : function ( num ) {
1303                         var self = this,
1304                                 rotation = self.options.rotation,
1305                                 $row = null,
1306                                 headItemIndex = 0,
1307                                 tailItemIndex = 0,
1308                                 itemIndex = 0,
1309                                 size = self._scalableSize,
1310                                 idx = 0;
1311
1312                         headItemIndex = parseInt( $(self._$view.children().first()).attr("row-index"), 10) - 1;
1313                         tailItemIndex = parseInt( $(self._$view.children()[self._rowsPerView]).attr("row-index"), 10) + 1;
1314
1315                         for ( idx = 1 ; idx <= num ; idx++ ) {
1316                                 if ( tailItemIndex + idx  >= self._totalRowCnt ) {
1317                                         $row = self._makeRow( self._template, headItemIndex );
1318                                         self._$view.prepend($row);
1319                                         headItemIndex -= 1;
1320                                 } else {
1321                                         $row = self._makeRow( self._template, tailItemIndex + idx );
1322                                         self._$view.append($row);
1323                                 }
1324                                 if ( self._direction ) {
1325                                         $row.width(self._cellSize);
1326                                 } else {
1327                                         $row.height(self._cellSize);
1328                                 }
1329                         }
1330                 },
1331
1332                 _decreaseRow : function ( num ) {
1333                         var self = this,
1334                                 idx = 0;
1335
1336                         for ( idx = 0 ; idx < num ; idx++ ) {
1337                                 self._$view.children().last().remove();
1338                         }
1339                 },
1340
1341                 _replaceRows : function ( curCnt, prevCnt, maxCnt, clipPosition ) {
1342                         var self = this,
1343                                 $rows = self._$view.children(),
1344                                 prevRowIndex = 0,
1345                                 rowIndex = 0,
1346                                 diffRowCnt = 0,
1347                                 targetCnt = 1,
1348                                 filterCondition = ( self._filterRatio * self._cellSize) + self._cellSize,
1349                                 idx = 0;
1350
1351                         if ( filterCondition < clipPosition ) {
1352                                 targetCnt += 1;
1353                         }
1354
1355                         prevRowIndex = parseInt( $($rows[targetCnt]).attr("row-index"), 10);
1356                         if ( prevRowIndex === 0 ) {
1357                                 // only top.
1358                                 rowIndex = maxCnt - targetCnt;
1359                         } else {
1360                                 rowIndex = Math.round( (prevRowIndex * prevCnt) / curCnt );
1361                                 if ( rowIndex + self._rowsPerView >= maxCnt ) {
1362                                         // only bottom.
1363                                         rowIndex = maxCnt - self._rowsPerView;
1364                                 }
1365                                 diffRowCnt = prevRowIndex - rowIndex;
1366                                 rowIndex -= targetCnt;
1367                         }
1368
1369                         for ( idx = 0 ; idx < $rows.length ; idx += 1 ) {
1370                                 self._replaceRow($rows[idx], circularNum( rowIndex, self._totalRowCnt ));
1371                                 rowIndex++;
1372                         }
1373                         return -diffRowCnt;
1374                 },
1375
1376                 _replaceRow : function ( block, index ) {
1377                         var self = this,
1378                                 opts = self.options,
1379                                 $columns = null,
1380                                 $column = null,
1381                                 $block = block.attr ? block : $( block ),
1382                                 data = null,
1383                                 htmlData = null,
1384                                 myTemplate = null,
1385                                 idx = 0,
1386                                 dataIdx = 0,
1387                                 tempBlocks = null;
1388
1389                         $columns = $block.attr("row-index", index).children();
1390                         if ( $columns.length !== self._itemCount ) {
1391                                 $block.children().remove();
1392                                 tempBlocks = $(self._makeRow( self._template, index ));
1393                                 $block.append(tempBlocks.children());
1394                                 tempBlocks.remove();
1395                                 return ;
1396                         }
1397
1398                         dataIdx = index * self._itemCount;
1399                         for ( idx = 0; idx < self._itemCount ; idx += 1 ) {
1400                                 $column = $columns.eq(idx);
1401                                 data = self._itemData(dataIdx);
1402                                 if ( $column && data ) {
1403                                         myTemplate = self._template;
1404                                         htmlData = myTemplate.tmpl( data );
1405                                         self._replace( $column, htmlData, false );
1406                                         htmlData.remove();      // Clear temporary htmlData to free cache
1407                                         dataIdx ++;
1408                                 } else if ($column && !data ) {
1409                                         $column.remove();
1410                                 }
1411                         }
1412                 },
1413
1414                 _createElement : function ( tag ) {
1415                         var element = document.createElement( tag );
1416
1417                         this._fragment.appendChild( element );
1418                         return element;
1419                 },
1420
1421                 /* Text & image src replace function */
1422                 // @param oldItem   : prev HtmlDivElement
1423                 // @param newItem   : new HtmlDivElement for replace
1424                 // @param key       :
1425                 _replace : function ( oldItem, newItem, key ) {
1426                         var NODETYPE = { ELEMENT_NODE : 1, TEXT_NODE : 3 };
1427
1428                         $( oldItem ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).each( function ( index ) {
1429                                 var oldObj = $( this ),
1430                                         newText = $( newItem ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).eq( index ).text();
1431
1432                                 $( oldObj ).contents().filter( function () {
1433                                         return ( this.nodeType == NODETYPE.TEXT_NODE );
1434                                 }).get( 0 ).data = newText;
1435                         });
1436
1437                         $( oldItem ).find( "img" ).each( function ( imgIndex ) {
1438                                 var oldObj = $( this ),
1439                                         newImg = $( newItem ).find( "img" ).eq( imgIndex ).attr( "src" );
1440
1441                                 $( oldObj ).attr( "src", newImg );
1442                         });
1443                         $( oldItem).removeData();
1444                         if ( key ) {
1445                                 $( oldItem ).data( key, $( newItem ).data( key ) );
1446                         }
1447                 }
1448         } );
1449
1450         $( document ).bind( "pagecreate create", function ( e ) {
1451                 $(":jqmData(role='virtualgrid')").virtualgrid();
1452         } );
1453 } (jQuery, window, document) );