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 )) &&
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,
204 bouncing = function ( dir ) {
205 setTimeout( function () {
206 self._bouncing_dir = dir;
207 self._setBouncing( self._$view, "in" );
210 setTimeout( function () {
211 self._setBouncing( self._$view, "out" );
217 if ( this._outerScrolling ) {
222 vt.update( this.options.overshootEnable );
223 y = vt.getPosition();
224 keepGoing = !vt.done();
226 if ( vt.getRemained() > this.options.overshootDuration ) {
227 scroll_height = this._getViewHeight() - this._$clip.height();
229 if ( !vt.isAvail() ) {
230 if ( this._speedY > 0 ) {
231 this._outerScroll( vt.getRemained() / 3, scroll_height );
233 this._outerScroll( y - vt.getRemained() / 3, scroll_height );
235 } else if ( vt.isMin() ) {
236 this._outerScroll( y - vt.getRemained() / 3, scroll_height );
238 if ( scroll_height > 0 ) {
241 } else if ( vt.isMax() ) {
242 this._outerScroll( vt.getRemained() / 3, scroll_height );
244 if ( scroll_height > 0 ) {
252 ht.update( this.options.overshootEnable );
253 x = ht.getPosition();
254 keepGoing = keepGoing || !ht.done();
257 this._setScrollPosition( x, y );
258 this._$clip.trigger( this.options.updateEventName,
259 [ { x: x, y: y } ] );
262 this._timerID = setTimeout( this._timerCB, this._timerInterval );
268 _setElementTransform: function ( $ele, x, y, duration ) {
272 if ( this._bouncing ) {
276 if ( !duration || duration === undefined ) {
279 transition = "-webkit-transform " + duration / 1000 + "s ease-out";
282 if ( $.support.cssTransform3d ) {
283 translate = "translate3d(" + x + "," + y + ", 0px)";
285 translate = "translate(" + x + "," + y + ")";
289 "-moz-transform": translate,
290 "-webkit-transform": translate,
291 "-ms-transform": translate,
292 "-o-transform": translate,
293 "transform": translate,
294 "-webkit-transition": transition
298 _setBouncing: function ( $ele, dir ) {
299 if ( dir === "in" ) {
300 if ( this._bouncing ) {
304 this._bouncing = true;
305 this._bouncing_count = 1;
307 this._bouncing_org_x = 1;
308 this._bouncing_org_y = 1;
310 this._bouncing_x = 0.99;
311 this._bouncing_y = 0.99;
313 this._setOverflowIndicator( this._bouncing_dir );
314 } else if ( dir === "out" ) {
315 if ( !this._bouncing ) {
319 this._bouncing = false;
320 this._bouncing_count = 1;
322 this._bouncing_org_x = this._bouncing_x;
323 this._bouncing_org_y = this._bouncing_y;
325 this._bouncing_x = 1;
326 this._bouncing_y = 1;
327 this._setOverflowIndicator( this._bouncing_dir );
332 this._doBouncing( $ele, dir );
335 _doBouncing: function ( $ele, dir ) {
343 if ( $.support.cssTransform3d ) {
344 translate = "translate3d(" + this._sx + "px," + this._sy + "px, 0px)";
346 translate = "translate(" + this._sx + "px," + this._sy + "px)";
349 if ( dir === "in" ) {
350 x_rate = this._bouncing_org_x - ( this._bouncing_org_x -
351 this._bouncing_x ) / frame * this._bouncing_count;
352 y_rate = this._bouncing_org_y - ( this._bouncing_org_y -
353 this._bouncing_y ) / frame * this._bouncing_count;
355 translate += " scale(" + x_rate + "," + y_rate + ")";
356 } else if ( dir === "out" ) {
357 x_rate = this._bouncing_org_x + ( this._bouncing_x -
358 this._bouncing_org_x ) / frame * this._bouncing_count;
359 y_rate = this._bouncing_org_y + ( this._bouncing_y -
360 this._bouncing_org_y ) / frame * this._bouncing_count;
362 translate += " scale(" + x_rate + "," + y_rate + ")";
367 if ( this._bouncing_dir ) {
368 origin = "50% " + ( this._bouncing_y * 100 - 10 ) + "%";
374 "-moz-transform": translate,
375 "-webkit-transform": translate,
376 "-ms-transform": translate,
377 "-o-transform": translate,
378 "transform": translate,
379 "-webkit-transform-origin": origin,
382 this._bouncing_count++;
384 if ( this._bouncing_count > frame ) {
388 setTimeout( function () {
389 self._doBouncing( $ele, dir );
390 }, this._timerInterval );
393 _setCalibration: function ( x, y ) {
394 if ( this.options.overshootEnable ) {
400 var $v = this._$view,
402 dirLock = this._directionLock,
406 if ( dirLock !== "y" && this._hTracker ) {
407 scroll_width = $v.width() - $c.width();
411 } else if ( x < -scroll_width ) {
412 this._sx = -scroll_width;
417 if ( scroll_width < 0 ) {
422 if ( dirLock !== "x" && this._vTracker ) {
423 scroll_height = this._getViewHeight() - $c.height();
428 if ( scroll_height > 0 ) {
429 this._bouncing_dir = 0;
430 this._setBouncing( this._$view, "in" );
432 } else if ( y < -scroll_height ) {
433 this._sy = -scroll_height;
435 if ( scroll_height > 0 ) {
436 this._bouncing_dir = 1;
437 this._setBouncing( this._$view, "in" );
440 if ( this._bouncing && this._sy !== y ) {
441 this._bouncing = false;
447 if ( scroll_height < 0 ) {
453 _setScrollPosition: function ( x, y, duration ) {
454 var $v = this._$view,
455 sm = this.options.scrollMethod,
456 $vsb = this._$vScrollBar,
457 $hsb = this._$hScrollBar,
460 this._setCalibration( x, y );
465 if ( sm === "translate" ) {
466 this._setElementTransform( $v, x + "px", y + "px", duration );
468 $v.css( {left: x + "px", top: y + "px"} );
472 $sbt = $vsb.find(".ui-scrollbar-thumb");
474 if ( sm === "translate" ) {
475 this._setElementTransform( $sbt, "0px",
476 -y / this._getViewHeight() * $sbt.parent().height() + "px",
479 $sbt.css( "top", -y / this._getViewHeight() * 100 + "%" );
484 $sbt = $hsb.find(".ui-scrollbar-thumb");
486 if ( sm === "translate" ) {
487 this._setElementTransform( $sbt,
488 -x / $v.width() * $sbt.parent().width() + "px", "0px",
491 $sbt.css("left", -x / $v.width() * 100 + "%");
496 _outerScroll: function ( y, scroll_height ) {
498 top = $( window ).scrollTop() - window.screenTop,
500 duration = this.options.snapbackDuration,
501 start = getCurrentTime(),
504 if ( !this.options.outerScrollEnable ) {
508 if ( this._$clip.jqmData("scroll") !== "y" ) {
512 if ( this._outerScrolling ) {
517 sy = ( window.screenTop ? window.screenTop : -y );
518 } else if ( y < -scroll_height ) {
519 sy = -y - scroll_height;
524 tfunc = function () {
525 var elapsed = getCurrentTime() - start;
527 if ( elapsed >= duration ) {
528 window.scrollTo( 0, top + sy );
529 self._outerScrolling = undefined;
533 ec = $.easing.easeOutQuad( elapsed / duration,
534 elapsed, 0, 1, duration );
536 window.scrollTo( 0, top + ( sy * ec ) );
537 self._outerScrolling = setTimeout( tfunc, self._timerInterval );
540 this._outerScrolling = setTimeout( tfunc, self._timerInterval );
543 _scrollTo: function ( x, y, duration ) {
545 start = getCurrentTime(),
546 efunc = $.easing.easeOutQuad,
556 tfunc = function () {
557 var elapsed = getCurrentTime() - start,
560 if ( elapsed >= duration ) {
562 self._setScrollPosition( x, y );
564 ec = efunc( elapsed / duration, elapsed, 0, 1, duration );
566 self._setScrollPosition( sx + ( dx * ec ), sy + ( dy * ec ) );
567 self._timerID = setTimeout( tfunc, self._timerInterval );
571 this._timerID = setTimeout( tfunc, this._timerInterval );
574 scrollTo: function ( x, y, duration ) {
577 if ( !duration || this.options.scrollMethod === "translate" ) {
578 this._setScrollPosition( x, y, duration );
580 this._scrollTo( x, y, duration );
584 getScrollPosition: function () {
585 return { x: -this._sx, y: -this._sy };
588 _getScrollHierarchy: function () {
592 this._$clip.parents( ".ui-scrollview-clip").each( function () {
593 d = $( this ).jqmData("scrollview");
601 _getAncestorByDirection: function ( dir ) {
602 var svh = this._getScrollHierarchy(),
609 svdir = sv.options.direction;
611 if (!svdir || svdir === dir) {
618 _handleDragStart: function ( e, ex, ey ) {
621 this._didDrag = false;
622 this._skip_dragging = false;
624 var target = $( e.target ),
627 svdir = this.options.direction;
629 /* should prevent the default behavior when click the button */
630 this._is_button = target.is( '.ui-btn-text' ) ||
631 target.is( '.ui-btn-inner' ) ||
632 target.is( '.ui-btn-inner .ui-icon' );
634 if ( this._is_button ) {
635 if ( target.parents('.ui-slider-handle').length ) {
636 this._skip_dragging = true;
642 * We need to prevent the default behavior to
643 * suppress accidental selection of text, etc.
645 this._is_inputbox = target.is(':input') ||
646 target.parents(':input').length > 0;
648 if ( this._is_inputbox ) {
649 target.one( "resize.scrollview", function () {
650 if ( ey > $c.height() ) {
651 self.scrollTo( -ex, self._sy - ey + $c.height(),
652 self.options.snapbackDuration );
657 if ( this.options.eventType === "mouse" && !this._is_inputbox && !this._is_button ) {
664 this._doSnapBackX = false;
665 this._doSnapBackY = false;
668 this._directionLock = "";
671 this._enableTracking();
673 this._set_scrollbar_size();
676 _propagateDragMove: function ( sv, e, ex, ey, dir ) {
677 this._hideScrollBars();
678 this._hideOverflowIndicator();
679 this._disableTracking();
680 sv._handleDragStart( e, ex, ey );
681 sv._directionLock = dir;
682 sv._didDrag = this._didDrag;
685 _handleDragMove: function ( e, ex, ey ) {
686 if ( this._skip_dragging ) {
690 if ( !this._dragging ) {
694 if ( !this._is_inputbox && !this._is_button ) {
698 var mt = this.options.moveThreshold,
699 dx = ex - this._lastX,
700 dy = ey - this._lastY,
701 svdir = this.options.direction,
711 this._lastMove = getCurrentTime();
713 if ( !this._directionLock ) {
717 if ( x < mt && y < mt ) {
721 if ( x < y && (x / y) < 0.5 ) {
723 } else if ( x > y && (y / x) < 0.5 ) {
727 if ( svdir && dir && svdir !== dir ) {
729 * This scrollview can't handle the direction the user
730 * is attempting to scroll. Find an ancestor scrollview
731 * that can handle the request.
734 sv = this._getAncestorByDirection( dir );
736 this._propagateDragMove( sv, e, ex, ey, dir );
741 this._directionLock = svdir || (dir || "none");
746 dirLock = this._directionLock;
748 if ( dirLock !== "y" && this._hTracker ) {
753 this._doSnapBackX = false;
755 scope = ( newX > 0 || newX < this._maxX );
757 if ( scope && dirLock === "x" ) {
758 sv = this._getAncestorByDirection("x");
760 this._setScrollPosition( newX > 0 ?
761 0 : this._maxX, newY );
762 this._propagateDragMove( sv, e, ex, ey, dir );
766 newX = x + ( dx / 2 );
767 this._doSnapBackX = true;
771 if ( dirLock !== "x" && this._vTracker ) {
772 if ( Math.abs( this._startY - ey ) < mt && dirLock !== "xy" ) {
780 this._doSnapBackY = false;
782 scope = ( newY > 0 || newY < this._maxY );
784 if ( scope && dirLock === "y" ) {
785 sv = this._getAncestorByDirection("y");
787 this._setScrollPosition( newX,
788 newY > 0 ? 0 : this._maxY );
789 this._propagateDragMove( sv, e, ex, ey, dir );
793 newY = y + ( dy / 2 );
794 this._doSnapBackY = true;
798 if ( this.options.overshootEnable === false ) {
799 this._doSnapBackX = false;
800 this._doSnapBackY = false;
803 this._didDrag = true;
807 this._setScrollPosition( newX, newY );
809 this._showScrollBars();
810 this._showOverflowIndicator();
813 _handleDragStop: function ( e ) {
814 if ( this._skip_dragging ) {
818 var l = this._lastMove,
819 t = getCurrentTime(),
820 doScroll = (l && (t - l) <= this.options.moveIntervalThreshold),
821 sx = ( this._hTracker && this._speedX && doScroll ) ?
822 this._speedX : ( this._doSnapBackX ? 1 : 0 ),
823 sy = ( this._vTracker && this._speedY && doScroll ) ?
824 this._speedY : ( this._doSnapBackY ? 1 : 0 ),
825 svdir = this.options.direction,
830 if ( !this._setGestureScroll( sx, sy ) ) {
831 this._startMScroll( sx, sy );
834 this._hideScrollBars();
835 this._hideOverflowIndicator();
838 this._disableTracking();
840 if ( this._bouncing ) {
841 this._setBouncing( this._$view, "out" );
842 this._hideScrollBars();
843 this._hideOverflowIndicator();
846 return !this._didDrag;
849 _setGestureScroll: function ( sx, sy ) {
851 reset = function () {
852 clearTimeout( self._gesture_timer );
853 self._gesture_dir = 0;
854 self._gesture_count = 0;
855 self._gesture_timer = undefined;
862 dir = sy > 0 ? 1 : -1;
864 if ( !this._gesture_timer ) {
865 this._gesture_count = 1;
866 this._gesture_dir = dir;
868 this._gesture_timer = setTimeout( function () {
875 if ( this._gesture_dir !== dir ) {
880 this._gesture_count++;
882 if ( this._gesture_count === 3 ) {
884 this.scrollTo( 0, 0, this.options.overshootDuration );
886 this.scrollTo( 0, -( this._getViewHeight() - this._$clip.height() ),
887 this.options.overshootDuration );
897 _enableTracking: function () {
898 this._dragging = true;
901 _disableTracking: function () {
902 this._dragging = false;
905 _showScrollBars: function ( interval ) {
906 var vclass = "ui-scrollbar-visible",
909 if ( !this.options.showScrollBars ) {
912 if ( this._scrollbar_showed ) {
916 if ( this._$vScrollBar ) {
917 this._$vScrollBar.addClass( vclass );
919 if ( this._$hScrollBar ) {
920 this._$hScrollBar.addClass( vclass );
923 this._scrollbar_showed = true;
926 setTimeout( function () {
927 self._hideScrollBars();
932 _hideScrollBars: function () {
933 var vclass = "ui-scrollbar-visible";
935 if ( !this.options.showScrollBars ) {
938 if ( !this._scrollbar_showed ) {
942 if ( this._$vScrollBar ) {
943 this._$vScrollBar.removeClass( vclass );
945 if ( this._$hScrollBar ) {
946 this._$hScrollBar.removeClass( vclass );
949 this._scrollbar_showed = false;
952 _resetOverflowIndicator: function () {
953 if ( !this.options.overflowEnable || !this._overflowAvail ) {
957 this._overflow_top.css( "-webkit-animation", "" );
958 this._overflow_bottom.css( "-webkit-animation", "" );
961 _setOverflowIndicator: function ( dir ) {
963 this._opacity_top = "0.2";
964 this._opacity_bottom = "0.8";
965 } else if ( dir === 0 ) {
966 this._opacity_top = "0.8";
967 this._opacity_bottom = "0.2";
969 this._opacity_top = "0.5";
970 this._opacity_bottom = "0.5";
974 _getOverflowIndicator: function ( opacity ) {
975 if ( opacity === "0.2" ) {
977 } else if ( opacity === "0.8" ) {
983 _showOverflowIndicator: function () {
984 if ( !this.options.overflowEnable || !this._overflowAvail ) {
988 this._overflow_top.css( "opacity", this._opacity_top );
989 this._overflow_bottom.css( "opacity", this._opacity_bottom );
991 if ( this._overflow_showed === true ) {
995 this._overflow_top.css( "-webkit-animation", "ui-overflow-show" +
996 this._getOverflowIndicator( this._opacity_top ) + " 0.3s 1 ease" );
997 this._overflow_bottom.css( "-webkit-animation", "ui-overflow-show" +
998 this._getOverflowIndicator( this._opacity_bottom ) + " 0.3s 1 ease" );
1000 this._overflow_showed = true;
1003 _hideOverflowIndicator: function () {
1007 if ( !this.options.overflowEnable || !this._overflowAvail ) {
1011 if ( this._overflow_showed === false ) {
1015 opacity_top = this._overflow_top.css( "opacity" );
1016 opacity_bottom = this._overflow_bottom.css( "opacity" );
1018 this._overflow_top.css( "opacity", "0" );
1019 this._overflow_bottom.css( "opacity", "0" );
1021 this._overflow_top.css( "-webkit-animation", "ui-overflow-hide" +
1022 this._getOverflowIndicator( opacity_top ) + " 0.5s 1 ease" );
1023 this._overflow_bottom.css( "-webkit-animation", "ui-overflow-hide" +
1024 this._getOverflowIndicator( opacity_bottom ) + " 0.5s 1 ease" );
1026 this._overflow_showed = false;
1027 this._setOverflowIndicator();
1030 _add_event: function () {
1035 if ( this.options.eventType === "mouse" ) {
1036 this._dragEvt = "mousedown mousemove mouseup click mousewheel";
1038 this._dragCB = function ( e ) {
1041 return self._handleDragStart( e,
1042 e.clientX, e.clientY );
1045 return self._handleDragMove( e,
1046 e.clientX, e.clientY );
1049 return self._handleDragStop( e );
1052 return !self._didDrag;
1055 var old = self.getScrollPosition();
1056 self.scrollTo( -old.x,
1057 -(old.y - e.originalEvent.wheelDelta) );
1062 this._dragEvt = "touchstart touchmove touchend click";
1064 this._dragCB = function ( e ) {
1069 t = e.originalEvent.targetTouches[0];
1070 return self._handleDragStart( e,
1074 t = e.originalEvent.targetTouches[0];
1075 return self._handleDragMove( e,
1079 return self._handleDragStop( e );
1082 return !self._didDrag;
1087 $v.bind( this._dragEvt, this._dragCB );
1089 $c.bind( "updatelayout", function ( e ) {
1092 view_h = self._getViewHeight();
1094 if ( !$c.height() || !view_h ) {
1095 self.scrollTo( 0, 0, 0 );
1099 sy = $c.height() - view_h;
1100 vh = view_h - self._view_height;
1102 self._view_height = view_h;
1104 if ( vh == 0 || vh > $c.height() / 2 ) {
1108 if ( self._sy - sy <= -vh ) {
1109 self.scrollTo( 0, sy,
1110 self.options.snapbackDuration );
1111 } else if ( self._sy - sy <= vh + self.options.moveThreshold ) {
1112 self.scrollTo( 0, sy,
1113 self.options.snapbackDuration );
1117 $( window ).bind( "resize", function ( e ) {
1119 view_h = self._getViewHeight();
1121 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1125 if ( !$c.height() || !view_h ) {
1129 focused = $c.find(".ui-focus");
1132 focused.trigger("resize.scrollview");
1135 /* calibration - after triggered throttledresize */
1136 setTimeout( function () {
1137 if ( self._sy < $c.height() - self._getViewHeight() ) {
1138 self.scrollTo( 0, $c.height() - self._getViewHeight(),
1139 self.options.overshootDuration );
1143 self._view_height = view_h;
1146 $c.closest(".ui-page")
1147 .one( "pageshow", function ( e ) {
1148 self._view_offset = self._$view.offset().top - self._$clip.offset().top;
1149 self._view_height = self._getViewHeight();
1151 .bind( "pageshow", function ( e ) {
1152 /* should be called after pagelayout */
1153 setTimeout( function () {
1154 self._set_scrollbar_size();
1155 self._setScrollPosition( self._sx, self._sy );
1156 self._showScrollBars( 2000 );
1157 self._resetOverflowIndicator();
1162 _add_scrollbar: function () {
1163 var $c = this._$clip,
1164 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
1165 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
1167 if ( !this.options.showScrollBars ) {
1171 if ( this._vTracker ) {
1172 $c.append( prefix + "y" + suffix );
1173 this._$vScrollBar = $c.children(".ui-scrollbar-y");
1175 if ( this._hTracker ) {
1176 $c.append( prefix + "x" + suffix );
1177 this._$hScrollBar = $c.children(".ui-scrollbar-x");
1180 this._scrollbar_showed = false;
1183 _add_scroll_jump: function () {
1184 var $c = this._$clip,
1189 if ( !this.options.scrollJump ) {
1193 if ( this._vTracker ) {
1194 top_btn = $( '<div class="ui-scroll-jump-top-bg">' +
1195 '<div data-role="button" data-inline="true" data-icon="jumptop" style="width:37px;height:37px">.</div></div>' );
1196 $c.append( top_btn ).trigger("create");
1198 top_btn.bind( "vclick", function () {
1199 self.scrollTo( 0, 0, self.options.overshootDuration );
1203 if ( this._hTracker ) {
1204 left_btn = $( '<div class="ui-scroll-jump-left-bg">' +
1205 '<div data-role="button" data-inline="true" data-icon="jumpleft" style="width:37px;height:37px">.</div></div>' );
1206 $c.append( left_btn ).trigger("create");
1208 left_btn.bind( "vclick", function () {
1209 self.scrollTo( 0, 0, self.options.overshootDuration );
1214 _add_overflow_indicator: function () {
1215 if ( !this.options.overflowEnable ) {
1219 this._overflow_top = $( '<div class="ui-overflow-indicator-top"></div>' );
1220 this._overflow_bottom = $( '<div class="ui-overflow-indicator-bottom"></div>' );
1222 this._$clip.append( this._overflow_top );
1223 this._$clip.append( this._overflow_bottom );
1225 this._opacity_top = "0.5";
1226 this._opacity_bottom = "0.5";
1227 this._overflow_showed = false;
1230 _set_scrollbar_size: function () {
1231 var $c = this._$clip,
1239 if ( !this.options.showScrollBars ) {
1243 if ( this._hTracker ) {
1246 this._maxX = cw - vw;
1248 if ( this._maxX > 0 ) {
1251 if ( this._$hScrollBar && vw ) {
1252 thumb = this._$hScrollBar.find(".ui-scrollbar-thumb");
1253 thumb.css( "width", (cw >= vw ? "0" :
1254 (Math.floor(cw / vw * 100) || 1) + "%") );
1258 if ( this._vTracker ) {
1260 vh = this._getViewHeight();
1261 this._maxY = ch - vh;
1263 if ( this._maxY > 0 ) {
1266 if ( this._$vScrollBar && vh ) {
1267 thumb = this._$vScrollBar.find(".ui-scrollbar-thumb");
1268 thumb.css( "height", (ch >= vh ? "0" :
1269 (Math.floor(ch / vh * 100) || 1) + "%") );
1271 this._overflowAvail = !!thumb.height();
1277 $.extend( MomentumTracker.prototype, {
1278 start: function ( pos, speed, duration, minPos, maxPos ) {
1279 var tstate = ( pos < minPos || pos > maxPos ) ?
1280 tstates.snapback : tstates.scrolling,
1283 this.state = ( speed !== 0 ) ? tstate : tstates.done;
1286 this.duration = ( this.state === tstates.snapback ) ?
1287 this.options.snapbackDuration : duration;
1288 this.minPos = minPos;
1289 this.maxPos = maxPos;
1291 this.fromPos = ( this.state === tstates.snapback ) ? this.pos : 0;
1292 pos_temp = ( this.pos < this.minPos ) ? this.minPos : this.maxPos;
1293 this.toPos = ( this.state === tstates.snapback ) ? pos_temp : 0;
1295 this.startTime = getCurrentTime();
1298 reset: function () {
1299 this.state = tstates.done;
1308 update: function ( overshootEnable ) {
1309 var state = this.state,
1310 cur_time = getCurrentTime(),
1311 duration = this.duration,
1312 elapsed = cur_time - this.startTime,
1317 if ( state === tstates.done ) {
1321 elapsed = elapsed > duration ? duration : elapsed;
1323 this.remained = duration - elapsed;
1325 if ( state === tstates.scrolling || state === tstates.overshot ) {
1327 ( 1 - $.easing[this.easing]( elapsed / duration,
1328 elapsed, 0, 1, duration ) );
1332 didOverShoot = ( state === tstates.scrolling ) &&
1333 ( x < this.minPos || x > this.maxPos );
1335 if ( didOverShoot ) {
1336 x = ( x < this.minPos ) ? this.minPos : this.maxPos;
1341 if ( state === tstates.overshot ) {
1342 if ( !overshootEnable ) {
1343 this.state = tstates.done;
1345 if ( elapsed >= duration ) {
1346 this.state = tstates.snapback;
1347 this.fromPos = this.pos;
1348 this.toPos = ( x < this.minPos ) ?
1349 this.minPos : this.maxPos;
1350 this.duration = this.options.snapbackDuration;
1351 this.startTime = cur_time;
1354 } else if ( state === tstates.scrolling ) {
1355 if ( didOverShoot && overshootEnable ) {
1356 this.state = tstates.overshot;
1357 this.speed = dx / 2;
1358 this.duration = this.options.overshootDuration;
1359 this.startTime = cur_time;
1360 } else if ( elapsed >= duration ) {
1361 this.state = tstates.done;
1364 } else if ( state === tstates.snapback ) {
1365 if ( elapsed >= duration ) {
1366 this.pos = this.toPos;
1367 this.state = tstates.done;
1369 this.pos = this.fromPos + (( this.toPos - this.fromPos ) *
1370 $.easing[this.easing]( elapsed / duration,
1371 elapsed, 0, 1, duration ));
1379 return this.state === tstates.done;
1382 isMin: function () {
1383 return this.pos === this.minPos;
1386 isMax: function () {
1387 return this.pos === this.maxPos;
1390 isAvail: function () {
1391 return !( this.minPos === this.maxPos );
1394 getRemained: function () {
1395 return this.remained;
1398 getPosition: function () {
1403 $( document ).bind( 'pagecreate create', function ( e ) {
1404 var $page = $( e.target ),
1405 content_scroll = $page.find(".ui-content").jqmData("scroll");
1407 /* content scroll */
1408 if ( $.support.scrollview === undefined ) {
1409 $.support.scrollview = true;
1412 if ( $.support.scrollview === true && content_scroll === undefined ) {
1413 content_scroll = "y";
1416 if ( content_scroll !== "y" ) {
1417 content_scroll = "none";
1420 $page.find(".ui-content").attr( "data-scroll", content_scroll );
1422 $page.find(":jqmData(scroll):not(.ui-scrollview-clip)").each( function () {
1423 if ( $( this ).hasClass("ui-scrolllistview") ) {
1424 $( this ).scrolllistview();
1426 var st = $( this ).jqmData("scroll"),
1427 dir = st && ( st.search(/^[xy]/) !== -1 ) ? st : null,
1428 content = $(this).hasClass("ui-content"),
1431 if ( st === "none" ) {
1436 direction: dir || undefined,
1437 overflowEnable: content,
1438 scrollMethod: $( this ).jqmData("scroll-method") || undefined,
1439 scrollJump: $( this ).jqmData("scroll-jump") || undefined
1442 $( this ).scrollview( opts );
1447 $( document ).bind( 'pageshow', function ( e ) {
1448 var $page = $( e.target ),
1449 scroll = $page.find(".ui-content").jqmData("scroll");
1451 if ( scroll === "y" ) {
1452 resizePageContentHeight( e.target );
1456 }( jQuery, window, document ) );