2 * jQuery Mobile Framework : scrollview plugin
3 * Copyright (c) 2010 Adobe Systems Incorporated - Kin Blas (jblas@adobe.com)
4 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
5 * Note: Code is in draft form and is subject to change
6 * Modified by Koeun Choi <koeun.choi@samsung.com>
7 * Modified by Minkyu Kang <mk7.kang@samsung.com>
10 (function ( $, window, document, undefined ) {
12 function resizePageContentHeight( page ) {
13 var $page = $( page ),
14 $content = $page.children(".ui-content"),
15 hh = $page.children(".ui-header").outerHeight() || 0,
16 fh = $page.children(".ui-footer").outerHeight() || 0,
17 pt = parseFloat( $content.css("padding-top") ),
18 pb = parseFloat( $content.css("padding-bottom") ),
19 wh = $( window ).height();
21 $content.height( wh - (hh + fh) - (pt + pb) );
24 function MomentumTracker( options ) {
25 this.options = $.extend( {}, options );
26 this.easing = "easeOutQuad";
37 function getCurrentTime() {
41 jQuery.widget( "tizen.scrollview", jQuery.mobile.widget, {
43 direction: null, // "x", "y", or null for both.
46 scrollDuration: 1000, // Duration of the scrolling animation in msecs.
47 overshootDuration: 250, // Duration of the overshoot animation in msecs.
48 snapbackDuration: 500, // Duration of the snapback animation in msecs.
50 moveThreshold: 30, // User must move this many pixels in any direction to trigger a scroll.
51 moveIntervalThreshold: 150, // Time between mousemoves must not exceed this threshold.
53 scrollMethod: "translate", // "translate", "position"
54 startEventName: "scrollstart",
55 updateEventName: "scrollupdate",
56 stopEventName: "scrollstop",
58 eventType: $.support.touch ? "touch" : "mouse",
61 overshootEnable: false,
62 outerScrollEnable: true,
67 _getViewHeight: function () {
68 return this._$view.height() + this._view_offset;
71 _makePositioned: function ( $ele ) {
72 if ( $ele.css("position") === "static" ) {
73 $ele.css( "position", "relative" );
77 _create: function () {
81 this._$clip = $( this.element ).addClass("ui-scrollview-clip");
83 if ( this._$clip.children(".ui-scrollview-view").length ) {
84 this._$view = this._$clip.children(".ui-scrollview-view");
86 this._$view = this._$clip.wrapInner("<div></div>").children()
87 .addClass("ui-scrollview-view");
90 if ( this.options.scrollMethod === "translate" ) {
91 if ( this._$view.css("transform") === undefined ) {
92 this.options.scrollMethod = "position";
96 this._$clip.css( "overflow", "hidden" );
97 this._makePositioned( this._$clip );
99 this._makePositioned( this._$view );
100 this._$view.css( { left: 0, top: 0 } );
102 this._view_offset = this._$view.offset().top - this._$clip.offset().top;
103 this._view_height = this._getViewHeight();
108 direction = this.options.direction;
110 this._hTracker = ( direction !== "y" ) ?
111 new MomentumTracker( this.options ) : null;
112 this._vTracker = ( direction !== "x" ) ?
113 new MomentumTracker( this.options ) : null;
115 this._timerInterval = this.options.timerInterval;
118 this._timerCB = function () {
119 self._handleMomentumScroll();
123 this._add_scrollbar();
124 this._add_scroll_jump();
125 this._add_overflow_indicator();
128 _startMScroll: function ( speedX, speedY ) {
129 var keepGoing = false,
130 duration = this.options.scrollDuration,
137 this._showScrollBars();
138 this._showOverflowIndicator();
140 this._$clip.trigger( this.options.startEventName );
143 c = this._$clip.width();
144 v = this._$view.width();
146 if ( ( this._sx === 0 && speedX > 0 ) ||
147 ( this._sx === -(v - c) && speedX < 0 ) ) {
151 ht.start( this._sx, speedX,
152 duration, (v > c) ? -(v - c) : 0, 0 );
153 keepGoing = !ht.done();
157 c = this._$clip.height();
158 v = this._getViewHeight();
160 if ( ( this._sy === 0 && speedY > 0 ) ||
161 ( this._sy === -(v - c) && speedY < 0 ) ) {
165 vt.start( this._sy, speedY,
166 duration, (v > c) ? -(v - c) : 0, 0 );
167 keepGoing = keepGoing || !vt.done();
171 this._timerID = setTimeout( this._timerCB, this._timerInterval );
177 _stopMScroll: function () {
178 if ( this._timerID ) {
179 this._$clip.trigger( this.options.stopEventName );
180 clearTimeout( this._timerID );
184 if ( this._vTracker ) {
185 this._vTracker.reset();
188 if ( this._hTracker ) {
189 this._hTracker.reset();
192 this._hideScrollBars();
193 this._hideOverflowIndicator();
196 _handleMomentumScroll: function () {
197 var keepGoing = false,
202 bouncing = function ( dir ) {
203 setTimeout( function () {
204 self._bouncing_dir = dir;
205 self._setBouncing( self._$view, "in" );
208 setTimeout( function () {
209 self._setBouncing( self._$view, "out" );
215 if ( this._outerScrolling ) {
220 vt.update( this.options.overshootEnable );
221 y = vt.getPosition();
222 keepGoing = !vt.done();
224 if ( vt.getRemained() > this.options.overshootDuration ) {
225 scroll_height = this._getViewHeight() - this._$clip.height();
228 this._outerScroll( y - vt.getRemained() / 3, scroll_height );
230 if ( scroll_height > 0 ) {
233 } else if ( vt.isMax() ) {
234 this._outerScroll( vt.getRemained() / 3, scroll_height );
236 if ( scroll_height > 0 ) {
244 ht.update( this.options.overshootEnable );
245 x = ht.getPosition();
246 keepGoing = keepGoing || !ht.done();
249 this._setScrollPosition( x, y );
250 this._$clip.trigger( this.options.updateEventName,
251 [ { x: x, y: y } ] );
254 this._timerID = setTimeout( this._timerCB, this._timerInterval );
260 _setElementTransform: function ( $ele, x, y, duration ) {
264 if ( this._bouncing ) {
268 if ( !duration || duration === undefined ) {
271 transition = "-webkit-transform " + duration / 1000 + "s ease-out";
274 if ( $.support.cssTransform3d ) {
275 translate = "translate3d(" + x + "," + y + ", 0px)";
277 translate = "translate(" + x + "," + y + ")";
281 "-moz-transform": translate,
282 "-webkit-transform": translate,
283 "-ms-transform": translate,
284 "-o-transform": translate,
285 "transform": translate,
286 "-webkit-transition": transition
290 _setBouncing: function ( $ele, dir ) {
291 if ( dir === "in" ) {
292 if ( this._bouncing ) {
296 this._bouncing = true;
297 this._bouncing_count = 1;
299 this._bouncing_org_x = 1;
300 this._bouncing_org_y = 1;
302 this._bouncing_x = 0.99;
303 this._bouncing_y = 0.99;
305 this._setOverflowIndicator( this._bouncing_dir );
306 } else if ( dir === "out" ) {
307 if ( !this._bouncing ) {
311 this._bouncing = false;
312 this._bouncing_count = 1;
314 this._bouncing_org_x = this._bouncing_x;
315 this._bouncing_org_y = this._bouncing_y;
317 this._bouncing_x = 1;
318 this._bouncing_y = 1;
319 this._setOverflowIndicator( this._bouncing_dir );
324 this._doBouncing( $ele, dir );
327 _doBouncing: function ( $ele, dir ) {
335 if ( $.support.cssTransform3d ) {
336 translate = "translate3d(" + this._sx + "px," + this._sy + "px, 0px)";
338 translate = "translate(" + this._sx + "px," + this._sy + "px)";
341 if ( dir === "in" ) {
342 x_rate = this._bouncing_org_x - ( this._bouncing_org_x -
343 this._bouncing_x ) / frame * this._bouncing_count;
344 y_rate = this._bouncing_org_y - ( this._bouncing_org_y -
345 this._bouncing_y ) / frame * this._bouncing_count;
347 translate += " scale(" + x_rate + "," + y_rate + ")";
348 } else if ( dir === "out" ) {
349 x_rate = this._bouncing_org_x + ( this._bouncing_x -
350 this._bouncing_org_x ) / frame * this._bouncing_count;
351 y_rate = this._bouncing_org_y + ( this._bouncing_y -
352 this._bouncing_org_y ) / frame * this._bouncing_count;
354 translate += " scale(" + x_rate + "," + y_rate + ")";
359 if ( this._bouncing_dir ) {
360 origin = "50% " + ( this._bouncing_y * 100 - 10 ) + "%";
366 "-moz-transform": translate,
367 "-webkit-transform": translate,
368 "-ms-transform": translate,
369 "-o-transform": translate,
370 "transform": translate,
371 "-webkit-transform-origin": origin,
374 this._bouncing_count++;
376 if ( this._bouncing_count > frame ) {
380 setTimeout( function () {
381 self._doBouncing( $ele, dir );
382 }, this._timerInterval );
385 _setCalibration: function ( x, y ) {
386 if ( this.options.overshootEnable ) {
392 var $v = this._$view,
394 dirLock = this._directionLock,
398 if ( dirLock !== "y" && this._hTracker ) {
399 scroll_width = $v.width() - $c.width();
403 } else if ( x < -scroll_width ) {
404 this._sx = -scroll_width;
409 if ( scroll_width < 0 ) {
414 if ( dirLock !== "x" && this._vTracker ) {
415 scroll_height = this._getViewHeight() - $c.height();
420 if ( scroll_height > 0 ) {
421 this._bouncing_dir = 0;
422 this._setBouncing( this._$view, "in" );
424 } else if ( y < -scroll_height ) {
425 this._sy = -scroll_height;
427 if ( scroll_height > 0 ) {
428 this._bouncing_dir = 1;
429 this._setBouncing( this._$view, "in" );
435 if ( scroll_height < 0 ) {
441 _setScrollPosition: function ( x, y, duration ) {
442 var $v = this._$view,
443 sm = this.options.scrollMethod,
444 $vsb = this._$vScrollBar,
445 $hsb = this._$hScrollBar,
448 if ( this._sx === x && this._sy === y ) {
452 this._setCalibration( x, y );
457 if ( sm === "translate" ) {
458 this._setElementTransform( $v, x + "px", y + "px", duration );
460 $v.css( {left: x + "px", top: y + "px"} );
464 $sbt = $vsb.find(".ui-scrollbar-thumb");
466 if ( sm === "translate" ) {
467 this._setElementTransform( $sbt, "0px",
468 -y / this._getViewHeight() * $sbt.parent().height() + "px",
471 $sbt.css( "top", -y / this._getViewHeight() * 100 + "%" );
476 $sbt = $hsb.find(".ui-scrollbar-thumb");
478 if ( sm === "translate" ) {
479 this._setElementTransform( $sbt,
480 -x / $v.width() * $sbt.parent().width() + "px", "0px",
483 $sbt.css("left", -x / $v.width() * 100 + "%");
488 _outerScroll: function ( y, scroll_height ) {
490 top = $( window ).scrollTop() - window.screenTop,
492 duration = this.options.snapbackDuration,
493 start = getCurrentTime(),
496 if ( !this.options.outerScrollEnable ) {
500 if ( this._$clip.jqmData("scroll") !== "y" ) {
504 if ( this._outerScrolling ) {
508 if ( scroll_height < 0 ) {
513 sy = ( window.screenTop ? window.screenTop : -y );
514 } else if ( y < -scroll_height ) {
515 sy = -y - scroll_height;
520 tfunc = function () {
521 var elapsed = getCurrentTime() - start;
523 if ( elapsed >= duration ) {
524 window.scrollTo( 0, top + sy );
525 self._outerScrolling = undefined;
529 ec = $.easing.easeOutQuad( elapsed / duration,
530 elapsed, 0, 1, duration );
532 window.scrollTo( 0, top + ( sy * ec ) );
533 self._outerScrolling = setTimeout( tfunc, self._timerInterval );
536 this._outerScrolling = setTimeout( tfunc, self._timerInterval );
539 _scrollTo: function ( x, y, duration ) {
541 start = getCurrentTime(),
542 efunc = $.easing.easeOutQuad,
552 tfunc = function () {
553 var elapsed = getCurrentTime() - start,
556 if ( elapsed >= duration ) {
558 self._setScrollPosition( x, y );
560 ec = efunc( elapsed / duration, elapsed, 0, 1, duration );
562 self._setScrollPosition( sx + ( dx * ec ), sy + ( dy * ec ) );
563 self._timerID = setTimeout( tfunc, self._timerInterval );
567 this._timerID = setTimeout( tfunc, this._timerInterval );
570 scrollTo: function ( x, y, duration ) {
573 if ( !duration || this.options.scrollMethod === "translate" ) {
574 this._setScrollPosition( x, y, duration );
576 this._scrollTo( x, y, duration );
580 getScrollPosition: function () {
581 return { x: -this._sx, y: -this._sy };
584 _getScrollHierarchy: function () {
588 this._$clip.parents( ".ui-scrollview-clip").each( function () {
589 d = $( this ).jqmData("scrollview");
597 _getAncestorByDirection: function ( dir ) {
598 var svh = this._getScrollHierarchy(),
605 svdir = sv.options.direction;
607 if (!svdir || svdir === dir) {
614 _handleDragStart: function ( e, ex, ey ) {
617 this._didDrag = false;
618 this._skip_dragging = false;
620 var target = $( e.target ),
623 svdir = this.options.direction;
625 /* should prevent the default behavior when click the button */
626 this._is_button = target.is( '.ui-btn-text' ) ||
627 target.is( '.ui-btn-inner' ) ||
628 target.is( '.ui-btn-inner .ui-icon' );
630 if ( this._is_button ) {
631 if ( target.parents('.ui-slider-handle').length ) {
632 this._skip_dragging = true;
638 * We need to prevent the default behavior to
639 * suppress accidental selection of text, etc.
641 this._is_inputbox = target.is(':input') ||
642 target.parents(':input').length > 0;
644 if ( this._is_inputbox ) {
645 target.one( "resize.scrollview", function () {
646 if ( ey > $c.height() ) {
647 self.scrollTo( -ex, self._sy - ey + $c.height(),
648 self.options.snapbackDuration );
653 if ( this.options.eventType === "mouse" && !this._is_inputbox && !this._is_button ) {
660 this._doSnapBackX = false;
661 this._doSnapBackY = false;
664 this._directionLock = "";
667 this._enableTracking();
669 this._set_scrollbar_size();
672 _propagateDragMove: function ( sv, e, ex, ey, dir ) {
673 this._hideScrollBars();
674 this._hideOverflowIndicator();
675 this._disableTracking();
676 sv._handleDragStart( e, ex, ey );
677 sv._directionLock = dir;
678 sv._didDrag = this._didDrag;
681 _handleDragMove: function ( e, ex, ey ) {
682 if ( this._skip_dragging ) {
686 if ( !this._dragging ) {
690 if ( !this._is_inputbox && !this._is_button ) {
694 var mt = this.options.moveThreshold,
695 dx = ex - this._lastX,
696 dy = ey - this._lastY,
697 svdir = this.options.direction,
707 this._lastMove = getCurrentTime();
709 if ( !this._directionLock ) {
713 if ( x < mt && y < mt ) {
717 if ( x < y && (x / y) < 0.5 ) {
719 } else if ( x > y && (y / x) < 0.5 ) {
723 if ( svdir && dir && svdir !== dir ) {
725 * This scrollview can't handle the direction the user
726 * is attempting to scroll. Find an ancestor scrollview
727 * that can handle the request.
730 sv = this._getAncestorByDirection( dir );
732 this._propagateDragMove( sv, e, ex, ey, dir );
737 this._directionLock = svdir || (dir || "none");
742 dirLock = this._directionLock;
744 if ( dirLock !== "y" && this._hTracker ) {
749 this._doSnapBackX = false;
751 scope = ( newX > 0 || newX < this._maxX );
753 if ( scope && dirLock === "x" ) {
754 sv = this._getAncestorByDirection("x");
756 this._setScrollPosition( newX > 0 ?
757 0 : this._maxX, newY );
758 this._propagateDragMove( sv, e, ex, ey, dir );
762 newX = x + ( dx / 2 );
763 this._doSnapBackX = true;
767 if ( dirLock !== "x" && this._vTracker ) {
768 if ( Math.abs( this._startY - ey ) < mt && dirLock !== "xy" ) {
776 this._doSnapBackY = false;
778 scope = ( newY > 0 || newY < this._maxY );
780 if ( scope && dirLock === "y" ) {
781 sv = this._getAncestorByDirection("y");
783 this._setScrollPosition( newX,
784 newY > 0 ? 0 : this._maxY );
785 this._propagateDragMove( sv, e, ex, ey, dir );
789 newY = y + ( dy / 2 );
790 this._doSnapBackY = true;
794 if ( this.options.overshootEnable === false ) {
795 this._doSnapBackX = false;
796 this._doSnapBackY = false;
799 this._didDrag = true;
803 this._setScrollPosition( newX, newY );
805 this._showScrollBars();
806 this._showOverflowIndicator();
809 _handleDragStop: function ( e ) {
810 if ( this._skip_dragging ) {
814 var l = this._lastMove,
815 t = getCurrentTime(),
816 doScroll = (l && (t - l) <= this.options.moveIntervalThreshold),
817 sx = ( this._hTracker && this._speedX && doScroll ) ?
818 this._speedX : ( this._doSnapBackX ? 1 : 0 ),
819 sy = ( this._vTracker && this._speedY && doScroll ) ?
820 this._speedY : ( this._doSnapBackY ? 1 : 0 ),
821 svdir = this.options.direction,
826 if ( !this._setGestureScroll( sx, sy ) ) {
827 this._startMScroll( sx, sy );
830 this._hideScrollBars();
831 this._hideOverflowIndicator();
834 this._disableTracking();
836 if ( this._bouncing ) {
837 this._setBouncing( this._$view, "out" );
838 this._hideScrollBars();
839 this._hideOverflowIndicator();
842 return !this._didDrag;
845 _setGestureScroll: function ( sx, sy ) {
847 reset = function () {
848 clearTimeout( self._gesture_timer );
849 self._gesture_dir = 0;
850 self._gesture_count = 0;
851 self._gesture_timer = undefined;
858 dir = sy > 0 ? 1 : -1;
860 if ( !this._gesture_timer ) {
861 this._gesture_count = 1;
862 this._gesture_dir = dir;
864 this._gesture_timer = setTimeout( function () {
871 if ( this._gesture_dir !== dir ) {
876 this._gesture_count++;
878 if ( this._gesture_count === 3 ) {
880 this.scrollTo( 0, 0, this.options.overshootDuration );
882 this.scrollTo( 0, -( this._getViewHeight() - this._$clip.height() ),
883 this.options.overshootDuration );
893 _enableTracking: function () {
894 this._dragging = true;
897 _disableTracking: function () {
898 this._dragging = false;
901 _showScrollBars: function ( interval ) {
902 var vclass = "ui-scrollbar-visible",
905 if ( !this.options.showScrollBars ) {
908 if ( this._scrollbar_showed ) {
912 if ( this._$vScrollBar ) {
913 this._$vScrollBar.addClass( vclass );
915 if ( this._$hScrollBar ) {
916 this._$hScrollBar.addClass( vclass );
919 this._scrollbar_showed = true;
922 setTimeout( function () {
923 self._hideScrollBars();
928 _hideScrollBars: function () {
929 var vclass = "ui-scrollbar-visible";
931 if ( !this.options.showScrollBars ) {
934 if ( !this._scrollbar_showed ) {
938 if ( this._$vScrollBar ) {
939 this._$vScrollBar.removeClass( vclass );
941 if ( this._$hScrollBar ) {
942 this._$hScrollBar.removeClass( vclass );
945 this._scrollbar_showed = false;
948 _resetOverflowIndicator: function () {
949 if ( !this.options.overflowEnable || !this._overflowAvail ) {
953 this._overflow_top.css( "-webkit-animation", "" );
954 this._overflow_bottom.css( "-webkit-animation", "" );
957 _setOverflowIndicator: function ( dir ) {
959 this._opacity_top = "0.2";
960 this._opacity_bottom = "0.8";
961 } else if ( dir === 0 ) {
962 this._opacity_top = "0.8";
963 this._opacity_bottom = "0.2";
965 this._opacity_top = "0.5";
966 this._opacity_bottom = "0.5";
970 _getOverflowIndicator: function ( opacity ) {
971 if ( opacity === "0.2" ) {
973 } else if ( opacity === "0.8" ) {
979 _showOverflowIndicator: function () {
980 if ( !this.options.overflowEnable || !this._overflowAvail ) {
984 this._overflow_top.css( "opacity", this._opacity_top );
985 this._overflow_bottom.css( "opacity", this._opacity_bottom );
987 if ( this._overflow_showed === true ) {
991 this._overflow_top.css( "-webkit-animation", "ui-overflow-show" +
992 this._getOverflowIndicator( this._opacity_top ) + " 0.3s 1 ease" );
993 this._overflow_bottom.css( "-webkit-animation", "ui-overflow-show" +
994 this._getOverflowIndicator( this._opacity_bottom ) + " 0.3s 1 ease" );
996 this._overflow_showed = true;
999 _hideOverflowIndicator: function () {
1003 if ( !this.options.overflowEnable || !this._overflowAvail ) {
1007 if ( this._overflow_showed === false ) {
1011 opacity_top = this._overflow_top.css( "opacity" );
1012 opacity_bottom = this._overflow_bottom.css( "opacity" );
1014 this._overflow_top.css( "opacity", "0" );
1015 this._overflow_bottom.css( "opacity", "0" );
1017 this._overflow_top.css( "-webkit-animation", "ui-overflow-hide" +
1018 this._getOverflowIndicator( opacity_top ) + " 0.5s 1 ease" );
1019 this._overflow_bottom.css( "-webkit-animation", "ui-overflow-hide" +
1020 this._getOverflowIndicator( opacity_bottom ) + " 0.5s 1 ease" );
1022 this._overflow_showed = false;
1023 this._setOverflowIndicator();
1026 _add_event: function () {
1031 if ( this.options.eventType === "mouse" ) {
1032 this._dragEvt = "mousedown mousemove mouseup click mousewheel";
1034 this._dragCB = function ( e ) {
1037 return self._handleDragStart( e,
1038 e.clientX, e.clientY );
1041 return self._handleDragMove( e,
1042 e.clientX, e.clientY );
1045 return self._handleDragStop( e );
1048 return !self._didDrag;
1051 var old = self.getScrollPosition();
1052 self.scrollTo( -old.x,
1053 -(old.y - e.originalEvent.wheelDelta) );
1058 this._dragEvt = "touchstart touchmove touchend click";
1060 this._dragCB = function ( e ) {
1065 t = e.originalEvent.targetTouches[0];
1066 return self._handleDragStart( e,
1070 t = e.originalEvent.targetTouches[0];
1071 return self._handleDragMove( e,
1075 return self._handleDragStop( e );
1078 return !self._didDrag;
1083 $v.bind( this._dragEvt, this._dragCB );
1085 $c.bind( "updatelayout", function ( e ) {
1088 view_h = self._getViewHeight();
1090 if ( !$c.height() || !view_h ) {
1091 self.scrollTo( 0, 0, 0 );
1095 sy = $c.height() - view_h;
1096 vh = view_h - self._view_height;
1098 self._view_height = view_h;
1100 if ( vh == 0 || vh > $c.height() / 2 ) {
1104 if ( self._sy - sy <= -vh ) {
1105 self.scrollTo( 0, sy,
1106 self.options.snapbackDuration );
1107 } else if ( self._sy - sy <= vh + self.options.moveThreshold ) {
1108 self.scrollTo( 0, sy,
1109 self.options.snapbackDuration );
1113 $( window ).bind( "resize", function ( e ) {
1115 view_h = self._getViewHeight();
1117 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1121 if ( !$c.height() || !view_h ) {
1125 focused = $c.find(".ui-focus");
1128 focused.trigger("resize.scrollview");
1131 /* calibration - after triggered throttledresize */
1132 setTimeout( function () {
1133 if ( self._sy < $c.height() - self._getViewHeight() ) {
1134 self.scrollTo( 0, self._sy,
1135 self.options.snapbackDuration );
1139 self._view_height = view_h;
1142 $c.closest(".ui-page")
1143 .one( "pageshow", function ( e ) {
1144 self._view_offset = self._$view.offset().top - self._$clip.offset().top;
1145 self._view_height = self._getViewHeight();
1147 .bind( "pageshow", function ( e ) {
1148 /* should be called after pagelayout */
1149 setTimeout( function () {
1150 self._set_scrollbar_size();
1151 self._setScrollPosition( self._sx, self._sy );
1152 self._showScrollBars( 2000 );
1153 self._resetOverflowIndicator();
1158 _add_scrollbar: function () {
1159 var $c = this._$clip,
1160 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
1161 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
1163 if ( !this.options.showScrollBars ) {
1167 if ( this._vTracker ) {
1168 $c.append( prefix + "y" + suffix );
1169 this._$vScrollBar = $c.children(".ui-scrollbar-y");
1171 if ( this._hTracker ) {
1172 $c.append( prefix + "x" + suffix );
1173 this._$hScrollBar = $c.children(".ui-scrollbar-x");
1176 this._scrollbar_showed = false;
1179 _add_scroll_jump: function () {
1180 var $c = this._$clip,
1185 if ( !this.options.scrollJump ) {
1189 if ( this._vTracker ) {
1190 top_btn = $( '<div class="ui-scroll-jump-top-bg">' +
1191 '<div data-role="button" data-inline="true" data-icon="jumptop" style="width:37px;height:37px">.</div></div>' );
1192 $c.append( top_btn ).trigger("create");
1194 top_btn.bind( "vclick", function () {
1195 self.scrollTo( 0, 0, self.options.overshootDuration );
1199 if ( this._hTracker ) {
1200 left_btn = $( '<div class="ui-scroll-jump-left-bg">' +
1201 '<div data-role="button" data-inline="true" data-icon="jumpleft" style="width:37px;height:37px">.</div></div>' );
1202 $c.append( left_btn ).trigger("create");
1204 left_btn.bind( "vclick", function () {
1205 self.scrollTo( 0, 0, self.options.overshootDuration );
1210 _add_overflow_indicator: function () {
1211 if ( !this.options.overflowEnable ) {
1215 this._overflow_top = $( '<div class="ui-overflow-indicator-top"></div>' );
1216 this._overflow_bottom = $( '<div class="ui-overflow-indicator-bottom"></div>' );
1218 this._$clip.append( this._overflow_top );
1219 this._$clip.append( this._overflow_bottom );
1221 this._opacity_top = "0.5";
1222 this._opacity_bottom = "0.5";
1223 this._overflow_showed = false;
1226 _set_scrollbar_size: function () {
1227 var $c = this._$clip,
1235 if ( !this.options.showScrollBars ) {
1239 if ( this._hTracker ) {
1242 this._maxX = cw - vw;
1244 if ( this._maxX > 0 ) {
1247 if ( this._$hScrollBar && vw ) {
1248 thumb = this._$hScrollBar.find(".ui-scrollbar-thumb");
1249 thumb.css( "width", (cw >= vw ? "0" :
1250 (Math.floor(cw / vw * 100) || 1) + "%") );
1254 if ( this._vTracker ) {
1256 vh = this._getViewHeight();
1257 this._maxY = ch - vh;
1259 if ( this._maxY > 0 ) {
1262 if ( this._$vScrollBar && vh ) {
1263 thumb = this._$vScrollBar.find(".ui-scrollbar-thumb");
1264 thumb.css( "height", (ch >= vh ? "0" :
1265 (Math.floor(ch / vh * 100) || 1) + "%") );
1267 this._overflowAvail = !!thumb.height();
1273 $.extend( MomentumTracker.prototype, {
1274 start: function ( pos, speed, duration, minPos, maxPos ) {
1275 var tstate = ( pos < minPos || pos > maxPos ) ?
1276 tstates.snapback : tstates.scrolling,
1279 this.state = ( speed !== 0 ) ? tstate : tstates.done;
1282 this.duration = ( this.state === tstates.snapback ) ?
1283 this.options.snapbackDuration : duration;
1284 this.minPos = minPos;
1285 this.maxPos = maxPos;
1287 this.fromPos = ( this.state === tstates.snapback ) ? this.pos : 0;
1288 pos_temp = ( this.pos < this.minPos ) ? this.minPos : this.maxPos;
1289 this.toPos = ( this.state === tstates.snapback ) ? pos_temp : 0;
1291 this.startTime = getCurrentTime();
1294 reset: function () {
1295 this.state = tstates.done;
1304 update: function ( overshootEnable ) {
1305 var state = this.state,
1306 cur_time = getCurrentTime(),
1307 duration = this.duration,
1308 elapsed = cur_time - this.startTime,
1313 if ( state === tstates.done ) {
1317 elapsed = elapsed > duration ? duration : elapsed;
1319 this.remained = duration - elapsed;
1321 if ( state === tstates.scrolling || state === tstates.overshot ) {
1323 ( 1 - $.easing[this.easing]( elapsed / duration,
1324 elapsed, 0, 1, duration ) );
1328 didOverShoot = ( state === tstates.scrolling ) &&
1329 ( x < this.minPos || x > this.maxPos );
1331 if ( didOverShoot ) {
1332 x = ( x < this.minPos ) ? this.minPos : this.maxPos;
1337 if ( state === tstates.overshot ) {
1338 if ( !overshootEnable ) {
1339 this.state = tstates.done;
1341 if ( elapsed >= duration ) {
1342 this.state = tstates.snapback;
1343 this.fromPos = this.pos;
1344 this.toPos = ( x < this.minPos ) ?
1345 this.minPos : this.maxPos;
1346 this.duration = this.options.snapbackDuration;
1347 this.startTime = cur_time;
1350 } else if ( state === tstates.scrolling ) {
1351 if ( didOverShoot && overshootEnable ) {
1352 this.state = tstates.overshot;
1353 this.speed = dx / 2;
1354 this.duration = this.options.overshootDuration;
1355 this.startTime = cur_time;
1356 } else if ( elapsed >= duration ) {
1357 this.state = tstates.done;
1360 } else if ( state === tstates.snapback ) {
1361 if ( elapsed >= duration ) {
1362 this.pos = this.toPos;
1363 this.state = tstates.done;
1365 this.pos = this.fromPos + (( this.toPos - this.fromPos ) *
1366 $.easing[this.easing]( elapsed / duration,
1367 elapsed, 0, 1, duration ));
1375 return this.state === tstates.done;
1378 isMin: function () {
1379 return this.pos === this.minPos;
1382 isMax: function () {
1383 return this.pos === this.maxPos;
1386 getRemained: function () {
1387 return this.remained;
1390 getPosition: function () {
1395 $( document ).bind( 'pagecreate create', function ( e ) {
1396 var $page = $( e.target ),
1397 content_scroll = $page.find(".ui-content").jqmData("scroll");
1399 /* content scroll */
1400 if ( $.support.scrollview === undefined ) {
1401 $.support.scrollview = true;
1404 if ( $.support.scrollview === true && content_scroll === undefined ) {
1405 content_scroll = "y";
1408 if ( content_scroll !== "y" ) {
1409 content_scroll = "none";
1412 $page.find(".ui-content").attr( "data-scroll", content_scroll );
1414 $page.find(":jqmData(scroll):not(.ui-scrollview-clip)").each( function () {
1415 if ( $( this ).hasClass("ui-scrolllistview") ) {
1416 $( this ).scrolllistview();
1418 var st = $( this ).jqmData("scroll"),
1419 dir = st && ( st.search(/^[xy]/) !== -1 ) ? st : null,
1420 content = $(this).hasClass("ui-content"),
1423 if ( st === "none" ) {
1428 direction: dir || undefined,
1429 overflowEnable: content,
1430 scrollMethod: $( this ).jqmData("scroll-method") || undefined,
1431 scrollJump: $( this ).jqmData("scroll-jump") || undefined
1434 $( this ).scrollview( opts );
1439 $( document ).bind( 'pageshow', function ( e ) {
1440 var $page = $( e.target ),
1441 scroll = $page.find(".ui-content").jqmData("scroll");
1443 if ( scroll === "y" ) {
1444 resizePageContentHeight( e.target );
1448 }( jQuery, window, document ) );