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
102 ( function ($, window, document, undefined) {
104 function circularNum (num, total) {
112 function MomentumTracker (options) {
113 this.options = $.extend({}, options);
114 this.easing = "easeOutQuad";
123 function getCurrentTime () {
127 $.extend (MomentumTracker.prototype, {
128 start : function (pos, speed, duration) {
129 this.state = (speed !== 0 ) ? tstates.scrolling : tstates.done;
132 this.duration = duration;
137 this.startTime = getCurrentTime();
140 reset : function () {
141 this.state = tstates.done;
147 update : function () {
148 var state = this.state, duration, elapsed, dx, x;
150 if (state == tstates.done) {
153 duration = this.duration;
154 elapsed = getCurrentTime () - this.startTime;
155 elapsed = elapsed > duration ? duration : elapsed;
156 dx = this.speed * (1 - $.easing[this.easing] (elapsed / duration, elapsed, 0, 1, duration) );
160 if (elapsed >= duration) {
161 this.state = tstates.done;
167 return this.state == tstates.done;
170 getPosition : function () {
175 jQuery.widget ("mobile.virtualgrid", jQuery.mobile.widget, {
177 // virtualgrid option
184 create : function () {
185 this._create.apply( this, arguments );
188 _create : function ( args ) {
197 _cellSize : undefined,
198 _currentItemCount : 0,
209 _itemData : function ( idx ) { return null; },
211 _cacheItemData : function ( minIdx, maxIdx ) { },
218 // axis - ( true : x , false : y )
236 $dom = $(self.element),
241 // If mandatory options are not given, Do nothing.
246 if ( !self._loadData(args) ) {
250 // set a scroll direction.
251 self._direction = opts.direction === 'x' ? true : false;
253 // itemcount is assigned 'auto'
254 if ( typeof opts.itemcount === "string" && opts.itemcount.toUpperCase() == "AUTO" ) {
259 self._$clip = $(self.element).addClass("ui-scrollview-clip").addClass("ui-virtualgrid-view");
260 $item = $(document.createElement("div")).addClass("ui-scrollview-view");
261 self._clipSize = self._calculateClipSize();
262 self._$clip.append($item);
264 self._$clip.css("overflow", "hidden");
265 self._$view.css("overflow", "hidden");
267 // inherit from scrollview widget.
268 self._scrollView = $.tizen.scrollview.prototype;
269 self._initScrollView();
272 self._createTracker();
273 self._makePositioned(self._$clip);
274 self._timerInterval = 1000 / self.options.fps;
277 self._timerCB = function () {
278 self._handleMomentumScroll();
280 $dom.closest(".ui-content").addClass("ui-virtualgrid-content").css("overflow", "hidden");
282 // add event handler.
283 self._addBehaviors();
285 self._currentItemCount = 0;
286 self._createScrollBar();
290 // The argument is checked for compliance with the specified format.
291 // @param args : Object
293 _loadData : function ( args ) {
296 if ( args.itemData && typeof args.itemData == 'function' ) {
297 self._itemData = args.itemData;
301 if ( args.numItemData ) {
302 if ( typeof args.numItemData == 'function' ) {
303 self._numItemData = args.numItemData( );
304 } else if ( typeof args.numItemData == 'number' ) {
305 self._numItemData = args.numItemData;
315 // Make up the first screen.
316 _initLayout: function () {
322 for ( i = -1; i < self._rowsPerView + 1; i += 1 ) {
323 $row = self._$rows[ circularNum( i, self._$rows.length ) ];
324 self._$view.append( $row );
326 self._setElementTransform( -self._cellSize );
328 self._replaceRow(self._$view.children().first(), self._totalRowCnt - 1);
329 if ( opts.rotation && self._rowsPerView >= self._totalRowCnt ) {
330 self._replaceRow(self._$view.children().last(), 0);
335 _setViewSize : function () {
340 if ( self._direction ) {
341 width = self._cellSize * ( self._rowsPerView + 2 );
342 width = parseInt(width, 10) + 1;
343 self._$view.width( width );
344 self._viewSize = self._$view.width();
346 self._$view.height( self._cellSize * ( self._rowsPerView + 2 ) );
347 self._$clip.height( self._clipSize );
348 self._viewSize = self._$view.height();
352 refresh : function () {
358 self._template = $( "#" + opts.template );
359 if ( !self._template ) {
363 width = self._calculateClipWidth();
364 height = self._calculateClipHeight();
365 self._$view.width(width).height(height);
366 self._$clip.width(width).height(height);
368 self._clipSize = self._calculateClipSize();
369 self._calculateColumnSize();
370 self._initPageProperty();
371 self._setScrollBarSize( );
374 _initPageProperty : function () {
380 attributeName = self._direction ? "width" : "height";
382 if ( self._isAuto ) {
383 columnCount = self._calculateColumnCount();
385 columnCount = self.options.itemcount;
387 totalRowCnt = parseInt(self._numItemData / columnCount , 10 );
388 self._totalRowCnt = self._numItemData % columnCount === 0 ? totalRowCnt : totalRowCnt + 1;
389 self._itemCount = columnCount;
391 if ( self._cellSize <= 0) {
395 rowsPerView = self._clipSize / self._cellSize;
396 rowsPerView = Math.ceil( rowsPerView );
397 self._rowsPerView = parseInt( rowsPerView, 10);
399 $child = self._makeRows( rowsPerView + 2 );
400 $(self._$view).append($child.children());
401 self._$view.children().css(attributeName, self._cellSize + "px");
402 self._$rows = self._$view.children().detach();
404 self._reservedPos = -self._cellSize;
405 self._scalableSize = -self._cellSize;
409 self._blockScroll = self._rowsPerView > self._totalRowCnt;
410 self._maxSize = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
411 self._maxViewSize = ( self._rowsPerView ) * self._cellSize;
412 self._modifyViewPos = -self._cellSize;
413 if ( self._clipSize < self._maxViewSize ) {
414 self._modifyViewPos = (-self._cellSize) + ( self._clipSize - self._maxViewSize );
418 resize : function ( ) {
428 itemCount = self._calculateColumnCount();
429 if ( self._isAuto && itemCount != self._itemCount ) {
430 totalRowCnt = parseInt(self._numItemData / itemCount , 10 );
431 self._totalRowCnt = self._numItemData % itemCount === 0 ? totalRowCnt : totalRowCnt + 1;
432 prevcnt = self._itemCount;
433 self._itemCount = itemCount;
434 clipPosition = self._getClipPosition();
437 diffRowCnt = self._replaceRows(itemCount, prevcnt, self._totalRowCnt, clipPosition);
438 self._maxSize = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
439 self._scalableSize += (-diffRowCnt) * self._cellSize;
440 self._reservedPos += (-diffRowCnt) * self._cellSize;
441 self._setScrollBarSize();
442 self._setScrollBarPosition(diffRowCnt);
447 clipSize = self._calculateClipSize();
448 if ( clipSize !== self._clipSize ) {
449 rowsPerView = clipSize / self._cellSize;
450 rowsPerView = parseInt( Math.ceil( rowsPerView ), 10 );
452 if ( rowsPerView > self._rowsPerView ) {
454 self._increaseRow( rowsPerView - self._rowsPerView );
455 } else if ( rowsPerView < self._rowsPerView ) {
457 self._decreaseRow( self._rowsPerView - rowsPerView );
459 self._rowsPerView = rowsPerView;
460 self._clipSize = clipSize;
461 self._blockScroll = self._rowsPerView > self._totalRowCnt;
462 self._maxSize = ( self._totalRowCnt - self._rowsPerView ) * self._cellSize;
463 self._maxViewSize = ( self._rowsPerView ) * self._cellSize;
464 if ( self._clipSize < self._maxViewSize ) {
465 self._modifyViewPos = (-self._cellSize) + ( self._clipSize - self._maxViewSize );
467 if ( self._direction ) {
468 self._$clip.width(self._clipSize);
470 self._$clip.height(self._clipSize);
472 self._setScrollBarSize();
473 self._setScrollBarPosition(0);
478 _initScrollView : function () {
480 $.extend(self.options, self._scrollView.options);
481 self.options.moveThreshold = 10;
482 self.options.showScrollBars = false;
483 self._getScrollHierarchy = self._scrollView._getScrollHierarchy;
484 self._makePositioned = self._scrollView._makePositioned;
485 self._set_scrollbar_size = self._scrollView._set_scrollbar_size;
486 self._setStyleTransform = self._scrollView._setElementTransform;
489 _createTracker : function () {
492 self._tracker = new MomentumTracker(self.options);
493 if ( self._direction ) {
494 self._hTracker = self._tracker;
495 self._$clip.width(self._clipSize);
497 self._vTracker = self._tracker;
498 self._$clip.height(self._clipSize);
502 //----------------------------------------------------//
504 //----------------------------------------------------//
505 _createScrollBar : function () {
507 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
508 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
510 if ( self.options.rotation ) {
514 if ( self._direction ) {
515 self._$clip.append( prefix + "x" + suffix );
516 self._hScrollBar = $(self._$clip.children(".ui-scrollbar-x"));
517 self._hScrollBar.find(".ui-scrollbar-thumb").addClass("ui-scrollbar-thumb-x");
519 self._$clip.append( prefix + "y" + suffix );
520 self._vScrollBar = $(self._$clip.children(".ui-scrollbar-y"));
521 self._vScrollBar.find(".ui-scrollbar-thumb").addClass("ui-scrollbar-thumb-y");
525 _setScrollBarSize: function () {
533 if ( self.options.rotation ) {
537 scrollBarSize = parseInt( self._maxViewSize / self._clipSize , 10);
538 if ( self._direction ) {
539 $scrollBar = self._hScrollBar.find(".ui-scrollbar-thumb");
541 currentSize = $scrollBar.width();
542 className = "ui-scrollbar-thumb-x";
543 self._hScrollBar.css("width", self._clipSize);
545 $scrollBar = self._vScrollBar.find(".ui-scrollbar-thumb");
547 className = "ui-scrollbar-thumb-y";
548 currentSize = $scrollBar.height();
549 self._vScrollBar.css("height", self._clipSize);
552 if ( scrollBarSize > currentSize ) {
553 $scrollBar.removeClass(className);
554 $scrollBar.css(attrName, scrollBarSize);
556 scrollBarSize = currentSize;
559 self._itemScrollSize = parseFloat( ( self._clipSize - scrollBarSize ) / ( self._totalRowCnt - self._rowsPerView ) );
560 self._itemScrollSize = Math.round(self._itemScrollSize * 100) / 100;
563 _setScrollBarPosition : function ( di, duration ) {
569 if ( self.options.rotation ) {
573 self._currentItemCount = self._currentItemCount + di;
574 if ( self._vScrollBar ) {
575 $sbt = self._vScrollBar .find(".ui-scrollbar-thumb");
576 y = ( self._currentItemCount * self._itemScrollSize ) + "px";
578 $sbt = self._hScrollBar .find(".ui-scrollbar-thumb");
579 x = ( self._currentItemCount * self._itemScrollSize ) + "px";
581 self._setStyleTransform( $sbt, x, y, duration );
584 _hideScrollBars : function () {
586 vclass = "ui-scrollbar-visible";
588 if ( self.options.rotation ) {
592 if ( self._vScrollBar ) {
593 self._vScrollBar.removeClass( vclass );
595 self._hScrollBar.removeClass( vclass );
599 _showScrollBars : function () {
601 vclass = "ui-scrollbar-visible";
603 if ( self.options.rotation ) {
607 if ( self._vScrollBar ) {
608 self._vScrollBar.addClass( vclass );
610 self._hScrollBar.addClass( vclass );
614 //----------------------------------------------------//
616 //----------------------------------------------------//
617 centerTo: function ( selector ) {
623 if ( !self.options.rotation ) {
627 for ( i = 0; i < self._$rows.length; i++ ) {
628 if ( $( self._$rows[i]).hasClass( selector ) ) {
629 if ( self._direction ) {
630 newX = -( i * self._cellSize - self._clipSize / 2 + self._cellSize * 2 );
632 newY = -( i * self._cellSize - self._clipSize / 2 + self._cellSize * 2 );
634 self.scrollTo( newX, newY );
640 scrollTo: function ( x, y, duration ) {
642 if ( self._direction ) {
643 self._sx = self._reservedPos;
644 self._reservedPos = x;
646 self._sy = self._reservedPos;
647 self._reservedPos = y;
649 self._scrollView.scrollTo.apply( this, [ x, y, duration ] );
652 getScrollPosition: function () {
653 if ( this.direction ) {
654 return { x: -this._ry, y: 0 };
656 return { x: 0, y: -this._ry };
659 _setScrollPosition: function ( x, y ) {
661 sy = self._scalableSize,
662 distance = self._direction ? x : y,
664 di = parseInt( dy / self._cellSize, 10 ),
669 if ( self._blockScroll ) {
673 if ( ! self.options.rotation ) {
674 if ( dy > 0 && distance >= -self._cellSize && self._scalableSize >= -self._cellSize ) {
677 self._scalableSize = -self._cellSize;
678 self._setElementTransform( -self._cellSize );
681 if ( (dy < 0 && self._scalableSize <= -(self._maxSize + self._cellSize) )) {
684 self._scalableSize = -(self._maxSize + self._cellSize);
685 self._setElementTransform( self._modifyViewPos );
690 if ( di > 0 ) { // scroll up
691 for ( i = 0; i < di; i++ ) {
692 idx = -parseInt( ( sy / self._cellSize ) + i + 3, 10 );
693 $row = self._$view.children( ).last( ).detach( );
694 self._replaceRow( $row, circularNum( idx, self._totalRowCnt ) );
695 self._$view.prepend( $row );
696 self._setScrollBarPosition(-1);
698 } else if ( di < 0 ) { // scroll down
699 for ( i = 0; i > di; i-- ) {
700 idx = self._rowsPerView - parseInt( ( sy / self._cellSize ) + i, 10 );
701 $row = self._$view.children().first().detach();
702 self._replaceRow($row, circularNum( idx, self._totalRowCnt ) );
703 self._$view.append( $row );
704 self._setScrollBarPosition(1);
707 self._scalableSize += di * self._cellSize;
708 self._setElementTransform( distance - self._scalableSize - self._cellSize );
711 _setElementTransform : function ( value ) {
716 if ( self._direction ) {
721 self._setStyleTransform(self._$view, x, y );
724 //----------------------------------------------------//
726 //----------------------------------------------------//
727 _handleMomentumScroll: function () {
738 if ( self._direction ) {
743 keepGoing = !t.done();
746 self._setScrollPosition( x, y );
747 if ( !opts.rotation ) {
748 keepGoing = !t.done();
749 self._reservedPos = self._direction ? x : y;
751 self._reservedPos = self._reservedPos <= (-(self._maxSize - self._modifyViewPos)) ? ( - ( self._maxSize + self._cellSize) ) : self._reservedPos;
753 self._reservedPos = self._reservedPos > -self._cellSize ? -self._cellSize : self._reservedPos;
755 self._reservedPos = self._direction ? x : y;
757 self._$clip.trigger( self.options.updateEventName, [ { x: x, y: y } ] );
760 self._timerID = setTimeout( self._timerCB, self._timerInterval );
766 _startMScroll: function ( speedX, speedY ) {
768 if ( self._direction ) {
769 self._sx = self._reservedPos;
771 self._sy = self._reservedPos;
773 self._scrollView._startMScroll.apply(self, [speedX, speedY]);
776 _stopMScroll: function () {
777 this._scrollView._stopMScroll.apply(this);
780 _enableTracking: function () {
781 $(document).bind( this._dragMoveEvt, this._dragMoveCB );
782 $(document).bind( this._dragStopEvt, this._dragStopCB );
785 _disableTracking: function () {
786 $(document).unbind( this._dragMoveEvt, this._dragMoveCB );
787 $(document).unbind( this._dragStopEvt, this._dragStopCB );
790 _handleDragStart: function ( e, ex, ey ) {
792 self._scrollView._handleDragStart.apply( this, [ e, ex, ey ] );
793 self._eventPos = self._direction ? ex : ey;
794 self._nextPos = self._reservedPos;
797 _handleDragMove: function ( e, ex, ey ) {
799 dx = ex - self._lastX,
800 dy = ey - self._lastY,
804 self._lastMove = getCurrentTime();
808 self._didDrag = true;
813 if ( self._direction ) {
814 self._movePos = ex - self._eventPos;
815 x = self._nextPos + self._movePos;
817 self._movePos = ey - self._eventPos;
818 y = self._nextPos + self._movePos;
820 self._showScrollBars();
821 self._setScrollPosition( x, y );
825 _handleDragStop: function ( e ) {
828 self._reservedPos = self._movePos ? self._nextPos + self._movePos : self._reservedPos;
829 self._scrollView._handleDragStop.apply( this, [ e ] );
830 return self._didDrag ? false : undefined;
833 _addBehaviors: function () {
836 // scroll event handler.
837 if ( self.options.eventType === "mouse" ) {
838 self._dragStartEvt = "mousedown";
839 self._dragStartCB = function ( e ) {
840 return self._handleDragStart( e, e.clientX, e.clientY );
843 self._dragMoveEvt = "mousemove";
844 self._dragMoveCB = function ( e ) {
845 return self._handleDragMove( e, e.clientX, e.clientY );
848 self._dragStopEvt = "mouseup";
849 self._dragStopCB = function ( e ) {
850 return self._handleDragStop( e, e.clientX, e.clientY );
853 self._$view.bind( "vclick", function (e) {
854 return !self._didDrag;
857 self._dragStartEvt = "touchstart";
858 self._dragStartCB = function ( e ) {
859 var t = e.originalEvent.targetTouches[0];
860 return self._handleDragStart(e, t.pageX, t.pageY );
863 self._dragMoveEvt = "touchmove";
864 self._dragMoveCB = function ( e ) {
865 var t = e.originalEvent.targetTouches[0];
866 return self._handleDragMove(e, t.pageX, t.pageY );
869 self._dragStopEvt = "touchend";
870 self._dragStopCB = function ( e ) {
871 return self._handleDragStop( e );
874 self._$view.bind( self._dragStartEvt, self._dragStartCB );
877 self._$view.delegate(".virtualgrid-item", "click", function (event) {
878 var $selectedItem = $(this);
879 $selectedItem.trigger("select", this);
882 $( window ).bind("resize", function ( e ) {
884 $virtualgrid = $(".ui-virtualgrid-view");
885 if ( $virtualgrid.length !== 0 ) {
886 if ( self._direction ) {
887 height = self._calculateClipHeight();
888 self._$view.height(height);
889 self._$clip.height(height);
891 height = self._calculateClipWidth();
892 self._$view.width(height);
893 self._$clip.width(height);
899 $(document).one("pageshow", function (event) {
900 var $page = $(self.element).parents(".ui-page"),
901 $header = $page.find( ":jqmData(role='header')" ),
902 $footer = $page.find( ":jqmData(role='footer')" ),
903 $content = $page.find( ":jqmData(role='content')" ),
904 footerHeight = $footer ? $footer.height() : 0,
905 headerHeight = $header ? $header.height() : 0;
907 if ( $page && $content ) {
908 $content.height(window.innerHeight - headerHeight - footerHeight).css("overflow", "hidden");
909 $content.addClass("ui-virtualgrid-content");
914 //----------------------------------------------------//
915 // Calculate size about dom element. //
916 //----------------------------------------------------//
917 _calculateClipSize : function () {
921 if ( self._direction ) {
922 clipSize = self._calculateClipWidth();
924 clipSize = self._calculateClipHeight();
929 _calculateClipWidth : function () {
931 view = $(self.element),
932 $parent = $(self.element).parent(),
934 clipSize = $(window).width();
935 if ( $parent.hasClass("ui-content") ) {
936 paddingValue = parseInt($parent.css("padding-left"), 10);
937 clipSize = clipSize - ( paddingValue || 0 );
938 paddingValue = parseInt($parent.css("padding-right"), 10);
939 clipSize = clipSize - ( paddingValue || 0);
941 clipSize = view.width();
946 _calculateClipHeight : function () {
948 view = $(self.element),
949 $parent = $(self.element).parent(),
953 clipSize = $(window).height();
954 if ( $parent.hasClass("ui-content") ) {
955 paddingValue = parseInt($parent.css("padding-top"), 10);
956 clipSize = clipSize - ( paddingValue || 0 );
957 paddingValue = parseInt($parent.css("padding-bottom"), 10);
958 clipSize = clipSize - ( paddingValue || 0);
959 header = $parent.siblings(".ui-header");
960 footer = $parent.siblings(".ui-footer");
963 if ( header.outerHeight(true) === null ) {
964 clipSize = clipSize - ( $(".ui-header").outerHeight() || 0 );
966 clipSize = clipSize - header.outerHeight(true);
970 clipSize = clipSize - footer.outerHeight(true);
973 clipSize = view.height();
978 _calculateColumnSize : function () {
983 $tempBlock = self._makeRows( 1 );
984 self._$view.append( $tempBlock.children().first() );
985 if ( self._direction ) {
987 self._viewSize = self._$view.width();
988 $cell = self._$view.children().first().children().first();
989 self._cellSize = $cell.outerWidth(true);
990 self._cellOtherSize = $cell.outerHeight(true);
993 self._viewSize = self._$view.height();
994 $cell = self._$view.children().first().children().first();
995 self._cellSize = $cell.outerHeight(true);
996 self._cellOtherSize = $cell.outerWidth(true);
999 self._$view.children().remove();
1002 _calculateColumnCount : function ( ) {
1004 $view = $(self.element),
1005 viewSize = self._direction ? $view.innerHeight() : $view.innerWidth(),
1008 if ( self._direction ) {
1009 viewSize = viewSize - ( parseInt( $view.css("padding-top"), 10 ) + parseInt( $view.css("padding-bottom"), 10 ) );
1011 viewSize = viewSize - ( parseInt( $view.css("padding-left"), 10 ) + parseInt( $view.css("padding-right"), 10 ) );
1014 itemCount = parseInt( (viewSize / self._cellOtherSize), 10);
1015 return itemCount > 0 ? itemCount : 1 ;
1018 // Read the position of clip form property ('webkit-transform').
1019 // @return : number - position of clip.
1020 _getClipPosition : function () {
1024 result = -self._cellSize,
1025 $scrollview = self._$view.closest(".ui-scrollview-view");
1027 if ( $scrollview ) {
1028 matrix = $scrollview.css("-webkit-transform");
1029 contents = matrix.substr( 7 );
1030 contents = contents.substr( 0, contents.length - 1 );
1031 contents = contents.split( ', ' );
1032 result = Math.abs(contents [5]);
1037 //----------------------------------------------------//
1038 // DOM Element handle //
1039 //----------------------------------------------------//
1040 _makeRows : function ( count ) {
1042 opts = self.options,
1047 $wrapper = $(document.createElement("div"));
1048 $wrapper.addClass("ui-scrollview-view");
1049 for ( index = 0; index < count ; index += 1 ) {
1050 $row = self._makeRow( self._template, index );
1051 if ( self._direction ) {
1052 $row.css("top", 0).css("left", ( index * self._cellSize ));
1054 $wrapper.append($row);
1059 // make a single row block
1060 _makeRow : function ( myTemplate, rowIndex ) {
1062 opts = self.options,
1063 index = rowIndex * self._itemCount,
1067 attrName = self._direction ? "top" : "left",
1068 blockClassName = self._direction ? "ui-virtualgrid-wrapblock-x" : "ui-virtualgrid-wrapblock-y",
1069 blockAttrName = self._direction ? "top" : "left",
1070 wrapBlock = $( document.createElement( "div" ));
1072 wrapBlock.addClass( blockClassName ).attr("row-index", rowIndex);
1073 for ( colIndex = 0; colIndex < self._itemCount; colIndex++ ) {
1074 itemData = self._itemData( index );
1076 htmlData = self._makeHtmlData( myTemplate, index, colIndex);
1077 wrapBlock.append( htmlData );
1084 _makeHtmlData : function ( myTemplate, dataIndex, colIndex ) {
1088 attrName = self._direction ? "top" : "left";
1090 itemData = self._itemData( dataIndex );
1092 htmlData = myTemplate.tmpl( itemData );
1093 $(htmlData).css(attrName, ( colIndex * self._cellOtherSize )).addClass("virtualgrid-item");
1098 _increaseRow : function ( num ) {
1100 rotation = self.options.rotation,
1105 size = self._scalableSize,
1108 headItemIndex = parseInt( $(self._$view.children().first()).attr("row-index"), 10) - 1;
1109 tailItemIndex = parseInt( $(self._$view.children()[self._rowsPerView]).attr("row-index"), 10) + 1;
1111 for ( idx = 1 ; idx <= num ; idx++ ) {
1112 if ( tailItemIndex + idx >= self._totalRowCnt ) {
1113 $row = self._makeRow( self._template, headItemIndex );
1114 self._$view.prepend($row);
1117 $row = self._makeRow( self._template, tailItemIndex + idx );
1118 self._$view.append($row);
1120 if ( self._direction ) {
1121 $row.width(self._cellSize);
1123 $row.height(self._cellSize);
1128 _decreaseRow : function ( num ) {
1132 for ( idx = 0 ; idx < num ; idx++ ) {
1133 self._$view.children().last().remove();
1137 _replaceRows : function ( curCnt, prevCnt, maxCnt, clipPosition ) {
1139 $rows = self._$view.children(),
1144 filterCondition = ( self._filterRatio * self._cellSize) + self._cellSize,
1147 if ( filterCondition < clipPosition ) {
1151 prevRowIndex = parseInt( $($rows[targetCnt]).attr("row-index"), 10);
1152 if ( prevRowIndex === 0 ) {
1154 rowIndex = maxCnt - targetCnt;
1156 rowIndex = Math.round( (prevRowIndex * prevCnt) / curCnt );
1157 if ( rowIndex + self._rowsPerView >= maxCnt ) {
1159 rowIndex = maxCnt - self._rowsPerView;
1161 diffRowCnt = prevRowIndex - rowIndex;
1162 rowIndex -= targetCnt;
1165 for ( idx = 0 ; idx < $rows.length ; idx += 1 ) {
1166 self._replaceRow($rows[idx], circularNum( rowIndex, self._totalRowCnt ));
1172 _replaceRow : function ( block, index ) {
1174 opts = self.options,
1184 $columns = $(block).attr("row-index", index).children();
1185 if ( $columns.length !== self._itemCount ) {
1186 $(block).children().remove();
1187 tempBlocks = $(self._makeRow( self._template, index ));
1188 $(block).append(tempBlocks.children());
1189 tempBlocks.remove();
1193 dataIdx = index * self._itemCount;
1194 for ( idx = 0; idx < self._itemCount ; idx += 1 ) {
1195 $column = $columns[idx];
1196 data = self._itemData(dataIdx);
1197 if ( $column && data ) {
1198 myTemplate = self._template;
1199 htmlData = myTemplate.tmpl( data );
1200 self._replace( $column, htmlData, false );
1201 htmlData.remove(); // Clear temporary htmlData to free cache
1203 } else if ($column && !data ) {
1204 $($column).remove();
1209 /* Text & image src replace function */
1210 // @param oldItem : prev HtmlDivElement
1211 // @param newItem : new HtmlDivElement for replace
1213 _replace : function ( oldItem, newItem, key ) {
1214 $( oldItem ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).each( function ( index ) {
1215 var oldObj = $( this ),
1216 newText = $( newItem ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).eq( index ).text();
1218 $( oldObj ).contents().filter( function () {
1219 return ( this.nodeType == 3 );
1220 }).get( 0 ).data = newText;
1223 $( oldItem ).find( "img" ).each( function ( imgIndex ) {
1224 var oldObj = $( this ),
1225 newImg = $( newItem ).find( "img" ).eq( imgIndex ).attr( "src" );
1227 $( oldObj ).attr( "src", newImg );
1229 $( oldItem).removeData();
1231 $( oldItem ).data( key, $( newItem ).data( key ) );
1236 $( document ).bind( "pagecreate create", function ( e ) {
1237 $(":jqmData(role='virtualgrid')").virtualgrid();
1239 } (jQuery, window, document) );