1 /* ***************************************************************************
2 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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 * ***************************************************************************
23 * Author: Kangsik Kim <kangsik81.kim@samsung.com>
24 * Youmin Ha <youmin.ha@samsung.com>
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.
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-itemcount : Number of column elements. (Default : 1)
44 * User can select a numeric type or 'auto'.
45 * If value of attribute is 'auto', Number of column is dependent on screen size.
46 * If value of attribute is numeric type, number of column is always fixed.
47 * data-direction : This option define the direction of the scroll.
48 * You must choose one of the 'x' and 'y' (Default : y)
49 * data-rotation : This option defines whether or not the circulation of the data.
50 * If option is 'true' and scroll is reached the last data,
51 * Widget will present the first data on the screen.
52 * If option is ‘false’, Widget will operate like a scrollview.
54 * ID : <DIV> element that has "data-role=virtualgrid" must have ID attribute.
59 * itemData: function ( idx ) { return json_obj; },
60 * numItemData: number or function () { return number; },
61 * cacheItemData: function ( minIdx, maxIdx ) {}
63 * : Create VirtualGrid widget. At this moment, _create method is called.
64 * args : A collection of options
65 * itemData: A function that returns JSON object for given index. Mandatory.
66 * numItemData: Total number of itemData. Mandatory.
67 * cacheItemData: Virtuallist will ask itemData between minIdx and maxIdx.
68 * Developers can implement this function for preparing data.
72 * : Find a DOM Element with the given class name.
73 * This element will be centered on the screen.
74 * Serveral elements were found, the first element is displayed.
77 * scrollstart : : This event triggers when a user begin to move the scroll on VirtualGrid.
78 * scrollupdate : : This event triggers while a user moves the scroll on VirtualGrid.
79 * scrollstop : This event triggers when a user stop the scroll on VirtualGrid.
80 * select : This event triggers when a cell is selected.
84 * <script id="tizen-demo-namecard" type="text/x-jquery-tmpl">
85 * <div class="ui-demo-namecard">
86 * <div class="ui-demo-namecard-pic">
87 * <img class="ui-demo-namecard-pic-img" src="${TEAM_LOGO}" />
89 * <div class="ui-demo-namecard-contents">
90 * <span class="name ui-li-text-main">${NAME}</span>
91 * <span class="active ui-li-text-sub">${ACTIVE}</span>
92 * <span class="from ui-li-text-sub">${FROM}</span>
96 * <div id="virtualgrid-demo" data-role="virtualgrid" data-itemcount="3" data-template="tizen-demo-namecard" >
101 // most of following codes are derived from jquery.mobile.scrollview.js
105 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.
107 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.
109 To add a virtual grid widget to the application, use the following code:
111 <script id="tizen-demo-namecard" type="text/x-jquery-tmpl">
112 <div class="ui-demo-namecard">
113 <div class="ui-demo-namecard-pic">
114 <img class="ui-demo-namecard-pic-img" src="${TEAM_LOGO}" />
116 <div class="ui-demo-namecard-contents">
117 <span class="name ui-li-text-main">${NAME}</span>
121 <div id="virtualgrid-demo" data-role="virtualgrid" data-itemcount="1" data-template="tizen-demo-namecard">
125 @property {String} data-template
126 Specifies the jQuery.template element ID.
127 The jQuery.template must be defined. The template style can use rem units to support scalability.
130 @property {String} data-direction
131 Defines the scroll direction. The direction options are x (horizontal) and y (vertical).
132 The default value is y.
135 @property {Boolean} data-rotation
136 Defines whether the data elements are displayed from the beginning of the list again once the end of file is reached.
137 The default value is false.
140 @property {String} data-itemcount
141 Sets the number of column elements displayed in one row. The value can be a number (when the column number is fixed and displayed as the input value) or auto (when the column number varies and the columns are arranged according to the screen size).
142 The default value is 1.
146 The scrollstart event is fired when the user starts scrolling through the grid:
148 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
150 $(".selector").virtualgrid
152 scrollstart: function(event, ui)
154 // Handle the scrollstart event
158 $(".selector").bind("scrollstart", function(event, ui)
160 // Handle the scrollstart event
165 The scrollupdate event is fired when the user moves the scroll bar in the grid:
167 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
169 $(".selector").virtualgrid
171 scrollupdate: function(event, ui)
173 // Handle the scrollupdate event
177 $(".selector").bind("scrollupdate", function(event, ui)
179 // Handle the scrollupdate event
184 The scrollstop event is fired when the user stops scrolling:
186 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
188 $(".selector").virtualgrid
190 scrollstop: function(event, ui)
192 // Handle the scrollstop event
196 $(".selector").bind("scrollstop", function(event, ui)
198 // Handle the scrollstop event
203 The select event is fired when a virtual grid cell is selected:
205 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
207 $(".selector").virtualgrid
209 select: function(event, ui)
211 // Handle the select event
215 $(".selector").bind("select", function(event, ui)
217 // Handle the select event
222 @param {function} itemData(index)
223 @param {Number} numItemData
224 @param {function} cacheItemData(minIndex, maxIndex)
225 The create method is used to call the jQuery _create method. In the method parameters:
227 function itemData(index) returns the JSON object matched with the given index. The index value is between 0 and numItemData-1.<br/>
228 number numItemData or function numItemData() defines or returns a static number of items.<br/>
229 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/>
231 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
232 function itemData(idx)
236 function cacheItemData(minIdx, maxIdx)
238 // Prepare JSON data between minIdx and maxIdx
240 var numItemData = DATA.length;
241 $(".selector").virtualgrid("create",
243 itemData, numItemData, cacheItemData
248 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.
250 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
251 $(".selector").virtualgrid("centerTo", "selector");
255 The resize method is used to rearrange the DOM elements to fit a new screen size when the screen is resized:
257 <div data-role="virtualgrid" data-scroll="y" data-template="tizen-demo-namecard"></div>
258 ".selector").virtualgrid("resize");
263 ( function ($, window, document, undefined) {
265 function circularNum (num, total) {
273 function MomentumTracker (options) {
274 this.options = $.extend({}, options);
275 this.easing = "easeOutQuad";
284 function getCurrentTime () {
288 $.extend (MomentumTracker.prototype, {
289 start : function (pos, speed, duration) {
290 this.state = (speed !== 0 ) ? tstates.scrolling : tstates.done;
293 this.duration = duration;
298 this.startTime = getCurrentTime();
301 reset : function () {
302 this.state = tstates.done;
308 update : function () {
309 var state = this.state, duration, elapsed, dx, x;
311 if (state == tstates.done) {
314 duration = this.duration;
315 elapsed = getCurrentTime () - this.startTime;
316 elapsed = elapsed > duration ? duration : elapsed;
317 dx = this.speed * (1 - $.easing[this.easing] (elapsed / duration, elapsed, 0, 1, duration) );
321 if (elapsed >= duration) {
322 this.state = tstates.done;
328 return this.state == tstates.done;
331 getPosition : function () {
336 jQuery.widget ("mobile.virtualgrid", jQuery.mobile.widget, {
338 // virtualgrid option
345 create : function () {
346 this._create.apply( this, arguments );
349 _create : function ( args ) {
358 _cellSize : undefined,
359 _currentItemCount : 0,
370 _itemData : function ( idx ) { return null; },
372 _cacheItemData : function ( minIdx, maxIdx ) { },
379 // axis - ( true : x , false : y )
397 $dom = $(self.element),
402 // If mandatory options are not given, Do nothing.
407 if ( !self._loadData(args) ) {
411 // set a scroll direction.
412 self._direction = opts.direction === 'x' ? true : false;
414 // itemcount is assigned 'auto'
415 if ( typeof opts.itemcount === "string" && opts.itemcount.toUpperCase() == "AUTO" ) {
420 self._$clip = $(self.element).addClass("ui-scrollview-clip").addClass("ui-virtualgrid-view");
421 $item = $(document.createElement("div")).addClass("ui-scrollview-view");
422 self._clipSize = self._calculateClipSize();
423 self._$clip.append($item);
425 self._$clip.css("overflow", "hidden");
426 self._$view.css("overflow", "hidden");
428 // inherit from scrollview widget.
429 self._scrollView = $.tizen.scrollview.prototype;
430 self._initScrollView();
433 self._createTracker();
434 self._makePositioned(self._$clip);
435 self._timerInterval = 1000 / self.options.fps;
438 self._timerCB = function () {
439 self._handleMomentumScroll();
441 $dom.closest(".ui-content").addClass("ui-virtualgrid-content").css("overflow", "hidden");
443 // add event handler.
444 self._addBehaviors();
446 self._currentItemCount = 0;
447 self._createScrollBar();
451 // The argument is checked for compliance with the specified format.
452 // @param args : Object
454 _loadData : function ( args ) {
457 if ( args.itemData && typeof args.itemData == 'function' ) {
458 self._itemData = args.itemData;
462 if ( args.numItemData ) {
463 if ( typeof args.numItemData == 'function' ) {
464 self._numItemData = args.numItemData( );
465 } else if ( typeof args.numItemData == 'number' ) {
466 self._numItemData = args.numItemData;
476 // Make up the first screen.
477 _initLayout: function () {
483 for ( i = -1; i < self._rowsPerView + 1; i += 1 ) {
484 $row = self._$rows[ circularNum( i, self._$rows.length ) ];
485 self._$view.append( $row );
487 self._setElementTransform( -self._cellSize );
489 self._replaceRow(self._$view.children().first(), self._totalRowCnt - 1);
490 if ( opts.rotation && self._rowsPerView >= self._totalRowCnt ) {
491 self._replaceRow(self._$view.children().last(), 0);
496 _setViewSize : function () {
501 if ( self._direction ) {
502 width = self._cellSize * ( self._rowsPerView + 2 );
503 width = parseInt(width, 10) + 1;
504 self._$view.width( width );
505 self._viewSize = self._$view.width();
507 self._$view.height( self._cellSize * ( self._rowsPerView + 2 ) );
508 self._$clip.height( self._clipSize );
509 self._viewSize = self._$view.height();
513 _getViewHeight : function () {
515 return self._$view.height();
518 refresh : function () {
524 self._template = $( "#" + opts.template );
525 if ( !self._template ) {
529 width = self._calculateClipWidth();
530 height = self._calculateClipHeight();
531 self._$view.width(width).height(height);
532 self._$clip.width(width).height(height);
534 self._clipSize = self._calculateClipSize();
535 self._calculateColumnSize();
536 self._initPageProperty();
537 self._setScrollBarSize( );
540 _initPageProperty : function () {
546 attributeName = self._direction ? "width" : "height";
548 if ( self._isAuto ) {
549 columnCount = self._calculateColumnCount();
551 columnCount = self.options.itemcount;
553 totalRowCnt = parseInt(self._numItemData / columnCount , 10 );
554 self._totalRowCnt = self._numItemData % columnCount === 0 ? totalRowCnt : totalRowCnt + 1;
555 self._itemCount = columnCount;
557 if ( self._cellSize <= 0) {
561 rowsPerView = self._clipSize / self._cellSize;
562 rowsPerView = Math.ceil( rowsPerView );
563 self._rowsPerView = parseInt( rowsPerView, 10);
565 $child = self._makeRows( rowsPerView + 2 );
566 $(self._$view).append($child.children());
567 self._$view.children().css(attributeName, self._cellSize + "px");
568 self._$rows = self._$view.children().detach();
570 self._reservedPos = -self._cellSize;
571 self._scalableSize = -self._cellSize;
575 self._blockScroll = self._rowsPerView > self._totalRowCnt;
576 self._maxSize = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
577 self._maxViewSize = ( self._rowsPerView ) * self._cellSize;
578 self._modifyViewPos = -self._cellSize;
579 if ( self._clipSize < self._maxViewSize ) {
580 self._modifyViewPos = (-self._cellSize) + ( self._clipSize - self._maxViewSize );
584 resize : function ( ) {
594 itemCount = self._calculateColumnCount();
595 if ( self._isAuto && itemCount != self._itemCount ) {
596 totalRowCnt = parseInt(self._numItemData / itemCount , 10 );
597 self._totalRowCnt = self._numItemData % itemCount === 0 ? totalRowCnt : totalRowCnt + 1;
598 prevcnt = self._itemCount;
599 self._itemCount = itemCount;
600 clipPosition = self._getClipPosition();
603 diffRowCnt = self._replaceRows(itemCount, prevcnt, self._totalRowCnt, clipPosition);
604 self._maxSize = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
605 self._scalableSize += (-diffRowCnt) * self._cellSize;
606 self._reservedPos += (-diffRowCnt) * self._cellSize;
607 self._setScrollBarSize();
608 self._setScrollBarPosition(diffRowCnt);
613 clipSize = self._calculateClipSize();
614 if ( clipSize !== self._clipSize ) {
615 rowsPerView = clipSize / self._cellSize;
616 rowsPerView = parseInt( Math.ceil( rowsPerView ), 10 );
618 if ( rowsPerView > self._rowsPerView ) {
620 self._increaseRow( rowsPerView - self._rowsPerView );
621 } else if ( rowsPerView < self._rowsPerView ) {
623 self._decreaseRow( self._rowsPerView - rowsPerView );
625 self._rowsPerView = rowsPerView;
626 self._clipSize = clipSize;
627 self._blockScroll = self._rowsPerView > self._totalRowCnt;
628 self._maxSize = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
629 self._maxViewSize = ( self._rowsPerView ) * self._cellSize;
630 if ( self._clipSize < self._maxViewSize ) {
631 self._modifyViewPos = (-self._cellSize) + ( self._clipSize - self._maxViewSize );
633 if ( self._direction ) {
634 self._$clip.width(self._clipSize);
636 self._$clip.height(self._clipSize);
638 self._setScrollBarSize();
639 self._setScrollBarPosition(0);
644 _initScrollView : function () {
646 $.extend(self.options, self._scrollView.options);
647 self.options.moveThreshold = 10;
648 self.options.showScrollBars = false;
649 self._getScrollHierarchy = self._scrollView._getScrollHierarchy;
650 self._makePositioned = self._scrollView._makePositioned;
651 self._set_scrollbar_size = self._scrollView._set_scrollbar_size;
652 self._setStyleTransform = self._scrollView._setElementTransform;
653 self._hideOverflowIndicator = self._scrollView._hideOverflowIndicator;
654 self._showOverflowIndicator = self._scrollView._showOverflowIndicator;
655 self._setGestureScroll = self._scrollView._setGestureScroll;
658 _createTracker : function () {
661 self._tracker = new MomentumTracker(self.options);
662 if ( self._direction ) {
663 self._hTracker = self._tracker;
664 self._$clip.width(self._clipSize);
666 self._vTracker = self._tracker;
667 self._$clip.height(self._clipSize);
671 //----------------------------------------------------//
673 //----------------------------------------------------//
674 _createScrollBar : function () {
676 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
677 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
679 if ( self.options.rotation ) {
683 if ( self._direction ) {
684 self._$clip.append( prefix + "x" + suffix );
685 self._hScrollBar = $(self._$clip.children(".ui-scrollbar-x"));
686 self._hScrollBar.find(".ui-scrollbar-thumb").addClass("ui-scrollbar-thumb-x");
688 self._$clip.append( prefix + "y" + suffix );
689 self._vScrollBar = $(self._$clip.children(".ui-scrollbar-y"));
690 self._vScrollBar.find(".ui-scrollbar-thumb").addClass("ui-scrollbar-thumb-y");
694 _setScrollBarSize: function () {
702 if ( self.options.rotation ) {
706 scrollBarSize = parseInt( self._maxViewSize / self._clipSize , 10);
707 if ( self._direction ) {
708 $scrollBar = self._hScrollBar.find(".ui-scrollbar-thumb");
710 currentSize = $scrollBar.width();
711 className = "ui-scrollbar-thumb-x";
712 self._hScrollBar.css("width", self._clipSize);
714 $scrollBar = self._vScrollBar.find(".ui-scrollbar-thumb");
716 className = "ui-scrollbar-thumb-y";
717 currentSize = $scrollBar.height();
718 self._vScrollBar.css("height", self._clipSize);
721 if ( scrollBarSize > currentSize ) {
722 $scrollBar.removeClass(className);
723 $scrollBar.css(attrName, scrollBarSize);
725 scrollBarSize = currentSize;
728 self._itemScrollSize = parseFloat( ( self._clipSize - scrollBarSize ) / ( self._totalRowCnt - self._rowsPerView ) );
729 self._itemScrollSize = Math.round(self._itemScrollSize * 100) / 100;
732 _setScrollBarPosition : function ( di, duration ) {
738 if ( self.options.rotation ) {
742 self._currentItemCount = self._currentItemCount + di;
743 if ( self._vScrollBar ) {
744 $sbt = self._vScrollBar .find(".ui-scrollbar-thumb");
745 y = ( self._currentItemCount * self._itemScrollSize ) + "px";
747 $sbt = self._hScrollBar .find(".ui-scrollbar-thumb");
748 x = ( self._currentItemCount * self._itemScrollSize ) + "px";
750 self._setStyleTransform( $sbt, x, y, duration );
753 _hideScrollBars : function () {
755 vclass = "ui-scrollbar-visible";
757 if ( self.options.rotation ) {
761 if ( self._vScrollBar ) {
762 self._vScrollBar.removeClass( vclass );
764 self._hScrollBar.removeClass( vclass );
768 _showScrollBars : function () {
770 vclass = "ui-scrollbar-visible";
772 if ( self.options.rotation ) {
776 if ( self._vScrollBar ) {
777 self._vScrollBar.addClass( vclass );
779 self._hScrollBar.addClass( vclass );
783 //----------------------------------------------------//
785 //----------------------------------------------------//
786 centerTo: function ( selector ) {
792 if ( !self.options.rotation ) {
796 for ( i = 0; i < self._$rows.length; i++ ) {
797 if ( $( self._$rows[i]).hasClass( selector ) ) {
798 if ( self._direction ) {
799 newX = -( i * self._cellSize - self._clipSize / 2 + self._cellSize * 2 );
801 newY = -( i * self._cellSize - self._clipSize / 2 + self._cellSize * 2 );
803 self.scrollTo( newX, newY );
809 scrollTo: function ( x, y, duration ) {
811 if ( self._direction ) {
812 self._sx = self._reservedPos;
813 self._reservedPos = x;
815 self._sy = self._reservedPos;
816 self._reservedPos = y;
818 self._scrollView.scrollTo.apply( this, [ x, y, duration ] );
821 getScrollPosition: function () {
822 if ( this.direction ) {
823 return { x: -this._ry, y: 0 };
825 return { x: 0, y: -this._ry };
828 _setScrollPosition: function ( x, y ) {
830 sy = self._scalableSize,
831 distance = self._direction ? x : y,
833 di = parseInt( dy / self._cellSize, 10 ),
838 if ( self._blockScroll ) {
842 if ( ! self.options.rotation ) {
843 if ( dy > 0 && distance >= -self._cellSize && self._scalableSize >= -self._cellSize ) {
846 self._scalableSize = -self._cellSize;
847 self._setElementTransform( -self._cellSize );
850 if ( (dy < 0 && self._scalableSize <= -(self._maxSize + self._cellSize) )) {
853 self._scalableSize = -(self._maxSize + self._cellSize);
854 self._setElementTransform( self._modifyViewPos );
859 if ( di > 0 ) { // scroll up
860 for ( i = 0; i < di; i++ ) {
861 idx = -parseInt( ( sy / self._cellSize ) + i + 3, 10 );
862 $row = self._$view.children( ).last( ).detach( );
863 self._replaceRow( $row, circularNum( idx, self._totalRowCnt ) );
864 self._$view.prepend( $row );
865 self._setScrollBarPosition(-1);
867 } else if ( di < 0 ) { // scroll down
868 for ( i = 0; i > di; i-- ) {
869 idx = self._rowsPerView - parseInt( ( sy / self._cellSize ) + i, 10 );
870 $row = self._$view.children().first().detach();
871 self._replaceRow($row, circularNum( idx, self._totalRowCnt ) );
872 self._$view.append( $row );
873 self._setScrollBarPosition(1);
876 self._scalableSize += di * self._cellSize;
877 self._setElementTransform( distance - self._scalableSize - self._cellSize );
880 _setElementTransform : function ( value ) {
885 if ( self._direction ) {
890 self._setStyleTransform(self._$view, x, y );
893 //----------------------------------------------------//
895 //----------------------------------------------------//
896 _handleMomentumScroll: function () {
907 if ( self._direction ) {
912 keepGoing = !t.done();
915 self._setScrollPosition( x, y );
916 if ( !opts.rotation ) {
917 keepGoing = !t.done();
918 self._reservedPos = self._direction ? x : y;
920 self._reservedPos = self._reservedPos <= (-(self._maxSize - self._modifyViewPos)) ? ( - ( self._maxSize + self._cellSize) ) : self._reservedPos;
922 self._reservedPos = self._reservedPos > -self._cellSize ? -self._cellSize : self._reservedPos;
924 self._reservedPos = self._direction ? x : y;
926 self._$clip.trigger( self.options.updateEventName, [ { x: x, y: y } ] );
929 self._timerID = setTimeout( self._timerCB, self._timerInterval );
935 _startMScroll: function ( speedX, speedY ) {
937 if ( self._direction ) {
938 self._sx = self._reservedPos;
940 self._sy = self._reservedPos;
942 self._scrollView._startMScroll.apply(self, [speedX, speedY]);
945 _stopMScroll: function () {
946 this._scrollView._stopMScroll.apply(this);
949 _enableTracking: function () {
950 $(document).bind( this._dragMoveEvt, this._dragMoveCB );
951 $(document).bind( this._dragStopEvt, this._dragStopCB );
954 _disableTracking: function () {
955 $(document).unbind( this._dragMoveEvt, this._dragMoveCB );
956 $(document).unbind( this._dragStopEvt, this._dragStopCB );
959 _handleDragStart: function ( e, ex, ey ) {
961 self._scrollView._handleDragStart.apply( this, [ e, ex, ey ] );
962 self._eventPos = self._direction ? ex : ey;
963 self._nextPos = self._reservedPos;
966 _handleDragMove: function ( e, ex, ey ) {
968 dx = ex - self._lastX,
969 dy = ey - self._lastY,
973 self._lastMove = getCurrentTime();
977 self._didDrag = true;
982 if ( self._direction ) {
983 self._movePos = ex - self._eventPos;
984 x = self._nextPos + self._movePos;
986 self._movePos = ey - self._eventPos;
987 y = self._nextPos + self._movePos;
989 self._showScrollBars();
990 self._setScrollPosition( x, y );
994 _handleDragStop: function ( e ) {
997 self._reservedPos = self._movePos ? self._nextPos + self._movePos : self._reservedPos;
998 self._scrollView._handleDragStop.apply( this, [ e ] );
999 return self._didDrag ? false : undefined;
1002 _addBehaviors: function () {
1005 // scroll event handler.
1006 if ( self.options.eventType === "mouse" ) {
1007 self._dragStartEvt = "mousedown";
1008 self._dragStartCB = function ( e ) {
1009 return self._handleDragStart( e, e.clientX, e.clientY );
1012 self._dragMoveEvt = "mousemove";
1013 self._dragMoveCB = function ( e ) {
1014 return self._handleDragMove( e, e.clientX, e.clientY );
1017 self._dragStopEvt = "mouseup";
1018 self._dragStopCB = function ( e ) {
1019 return self._handleDragStop( e, e.clientX, e.clientY );
1022 self._$view.bind( "vclick", function (e) {
1023 return !self._didDrag;
1026 self._dragStartEvt = "touchstart";
1027 self._dragStartCB = function ( e ) {
1028 var t = e.originalEvent.targetTouches[0];
1029 return self._handleDragStart(e, t.pageX, t.pageY );
1032 self._dragMoveEvt = "touchmove";
1033 self._dragMoveCB = function ( e ) {
1034 var t = e.originalEvent.targetTouches[0];
1035 return self._handleDragMove(e, t.pageX, t.pageY );
1038 self._dragStopEvt = "touchend";
1039 self._dragStopCB = function ( e ) {
1040 return self._handleDragStop( e );
1043 self._$view.bind( self._dragStartEvt, self._dragStartCB );
1046 self._$view.delegate(".virtualgrid-item", "click", function (event) {
1047 var $selectedItem = $(this);
1048 $selectedItem.trigger("select", this);
1051 $( window ).bind("resize", function ( e ) {
1053 $virtualgrid = $(".ui-virtualgrid-view");
1054 if ( $virtualgrid.length !== 0 ) {
1055 if ( self._direction ) {
1056 height = self._calculateClipHeight();
1057 self._$view.height(height);
1058 self._$clip.height(height);
1060 height = self._calculateClipWidth();
1061 self._$view.width(height);
1062 self._$clip.width(height);
1068 $(document).one("pageshow", function (event) {
1069 var $page = $(self.element).parents(".ui-page"),
1070 $header = $page.find( ":jqmData(role='header')" ),
1071 $footer = $page.find( ":jqmData(role='footer')" ),
1072 $content = $page.find( ":jqmData(role='content')" ),
1073 footerHeight = $footer ? $footer.height() : 0,
1074 headerHeight = $header ? $header.height() : 0;
1076 if ( $page && $content ) {
1077 $content.height(window.innerHeight - headerHeight - footerHeight).css("overflow", "hidden");
1078 $content.addClass("ui-virtualgrid-content");
1083 //----------------------------------------------------//
1084 // Calculate size about dom element. //
1085 //----------------------------------------------------//
1086 _calculateClipSize : function () {
1090 if ( self._direction ) {
1091 clipSize = self._calculateClipWidth();
1093 clipSize = self._calculateClipHeight();
1098 _calculateClipWidth : function () {
1100 view = $(self.element),
1101 $parent = $(self.element).parent(),
1103 clipSize = $(window).width();
1104 if ( $parent.hasClass("ui-content") ) {
1105 paddingValue = parseInt($parent.css("padding-left"), 10);
1106 clipSize = clipSize - ( paddingValue || 0 );
1107 paddingValue = parseInt($parent.css("padding-right"), 10);
1108 clipSize = clipSize - ( paddingValue || 0);
1110 clipSize = view.width();
1115 _calculateClipHeight : function () {
1117 view = $(self.element),
1118 $parent = $(self.element).parent(),
1122 clipSize = $(window).height();
1123 if ( $parent.hasClass("ui-content") ) {
1124 paddingValue = parseInt($parent.css("padding-top"), 10);
1125 clipSize = clipSize - ( paddingValue || 0 );
1126 paddingValue = parseInt($parent.css("padding-bottom"), 10);
1127 clipSize = clipSize - ( paddingValue || 0);
1128 header = $parent.siblings(".ui-header");
1129 footer = $parent.siblings(".ui-footer");
1132 if ( header.outerHeight(true) === null ) {
1133 clipSize = clipSize - ( $(".ui-header").outerHeight() || 0 );
1135 clipSize = clipSize - header.outerHeight(true);
1139 clipSize = clipSize - footer.outerHeight(true);
1142 clipSize = view.height();
1147 _calculateColumnSize : function () {
1152 $tempBlock = self._makeRows( 1 );
1153 self._$view.append( $tempBlock.children().first() );
1154 if ( self._direction ) {
1156 self._viewSize = self._$view.width();
1157 $cell = self._$view.children().first().children().first();
1158 self._cellSize = $cell.outerWidth(true);
1159 self._cellOtherSize = $cell.outerHeight(true);
1162 self._viewSize = self._$view.height();
1163 $cell = self._$view.children().first().children().first();
1164 self._cellSize = $cell.outerHeight(true);
1165 self._cellOtherSize = $cell.outerWidth(true);
1167 $tempBlock.remove();
1168 self._$view.children().remove();
1171 _calculateColumnCount : function ( ) {
1173 $view = $(self.element),
1174 viewSize = self._direction ? $view.innerHeight() : $view.innerWidth(),
1177 if ( self._direction ) {
1178 viewSize = viewSize - ( parseInt( $view.css("padding-top"), 10 ) + parseInt( $view.css("padding-bottom"), 10 ) );
1180 viewSize = viewSize - ( parseInt( $view.css("padding-left"), 10 ) + parseInt( $view.css("padding-right"), 10 ) );
1183 itemCount = parseInt( (viewSize / self._cellOtherSize), 10);
1184 return itemCount > 0 ? itemCount : 1 ;
1187 // Read the position of clip form property ('webkit-transform').
1188 // @return : number - position of clip.
1189 _getClipPosition : function () {
1193 result = -self._cellSize,
1194 $scrollview = self._$view.closest(".ui-scrollview-view");
1196 if ( $scrollview ) {
1197 matrix = $scrollview.css("-webkit-transform");
1198 contents = matrix.substr( 7 );
1199 contents = contents.substr( 0, contents.length - 1 );
1200 contents = contents.split( ', ' );
1201 result = Math.abs(contents [5]);
1206 //----------------------------------------------------//
1207 // DOM Element handle //
1208 //----------------------------------------------------//
1209 _makeRows : function ( count ) {
1211 opts = self.options,
1216 $wrapper = $(document.createElement("div"));
1217 $wrapper.addClass("ui-scrollview-view");
1218 for ( index = 0; index < count ; index += 1 ) {
1219 $row = self._makeRow( self._template, index );
1220 if ( self._direction ) {
1221 $row.css("top", 0).css("left", ( index * self._cellSize ));
1223 $wrapper.append($row);
1228 // make a single row block
1229 _makeRow : function ( myTemplate, rowIndex ) {
1231 opts = self.options,
1232 index = rowIndex * self._itemCount,
1236 attrName = self._direction ? "top" : "left",
1237 blockClassName = self._direction ? "ui-virtualgrid-wrapblock-x" : "ui-virtualgrid-wrapblock-y",
1238 blockAttrName = self._direction ? "top" : "left",
1239 wrapBlock = $( document.createElement( "div" ));
1241 wrapBlock.addClass( blockClassName ).attr("row-index", rowIndex);
1242 for ( colIndex = 0; colIndex < self._itemCount; colIndex++ ) {
1243 itemData = self._itemData( index );
1245 htmlData = self._makeHtmlData( myTemplate, index, colIndex);
1246 wrapBlock.append( htmlData );
1253 _makeHtmlData : function ( myTemplate, dataIndex, colIndex ) {
1257 attrName = self._direction ? "top" : "left";
1259 itemData = self._itemData( dataIndex );
1261 htmlData = myTemplate.tmpl( itemData );
1262 $(htmlData).css(attrName, ( colIndex * self._cellOtherSize )).addClass("virtualgrid-item");
1267 _increaseRow : function ( num ) {
1269 rotation = self.options.rotation,
1274 size = self._scalableSize,
1277 headItemIndex = parseInt( $(self._$view.children().first()).attr("row-index"), 10) - 1;
1278 tailItemIndex = parseInt( $(self._$view.children()[self._rowsPerView]).attr("row-index"), 10) + 1;
1280 for ( idx = 1 ; idx <= num ; idx++ ) {
1281 if ( tailItemIndex + idx >= self._totalRowCnt ) {
1282 $row = self._makeRow( self._template, headItemIndex );
1283 self._$view.prepend($row);
1286 $row = self._makeRow( self._template, tailItemIndex + idx );
1287 self._$view.append($row);
1289 if ( self._direction ) {
1290 $row.width(self._cellSize);
1292 $row.height(self._cellSize);
1297 _decreaseRow : function ( num ) {
1301 for ( idx = 0 ; idx < num ; idx++ ) {
1302 self._$view.children().last().remove();
1306 _replaceRows : function ( curCnt, prevCnt, maxCnt, clipPosition ) {
1308 $rows = self._$view.children(),
1313 filterCondition = ( self._filterRatio * self._cellSize) + self._cellSize,
1316 if ( filterCondition < clipPosition ) {
1320 prevRowIndex = parseInt( $($rows[targetCnt]).attr("row-index"), 10);
1321 if ( prevRowIndex === 0 ) {
1323 rowIndex = maxCnt - targetCnt;
1325 rowIndex = Math.round( (prevRowIndex * prevCnt) / curCnt );
1326 if ( rowIndex + self._rowsPerView >= maxCnt ) {
1328 rowIndex = maxCnt - self._rowsPerView;
1330 diffRowCnt = prevRowIndex - rowIndex;
1331 rowIndex -= targetCnt;
1334 for ( idx = 0 ; idx < $rows.length ; idx += 1 ) {
1335 self._replaceRow($rows[idx], circularNum( rowIndex, self._totalRowCnt ));
1341 _replaceRow : function ( block, index ) {
1343 opts = self.options,
1353 $columns = $(block).attr("row-index", index).children();
1354 if ( $columns.length !== self._itemCount ) {
1355 $(block).children().remove();
1356 tempBlocks = $(self._makeRow( self._template, index ));
1357 $(block).append(tempBlocks.children());
1358 tempBlocks.remove();
1362 dataIdx = index * self._itemCount;
1363 for ( idx = 0; idx < self._itemCount ; idx += 1 ) {
1364 $column = $columns[idx];
1365 data = self._itemData(dataIdx);
1366 if ( $column && data ) {
1367 myTemplate = self._template;
1368 htmlData = myTemplate.tmpl( data );
1369 self._replace( $column, htmlData, false );
1370 htmlData.remove(); // Clear temporary htmlData to free cache
1372 } else if ($column && !data ) {
1373 $($column).remove();
1378 /* Text & image src replace function */
1379 // @param oldItem : prev HtmlDivElement
1380 // @param newItem : new HtmlDivElement for replace
1382 _replace : function ( oldItem, newItem, key ) {
1383 $( oldItem ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).each( function ( index ) {
1384 var oldObj = $( this ),
1385 newText = $( newItem ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).eq( index ).text();
1387 $( oldObj ).contents().filter( function () {
1388 return ( this.nodeType == 3 );
1389 }).get( 0 ).data = newText;
1392 $( oldItem ).find( "img" ).each( function ( imgIndex ) {
1393 var oldObj = $( this ),
1394 newImg = $( newItem ).find( "img" ).eq( imgIndex ).attr( "src" );
1396 $( oldObj ).attr( "src", newImg );
1398 $( oldItem).removeData();
1400 $( oldItem ).data( key, $( newItem ).data( key ) );
1405 $( document ).bind( "pagecreate create", function ( e ) {
1406 $(":jqmData(role='virtualgrid')").virtualgrid();
1408 } (jQuery, window, document) );