3 * jQuery Mobile Framework : scrollview plugin
4 * Copyright (c) 2010 Adobe Systems Incorporated - Kin Blas (jblas@adobe.com)
5 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
6 * Note: Code is in draft form and is subject to change
7 * Modified by Koeun Choi <koeun.choi@samsung.com>
8 * Modified by Minkyu Kang <mk7.kang@samsung.com>
11 (function ( $, window, document, undefined ) {
13 function resizePageContentHeight( page ) {
14 var $page = $( page ),
15 $content = $page.children(".ui-content"),
16 hh = $page.children(".ui-header").outerHeight() || 0,
17 fh = $page.children(".ui-footer").outerHeight() || 0,
18 pt = parseFloat( $content.css("padding-top") ),
19 pb = parseFloat( $content.css("padding-bottom") ),
20 wh = $( window ).height();
22 $content.height( wh - (hh + fh) - (pt + pb) );
25 function MomentumTracker( options ) {
26 this.options = $.extend( {}, options );
27 this.easing = "easeOutQuad";
38 function getCurrentTime() {
42 jQuery.widget( "tizen.scrollview", jQuery.mobile.widget, {
44 direction: null, // "x", "y", or null for both.
47 scrollDuration: 1000, // Duration of the scrolling animation in msecs.
48 overshootDuration: 250, // Duration of the overshoot animation in msecs.
49 snapbackDuration: 500, // Duration of the snapback animation in msecs.
51 moveThreshold: 30, // User must move this many pixels in any direction to trigger a scroll.
52 moveIntervalThreshold: 150, // Time between mousemoves must not exceed this threshold.
54 scrollMethod: "translate", // "translate", "position"
55 startEventName: "scrollstart",
56 updateEventName: "scrollupdate",
57 stopEventName: "scrollstop",
59 eventType: $.support.touch ? "touch" : "mouse",
62 overshootEnable: false,
63 outerScrollEnable: false,
68 _getViewHeight: function () {
69 return this._$view.height();
72 _getViewWidth: function () {
73 return this._$view.width();
76 _makePositioned: function ( $ele ) {
77 if ( $ele.css("position") === "static" ) {
78 $ele.css( "position", "relative" );
82 _create: function () {
86 this._$clip = $( this.element ).addClass("ui-scrollview-clip");
88 if ( this._$clip.children(".ui-scrollview-view").length ) {
89 this._$view = this._$clip.children(".ui-scrollview-view");
91 this._$view = this._$clip.wrapInner("<div></div>").children()
92 .addClass("ui-scrollview-view");
95 if ( this.options.scrollMethod === "translate" ) {
96 if ( this._$view.css("transform") === undefined ) {
97 this.options.scrollMethod = "position";
101 this._$clip.css( "overflow", "hidden" );
102 this._makePositioned( this._$clip );
104 this._makePositioned( this._$view );
105 this._$view.css( { left: 0, top: 0 } );
107 this._view_height = this._getViewHeight();
112 direction = this.options.direction;
114 this._hTracker = ( direction !== "y" ) ?
115 new MomentumTracker( this.options ) : null;
116 this._vTracker = ( direction !== "x" ) ?
117 new MomentumTracker( this.options ) : null;
119 this._timerInterval = this.options.timerInterval;
122 this._timerCB = function () {
123 self._handleMomentumScroll();
127 this._add_scrollbar();
128 this._add_scroll_jump();
129 this._add_overflow_indicator();
132 _startMScroll: function ( speedX, speedY ) {
133 var keepGoing = false,
134 duration = this.options.scrollDuration,
140 this._$clip.trigger( this.options.startEventName );
143 c = this._$clip.width();
144 v = this._getViewWidth();
146 if ( (( this._sx === 0 && speedX > 0 ) ||
147 ( this._sx === -(v - c) && speedX < 0 )) &&
152 ht.start( this._sx, speedX,
153 duration, (v > c) ? -(v - c) : 0, 0 );
154 keepGoing = !ht.done();
158 c = this._$clip.height();
159 v = this._getViewHeight();
161 if ( (( this._sy === 0 && speedY > 0 ) ||
162 ( this._sy === -(v - c) && speedY < 0 )) &&
167 vt.start( this._sy, speedY,
168 duration, (v > c) ? -(v - c) : 0, 0 );
169 keepGoing = keepGoing || !vt.done();
173 this._timerID = setTimeout( this._timerCB, this._timerInterval );
179 _stopMScroll: function () {
180 if ( this._timerID ) {
181 this._$clip.trigger( this.options.stopEventName );
182 clearTimeout( this._timerID );
186 if ( this._vTracker ) {
187 this._vTracker.reset();
190 if ( this._hTracker ) {
191 this._hTracker.reset();
194 this._hideScrollBars();
195 this._hideOverflowIndicator();
198 _handleMomentumScroll: function () {
199 var keepGoing = false,
207 if ( this._outerScrolling ) {
212 vt.update( this.options.overshootEnable );
213 y = vt.getPosition();
214 keepGoing = !vt.done();
216 if ( vt.getRemained() > this.options.overshootDuration ) {
217 scroll_height = this._getViewHeight() - this._$clip.height();
219 if ( !vt.isAvail() ) {
220 if ( this._speedY > 0 ) {
221 this._outerScroll( vt.getRemained() / 3, scroll_height );
223 this._outerScroll( y - vt.getRemained() / 3, scroll_height );
225 } else if ( vt.isMin() ) {
226 this._outerScroll( y - vt.getRemained() / 3, scroll_height );
228 } else if ( vt.isMax() ) {
229 this._outerScroll( vt.getRemained() / 3, scroll_height );
235 ht.update( this.options.overshootEnable );
236 x = ht.getPosition();
237 keepGoing = keepGoing || !ht.done();
240 this._setScrollPosition( x, y );
241 this._$clip.trigger( this.options.updateEventName,
242 [ { x: x, y: y } ] );
245 this._timerID = setTimeout( this._timerCB, this._timerInterval );
251 _setElementTransform: function ( $ele, x, y, duration ) {
255 if ( !duration || duration === undefined ) {
258 transition = "-webkit-transform " + duration / 1000 + "s ease-out";
261 if ( $.support.cssTransform3d ) {
262 translate = "translate3d(" + x + "," + y + ", 0px)";
264 translate = "translate(" + x + "," + y + ")";
268 "-moz-transform": translate,
269 "-webkit-transform": translate,
270 "-ms-transform": translate,
271 "-o-transform": translate,
272 "transform": translate,
273 "-webkit-transition": transition
277 _setEndEffect: function ( dir ) {
278 var scroll_height = this._getViewHeight() - this._$clip.height();
280 if ( this._softkeyboard ) {
281 if ( this._effect_dir ) {
282 this._outerScroll( -scroll_height - this._softkeyboardHeight,
285 this._outerScroll( this._softkeyboardHeight, scroll_height );
290 if ( dir === "in" ) {
291 if ( this._endEffect ) {
295 this._endEffect = true;
296 this._setOverflowIndicator( this._effect_dir );
297 this._showOverflowIndicator();
298 } else if ( dir === "out" ) {
299 if ( !this._endEffect ) {
303 this._endEffect = false;
305 this._endEffect = false;
306 this._setOverflowIndicator();
307 this._showOverflowIndicator();
311 _setCalibration: function ( x, y ) {
312 if ( this.options.overshootEnable ) {
318 var $v = this._$view,
320 dirLock = this._directionLock,
324 if ( dirLock !== "y" && this._hTracker ) {
325 scroll_width = $v.width() - $c.width();
329 } else if ( x < -scroll_width ) {
330 this._sx = -scroll_width;
335 if ( scroll_width < 0 ) {
340 if ( dirLock !== "x" && this._vTracker ) {
341 scroll_height = this._getViewHeight() - $c.height();
346 this._effect_dir = 0;
347 this._setEndEffect( "in" );
348 } else if ( y < -scroll_height ) {
349 this._sy = -scroll_height;
351 this._effect_dir = 1;
352 this._setEndEffect( "in" );
354 if ( this._endEffect && this._sy !== y ) {
355 this._setEndEffect();
361 if ( scroll_height < 0 ) {
367 _setScrollPosition: function ( x, y, duration ) {
368 var $v = this._$view,
369 sm = this.options.scrollMethod,
370 $vsb = this._$vScrollBar,
371 $hsb = this._$hScrollBar,
374 this._setCalibration( x, y );
379 if ( sm === "translate" ) {
380 this._setElementTransform( $v, x + "px", y + "px", duration );
382 $v.css( {left: x + "px", top: y + "px"} );
386 $sbt = $vsb.find(".ui-scrollbar-thumb");
388 if ( sm === "translate" ) {
389 this._setElementTransform( $sbt, "0px",
390 -y / this._getViewHeight() * $sbt.parent().height() + "px",
393 $sbt.css( "top", -y / this._getViewHeight() * 100 + "%" );
398 $sbt = $hsb.find(".ui-scrollbar-thumb");
400 if ( sm === "translate" ) {
401 this._setElementTransform( $sbt,
402 -x / $v.width() * $sbt.parent().width() + "px", "0px",
405 $sbt.css("left", -x / $v.width() * 100 + "%");
410 _outerScroll: function ( y, scroll_height ) {
412 top = $( window ).scrollTop() - window.screenTop,
414 duration = this.options.snapbackDuration,
415 start = getCurrentTime(),
418 if ( !this.options.outerScrollEnable ) {
422 if ( this._$clip.jqmData("scroll") !== "y" ) {
426 if ( this._outerScrolling ) {
431 sy = ( window.screenTop ? window.screenTop : -y );
432 } else if ( y < -scroll_height ) {
433 sy = -y - scroll_height;
438 tfunc = function () {
439 var elapsed = getCurrentTime() - start;
441 if ( elapsed >= duration ) {
442 window.scrollTo( 0, top + sy );
443 self._outerScrolling = undefined;
447 ec = $.easing.easeOutQuad( elapsed / duration,
448 elapsed, 0, 1, duration );
450 window.scrollTo( 0, top + ( sy * ec ) );
451 self._outerScrolling = setTimeout( tfunc, self._timerInterval );
454 this._outerScrolling = setTimeout( tfunc, self._timerInterval );
457 _scrollTo: function ( x, y, duration ) {
459 start = getCurrentTime(),
460 efunc = $.easing.easeOutQuad,
470 tfunc = function () {
471 var elapsed = getCurrentTime() - start,
474 if ( elapsed >= duration ) {
476 self._setScrollPosition( x, y );
478 ec = efunc( elapsed / duration, elapsed, 0, 1, duration );
480 self._setScrollPosition( sx + ( dx * ec ), sy + ( dy * ec ) );
481 self._timerID = setTimeout( tfunc, self._timerInterval );
485 this._timerID = setTimeout( tfunc, this._timerInterval );
488 scrollTo: function ( x, y, duration ) {
490 this._didDrag = false;
492 if ( !duration || this.options.scrollMethod === "translate" ) {
493 this._setScrollPosition( x, y, duration );
495 this._scrollTo( x, y, duration );
499 getScrollPosition: function () {
500 return { x: -this._sx, y: -this._sy };
503 skipDragging: function ( value ) {
504 this._skip_dragging = value;
507 _getScrollHierarchy: function () {
511 this._$clip.parents( ".ui-scrollview-clip").each( function () {
512 d = $( this ).jqmData("scrollview");
520 _getAncestorByDirection: function ( dir ) {
521 var svh = this._getScrollHierarchy(),
528 svdir = sv.options.direction;
530 if (!svdir || svdir === dir) {
537 _handleDragStart: function ( e, ex, ey ) {
540 this._didDrag = false;
541 this._skip_dragging = false;
543 var target = $( e.target ),
546 svdir = this.options.direction;
548 /* should prevent the default behavior when click the button */
549 this._is_button = target.is( '.ui-btn' ) ||
550 target.is( '.ui-btn-text' ) ||
551 target.is( '.ui-btn-inner' ) ||
552 target.is( '.ui-btn-inner .ui-icon' );
554 /* should prevent the default behavior when click the slider */
555 if ( target.parents('.ui-slider').length || target.is('.ui-slider') ) {
556 this._skip_dragging = true;
560 if ( target.is('textarea') ) {
561 target.bind( "scroll", function () {
562 self._skip_dragging = true;
563 target.unbind("scroll");
568 * We need to prevent the default behavior to
569 * suppress accidental selection of text, etc.
571 this._is_inputbox = target.is(':input') ||
572 target.parents(':input').length > 0;
574 if ( this._is_inputbox ) {
575 target.one( "resize.scrollview", function () {
576 if ( ey > $c.height() ) {
577 self.scrollTo( -ex, self._sy - ey + $c.height(),
578 self.options.snapbackDuration );
583 if ( this.options.eventType === "mouse" && !this._is_inputbox && !this._is_button ) {
590 this._doSnapBackX = false;
591 this._doSnapBackY = false;
594 this._directionLock = "";
597 this._enableTracking();
599 this._set_scrollbar_size();
602 _propagateDragMove: function ( sv, e, ex, ey, dir ) {
603 this._hideScrollBars();
604 this._hideOverflowIndicator();
605 this._disableTracking();
606 sv._handleDragStart( e, ex, ey );
607 sv._directionLock = dir;
608 sv._didDrag = this._didDrag;
611 _handleDragMove: function ( e, ex, ey ) {
612 if ( this._skip_dragging ) {
616 if ( !this._dragging ) {
620 if ( !this._is_inputbox && !this._is_button ) {
624 var mt = this.options.moveThreshold,
625 dx = ex - this._lastX,
626 dy = ey - this._lastY,
627 svdir = this.options.direction,
637 this._lastMove = getCurrentTime();
639 if ( !this._directionLock ) {
643 if ( x < mt && y < mt ) {
647 if ( x < y && (x / y) < 0.5 ) {
649 } else if ( x > y && (y / x) < 0.5 ) {
653 if ( svdir && dir && svdir !== dir ) {
655 * This scrollview can't handle the direction the user
656 * is attempting to scroll. Find an ancestor scrollview
657 * that can handle the request.
660 sv = this._getAncestorByDirection( dir );
662 this._propagateDragMove( sv, e, ex, ey, dir );
667 this._directionLock = svdir || (dir || "none");
672 dirLock = this._directionLock;
674 if ( dirLock !== "y" && this._hTracker ) {
679 this._doSnapBackX = false;
681 scope = ( newX > 0 || newX < this._maxX );
683 if ( scope && dirLock === "x" ) {
684 sv = this._getAncestorByDirection("x");
686 this._setScrollPosition( newX > 0 ?
687 0 : this._maxX, newY );
688 this._propagateDragMove( sv, e, ex, ey, dir );
692 newX = x + ( dx / 2 );
693 this._doSnapBackX = true;
697 if ( dirLock !== "x" && this._vTracker ) {
698 if ( Math.abs( this._startY - ey ) < mt && dirLock !== "xy" ) {
706 this._doSnapBackY = false;
708 scope = ( newY > 0 || newY < this._maxY );
710 if ( scope && dirLock === "y" ) {
711 sv = this._getAncestorByDirection("y");
713 this._setScrollPosition( newX,
714 newY > 0 ? 0 : this._maxY );
715 this._propagateDragMove( sv, e, ex, ey, dir );
719 newY = y + ( dy / 2 );
720 this._doSnapBackY = true;
724 if ( this.options.overshootEnable === false ) {
725 this._doSnapBackX = false;
726 this._doSnapBackY = false;
732 this._setScrollPosition( newX, newY );
734 if ( this._didDrag === false ) {
735 this._didDrag = true;
736 this._showScrollBars();
737 this._showOverflowIndicator();
739 this._$clip.parents(".ui-scrollview-clip").each( function () {
740 $( this ).scrollview( "skipDragging", true );
745 _handleDragStop: function ( e ) {
748 if ( this._skip_dragging ) {
752 var l = this._lastMove,
753 t = getCurrentTime(),
754 doScroll = (l && (t - l) <= this.options.moveIntervalThreshold),
755 sx = ( this._hTracker && this._speedX && doScroll ) ?
756 this._speedX : ( this._doSnapBackX ? 1 : 0 ),
757 sy = ( this._vTracker && this._speedY && doScroll ) ?
758 this._speedY : ( this._doSnapBackY ? 1 : 0 ),
759 svdir = this.options.direction,
764 if ( !this._setGestureScroll( sx, sy ) ) {
765 this._startMScroll( sx, sy );
768 this._hideScrollBars();
769 this._hideOverflowIndicator();
772 this._disableTracking();
774 if ( this._endEffect ) {
775 setTimeout( function () {
776 self._setEndEffect( "out" );
777 self._hideScrollBars();
778 self._hideOverflowIndicator();
782 return !this._didDrag;
785 _setGestureScroll: function ( sx, sy ) {
787 reset = function () {
788 clearTimeout( self._gesture_timer );
789 self._gesture_dir = 0;
790 self._gesture_timer = undefined;
803 if ( Math.abs( sx ) > Math.abs( sy ) ) {
804 dir = sx > 0 ? direction.left : direction.right;
806 dir = sy > 0 ? direction.top : direction.bottom;
809 if ( !this._gesture_timer ) {
810 this._gesture_dir = dir;
812 this._gesture_timer = setTimeout( function () {
819 if ( this._gesture_dir !== dir ) {
827 _enableTracking: function () {
828 this._dragging = true;
831 _disableTracking: function () {
832 this._dragging = false;
835 _showScrollBars: function ( interval ) {
836 var vclass = "ui-scrollbar-visible",
839 if ( !this.options.showScrollBars ) {
842 if ( this._scrollbar_showed ) {
846 if ( this._$vScrollBar ) {
847 this._$vScrollBar.addClass( vclass );
849 if ( this._$hScrollBar ) {
850 this._$hScrollBar.addClass( vclass );
853 this._scrollbar_showed = true;
856 setTimeout( function () {
857 self._hideScrollBars();
862 _hideScrollBars: function () {
863 var vclass = "ui-scrollbar-visible";
865 if ( !this.options.showScrollBars ) {
868 if ( !this._scrollbar_showed ) {
872 if ( this._$vScrollBar ) {
873 this._$vScrollBar.removeClass( vclass );
875 if ( this._$hScrollBar ) {
876 this._$hScrollBar.removeClass( vclass );
879 this._scrollbar_showed = false;
882 _setOverflowIndicator: function ( dir ) {
884 this._opacity_top = "0";
885 this._opacity_bottom = "0.8";
886 } else if ( dir === 0 ) {
887 this._opacity_top = "0.8";
888 this._opacity_bottom = "0";
890 this._opacity_top = "0.5";
891 this._opacity_bottom = "0.5";
895 _showOverflowIndicator: function () {
896 if ( !this.options.overflowEnable || !this._overflowAvail || this._softkeyboard ) {
900 this._overflow_top.animate( { opacity: this._opacity_top }, 300 );
901 this._overflow_bottom.animate( { opacity: this._opacity_bottom }, 300 );
903 this._overflow_showed = true;
906 _hideOverflowIndicator: function () {
907 if ( !this.options.overflowEnable || !this._overflowAvail || this._softkeyboard ) {
911 if ( this._overflow_showed === false ) {
915 this._overflow_top.animate( { opacity: 0 }, 300 );
916 this._overflow_bottom.animate( { opacity: 0 }, 300 );
918 this._overflow_showed = false;
919 this._setOverflowIndicator();
922 _add_event: function () {
927 if ( this.options.eventType === "mouse" ) {
928 this._dragEvt = "mousedown mousemove mouseup click mousewheel";
930 this._dragCB = function ( e ) {
933 return self._handleDragStart( e,
934 e.clientX, e.clientY );
937 return self._handleDragMove( e,
938 e.clientX, e.clientY );
941 return self._handleDragStop( e );
944 return !self._didDrag;
947 var old = self.getScrollPosition();
948 self.scrollTo( -old.x,
949 -(old.y - e.originalEvent.wheelDelta) );
954 this._dragEvt = "touchstart touchmove touchend click";
956 this._dragCB = function ( e ) {
957 var touches = e.originalEvent.touches;
961 if ( touches.length != 1) {
965 return self._handleDragStart( e,
966 touches[0].pageX, touches[0].pageY );
969 if ( touches.length != 1) {
973 return self._handleDragMove( e,
974 touches[0].pageX, touches[0].pageY );
977 if ( touches.length != 0) {
981 return self._handleDragStop( e );
984 return !self._didDrag;
989 $v.bind( this._dragEvt, this._dragCB );
991 $v.bind( "keydown", function ( e ) {
994 scroll_top = $( window ).scrollTop() - window.screenTop,
997 if ( e.keyCode == 9 ) {
1001 elem = $c.find(".ui-focus");
1003 if ( elem === undefined ) {
1007 elem_top = elem.offset().top - scroll_top;
1008 screen_h = $c.offset().top + $c.height() - elem.height();
1010 if ( self._softkeyboard ) {
1011 screen_h -= self._softkeyboardHeight;
1014 if ( ( elem_top < $c.offset().top ) || ( elem_top > screen_h ) ) {
1015 self.scrollTo( 0, self._sy -
1016 ( elem_top - $c.offset().top - elem.height() ) );
1022 $v.bind( "keyup", function ( e ) {
1026 scroll_top = $( window ).scrollTop() - window.screenTop,
1029 if ( e.keyCode != 9 ) {
1035 input = $( this ).find(":input");
1037 for ( i = 0; i < input.length; i++ ) {
1038 if ( !$( input[i] ).hasClass("ui-focus") ) {
1042 if ( i + 1 == input.length ) {
1043 elem = $( input[0] );
1045 elem = $( input[i + 1] );
1048 elem_top = elem.offset().top - scroll_top;
1049 screen_h = $c.offset().top + $c.height() - elem.height();
1051 if ( self._softkeyboard ) {
1052 screen_h -= self._softkeyboardHeight;
1055 if ( ( elem_top < 0 ) || ( elem_top > screen_h ) ) {
1056 self.scrollTo( 0, self._sy - elem_top +
1057 elem.height() + $c.offset().top, 0);
1068 $c.bind( "updatelayout", function ( e ) {
1071 view_h = self._getViewHeight();
1073 if ( !$c.height() || !view_h ) {
1074 self.scrollTo( 0, 0, 0 );
1078 sy = $c.height() - view_h;
1079 vh = view_h - self._view_height;
1081 self._view_height = view_h;
1083 if ( vh == 0 || vh > $c.height() / 2 ) {
1088 self.scrollTo( 0, 0, 0 );
1089 } else if ( self._sy - sy <= -vh ) {
1090 self.scrollTo( 0, self._sy,
1091 self.options.snapbackDuration );
1092 } else if ( self._sy - sy <= vh + self.options.moveThreshold ) {
1093 self.scrollTo( 0, sy,
1094 self.options.snapbackDuration );
1098 $( window ).bind( "resize", function ( e ) {
1100 view_h = self._getViewHeight();
1102 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1106 if ( !$c.height() || !view_h ) {
1110 focused = $c.find(".ui-focus");
1113 focused.trigger("resize.scrollview");
1116 /* calibration - after triggered throttledresize */
1117 setTimeout( function () {
1118 if ( self._sy < $c.height() - self._getViewHeight() ) {
1119 self.scrollTo( 0, $c.height() - self._getViewHeight(),
1120 self.options.overshootDuration );
1124 self._view_height = view_h;
1127 $( window ).bind( "vmouseout", function ( e ) {
1128 var drag_stop = false;
1130 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1134 if ( !self._dragging ) {
1138 if ( e.pageX < 0 || e.pageX > $( window ).width() ) {
1142 if ( e.pageY < 0 || e.pageY > $( window ).height() ) {
1147 self._hideScrollBars();
1148 self._hideOverflowIndicator();
1149 self._disableTracking();
1153 this._softkeyboard = false;
1154 this._softkeyboardHeight = 0;
1156 window.addEventListener( "softkeyboardchange", function ( e ) {
1157 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1161 self._softkeyboard = ( e.state === "on" ? true : false );
1162 self._softkeyboardHeight = parseInt( e.height ) *
1163 ( $( window ).width() / window.screen.availWidth );
1166 $c.closest(".ui-page")
1167 .bind( "pageshow", function ( e ) {
1168 /* should be called after pagelayout */
1169 setTimeout( function () {
1170 self._view_height = self._getViewHeight();
1171 self._set_scrollbar_size();
1172 self._setScrollPosition( self._sx, self._sy );
1173 self._showScrollBars( 2000 );
1178 _add_scrollbar: function () {
1179 var $c = this._$clip,
1180 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
1181 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
1183 if ( !this.options.showScrollBars ) {
1187 if ( this._vTracker ) {
1188 $c.append( prefix + "y" + suffix );
1189 this._$vScrollBar = $c.children(".ui-scrollbar-y");
1191 if ( this._hTracker ) {
1192 $c.append( prefix + "x" + suffix );
1193 this._$hScrollBar = $c.children(".ui-scrollbar-x");
1196 this._scrollbar_showed = false;
1199 _add_scroll_jump: function () {
1200 var $c = this._$clip,
1205 if ( !this.options.scrollJump ) {
1209 if ( this._vTracker ) {
1210 top_btn = $( '<div class="ui-scroll-jump-top-bg">' +
1211 '<div data-role="button" data-inline="true" data-icon="scrolltop" data-style="box"></div></div>' );
1212 $c.append( top_btn ).trigger("create");
1214 top_btn.bind( "vclick", function () {
1215 self.scrollTo( 0, 0, self.options.overshootDuration );
1219 if ( this._hTracker ) {
1220 left_btn = $( '<div class="ui-scroll-jump-left-bg">' +
1221 '<div data-role="button" data-inline="true" data-icon="scrollleft" data-style="box"></div></div>' );
1222 $c.append( left_btn ).trigger("create");
1224 left_btn.bind( "vclick", function () {
1225 self.scrollTo( 0, 0, self.options.overshootDuration );
1230 _add_overflow_indicator: function () {
1231 if ( !this.options.overflowEnable ) {
1235 this._overflow_top = $( '<div class="ui-overflow-indicator-top"></div>' );
1236 this._overflow_bottom = $( '<div class="ui-overflow-indicator-bottom"></div>' );
1238 this._$clip.append( this._overflow_top );
1239 this._$clip.append( this._overflow_bottom );
1241 this._opacity_top = "0.5";
1242 this._opacity_bottom = "0.5";
1243 this._overflow_showed = false;
1246 _set_scrollbar_size: function () {
1247 var $c = this._$clip,
1255 if ( !this.options.showScrollBars ) {
1259 if ( this._hTracker ) {
1262 this._maxX = cw - vw;
1264 if ( this._maxX > 0 ) {
1267 if ( this._$hScrollBar && vw ) {
1268 thumb = this._$hScrollBar.find(".ui-scrollbar-thumb");
1269 thumb.css( "width", (cw >= vw ? "0" :
1270 (Math.floor(cw / vw * 100) || 1) + "%") );
1274 if ( this._vTracker ) {
1276 vh = this._getViewHeight();
1277 this._maxY = ch - vh;
1279 if ( this._maxY > 0 || vh === 0 ) {
1282 if ( ( this._$vScrollBar && vh ) || vh === 0 ) {
1283 thumb = this._$vScrollBar.find(".ui-scrollbar-thumb");
1284 thumb.css( "height", (ch >= vh ? "0" :
1285 (Math.floor(ch / vh * 100) || 1) + "%") );
1287 this._overflowAvail = !!thumb.height();
1293 $.extend( MomentumTracker.prototype, {
1294 start: function ( pos, speed, duration, minPos, maxPos ) {
1295 var tstate = ( pos < minPos || pos > maxPos ) ?
1296 tstates.snapback : tstates.scrolling,
1299 this.state = ( speed !== 0 ) ? tstate : tstates.done;
1302 this.duration = ( this.state === tstates.snapback ) ?
1303 this.options.snapbackDuration : duration;
1304 this.minPos = minPos;
1305 this.maxPos = maxPos;
1307 this.fromPos = ( this.state === tstates.snapback ) ? this.pos : 0;
1308 pos_temp = ( this.pos < this.minPos ) ? this.minPos : this.maxPos;
1309 this.toPos = ( this.state === tstates.snapback ) ? pos_temp : 0;
1311 this.startTime = getCurrentTime();
1314 reset: function () {
1315 this.state = tstates.done;
1324 update: function ( overshootEnable ) {
1325 var state = this.state,
1326 cur_time = getCurrentTime(),
1327 duration = this.duration,
1328 elapsed = cur_time - this.startTime,
1333 if ( state === tstates.done ) {
1337 elapsed = elapsed > duration ? duration : elapsed;
1339 this.remained = duration - elapsed;
1341 if ( state === tstates.scrolling || state === tstates.overshot ) {
1343 ( 1 - $.easing[this.easing]( elapsed / duration,
1344 elapsed, 0, 1, duration ) );
1348 didOverShoot = ( state === tstates.scrolling ) &&
1349 ( x < this.minPos || x > this.maxPos );
1351 if ( didOverShoot ) {
1352 x = ( x < this.minPos ) ? this.minPos : this.maxPos;
1357 if ( state === tstates.overshot ) {
1358 if ( !overshootEnable ) {
1359 this.state = tstates.done;
1361 if ( elapsed >= duration ) {
1362 this.state = tstates.snapback;
1363 this.fromPos = this.pos;
1364 this.toPos = ( x < this.minPos ) ?
1365 this.minPos : this.maxPos;
1366 this.duration = this.options.snapbackDuration;
1367 this.startTime = cur_time;
1370 } else if ( state === tstates.scrolling ) {
1371 if ( didOverShoot && overshootEnable ) {
1372 this.state = tstates.overshot;
1373 this.speed = dx / 2;
1374 this.duration = this.options.overshootDuration;
1375 this.startTime = cur_time;
1376 } else if ( elapsed >= duration ) {
1377 this.state = tstates.done;
1380 } else if ( state === tstates.snapback ) {
1381 if ( elapsed >= duration ) {
1382 this.pos = this.toPos;
1383 this.state = tstates.done;
1385 this.pos = this.fromPos + (( this.toPos - this.fromPos ) *
1386 $.easing[this.easing]( elapsed / duration,
1387 elapsed, 0, 1, duration ));
1395 return this.state === tstates.done;
1398 isMin: function () {
1399 return this.pos === this.minPos;
1402 isMax: function () {
1403 return this.pos === this.maxPos;
1406 isAvail: function () {
1407 return !( this.minPos === this.maxPos );
1410 getRemained: function () {
1411 return this.remained;
1414 getPosition: function () {
1419 $( document ).bind( 'pagecreate create', function ( e ) {
1420 var $page = $( e.target ),
1421 content_scroll = $page.find(".ui-content").jqmData("scroll");
1423 /* content scroll */
1424 if ( $.support.scrollview === undefined ) {
1425 $.support.scrollview = true;
1428 if ( $.support.scrollview === true && content_scroll === undefined ) {
1429 content_scroll = "y";
1432 if ( content_scroll !== "y" ) {
1433 content_scroll = "none";
1436 $page.find(".ui-content").attr( "data-scroll", content_scroll );
1438 $page.find(":jqmData(scroll)").not(".ui-scrollview-clip").each( function () {
1439 if ( $( this ).hasClass("ui-scrolllistview") ) {
1440 $( this ).scrolllistview();
1442 var st = $( this ).jqmData("scroll"),
1443 dir = st && ( st.search(/^[xy]/) !== -1 ) ? st : null,
1444 content = $(this).hasClass("ui-content"),
1447 if ( st === "none" ) {
1452 direction: dir || undefined,
1453 overflowEnable: content,
1454 scrollMethod: $( this ).jqmData("scroll-method") || undefined,
1455 scrollJump: $( this ).jqmData("scroll-jump") || undefined
1458 $( this ).scrollview( opts );
1463 $( document ).bind( 'pageshow', function ( e ) {
1464 var $page = $( e.target ),
1465 scroll = $page.find(".ui-content").jqmData("scroll");
1467 if ( scroll === "y" ) {
1468 resizePageContentHeight( e.target );
1472 }( jQuery, window, document ) );