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();
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_height = this._getViewHeight();
107 direction = this.options.direction;
109 this._hTracker = ( direction !== "y" ) ?
110 new MomentumTracker( this.options ) : null;
111 this._vTracker = ( direction !== "x" ) ?
112 new MomentumTracker( this.options ) : null;
114 this._timerInterval = this.options.timerInterval;
117 this._timerCB = function () {
118 self._handleMomentumScroll();
122 this._add_scrollbar();
123 this._add_scroll_jump();
124 this._add_overflow_indicator();
127 _startMScroll: function ( speedX, speedY ) {
128 var keepGoing = false,
129 duration = this.options.scrollDuration,
135 this._$clip.trigger( this.options.startEventName );
138 c = this._$clip.width();
139 v = this._$view.width();
141 if ( (( this._sx === 0 && speedX > 0 ) ||
142 ( this._sx === -(v - c) && speedX < 0 )) &&
147 ht.start( this._sx, speedX,
148 duration, (v > c) ? -(v - c) : 0, 0 );
149 keepGoing = !ht.done();
153 c = this._$clip.height();
154 v = this._getViewHeight();
156 if ( (( this._sy === 0 && speedY > 0 ) ||
157 ( this._sy === -(v - c) && speedY < 0 )) &&
162 vt.start( this._sy, speedY,
163 duration, (v > c) ? -(v - c) : 0, 0 );
164 keepGoing = keepGoing || !vt.done();
168 this._timerID = setTimeout( this._timerCB, this._timerInterval );
174 _stopMScroll: function () {
175 if ( this._timerID ) {
176 this._$clip.trigger( this.options.stopEventName );
177 clearTimeout( this._timerID );
181 if ( this._vTracker ) {
182 this._vTracker.reset();
185 if ( this._hTracker ) {
186 this._hTracker.reset();
189 this._hideScrollBars();
190 this._hideOverflowIndicator();
193 _handleMomentumScroll: function () {
194 var keepGoing = false,
199 end_effect = function ( dir ) {
200 setTimeout( function () {
201 self._effect_dir = dir;
202 self._setEndEffect( "in" );
205 setTimeout( function () {
206 self._setEndEffect( "out" );
212 if ( this._outerScrolling ) {
217 vt.update( this.options.overshootEnable );
218 y = vt.getPosition();
219 keepGoing = !vt.done();
221 if ( vt.getRemained() > this.options.overshootDuration ) {
222 scroll_height = this._getViewHeight() - this._$clip.height();
224 if ( !vt.isAvail() ) {
225 if ( this._speedY > 0 ) {
226 this._outerScroll( vt.getRemained() / 3, scroll_height );
228 this._outerScroll( y - vt.getRemained() / 3, scroll_height );
230 } else if ( vt.isMin() ) {
231 this._outerScroll( y - vt.getRemained() / 3, scroll_height );
233 if ( scroll_height > 0 ) {
236 } else if ( vt.isMax() ) {
237 this._outerScroll( vt.getRemained() / 3, scroll_height );
239 if ( scroll_height > 0 ) {
247 ht.update( this.options.overshootEnable );
248 x = ht.getPosition();
249 keepGoing = keepGoing || !ht.done();
252 this._setScrollPosition( x, y );
253 this._$clip.trigger( this.options.updateEventName,
254 [ { x: x, y: y } ] );
257 this._timerID = setTimeout( this._timerCB, this._timerInterval );
263 _setElementTransform: function ( $ele, x, y, duration ) {
267 if ( !duration || duration === undefined ) {
270 transition = "-webkit-transform " + duration / 1000 + "s ease-out";
273 if ( $.support.cssTransform3d ) {
274 translate = "translate3d(" + x + "," + y + ", 0px)";
276 translate = "translate(" + x + "," + y + ")";
280 "-moz-transform": translate,
281 "-webkit-transform": translate,
282 "-ms-transform": translate,
283 "-o-transform": translate,
284 "transform": translate,
285 "-webkit-transition": transition
289 _setEndEffect: function ( dir ) {
290 var scroll_height = this._getViewHeight() - this._$clip.height();
292 if ( this._softkeyboard ) {
293 if ( this._effect_dir ) {
294 this._outerScroll( -scroll_height - this._softkeyboardHeight,
297 this._outerScroll( this._softkeyboardHeight, scroll_height );
302 if ( dir === "in" ) {
303 if ( this._endEffect ) {
307 this._endEffect = true;
308 this._setOverflowIndicator( this._effect_dir );
309 this._showOverflowIndicator();
310 } else if ( dir === "out" ) {
311 if ( !this._endEffect ) {
315 this._endEffect = false;
317 this._endEffect = false;
318 this._setOverflowIndicator();
319 this._showOverflowIndicator();
323 _setCalibration: function ( x, y ) {
324 if ( this.options.overshootEnable ) {
330 var $v = this._$view,
332 dirLock = this._directionLock,
336 if ( dirLock !== "y" && this._hTracker ) {
337 scroll_width = $v.width() - $c.width();
341 } else if ( x < -scroll_width ) {
342 this._sx = -scroll_width;
347 if ( scroll_width < 0 ) {
352 if ( dirLock !== "x" && this._vTracker ) {
353 scroll_height = this._getViewHeight() - $c.height();
358 if ( this._didDrag && scroll_height > 0 ) {
359 this._effect_dir = 0;
360 this._setEndEffect( "in" );
362 } else if ( y < -scroll_height ) {
363 this._sy = -scroll_height;
365 if ( this._didDrag && scroll_height > 0 ) {
366 this._effect_dir = 1;
367 this._setEndEffect( "in" );
370 if ( this._endEffect && this._sy !== y ) {
371 this._setEndEffect();
377 if ( scroll_height < 0 ) {
383 _setScrollPosition: function ( x, y, duration ) {
384 var $v = this._$view,
385 sm = this.options.scrollMethod,
386 $vsb = this._$vScrollBar,
387 $hsb = this._$hScrollBar,
390 this._setCalibration( x, y );
395 if ( sm === "translate" ) {
396 this._setElementTransform( $v, x + "px", y + "px", duration );
398 $v.css( {left: x + "px", top: y + "px"} );
402 $sbt = $vsb.find(".ui-scrollbar-thumb");
404 if ( sm === "translate" ) {
405 this._setElementTransform( $sbt, "0px",
406 -y / this._getViewHeight() * $sbt.parent().height() + "px",
409 $sbt.css( "top", -y / this._getViewHeight() * 100 + "%" );
414 $sbt = $hsb.find(".ui-scrollbar-thumb");
416 if ( sm === "translate" ) {
417 this._setElementTransform( $sbt,
418 -x / $v.width() * $sbt.parent().width() + "px", "0px",
421 $sbt.css("left", -x / $v.width() * 100 + "%");
426 _outerScroll: function ( y, scroll_height ) {
428 top = $( window ).scrollTop() - window.screenTop,
430 duration = this.options.snapbackDuration,
431 start = getCurrentTime(),
434 if ( !this.options.outerScrollEnable ) {
438 if ( this._$clip.jqmData("scroll") !== "y" ) {
442 if ( this._outerScrolling ) {
447 sy = ( window.screenTop ? window.screenTop : -y );
448 } else if ( y < -scroll_height ) {
449 sy = -y - scroll_height;
454 tfunc = function () {
455 var elapsed = getCurrentTime() - start;
457 if ( elapsed >= duration ) {
458 window.scrollTo( 0, top + sy );
459 self._outerScrolling = undefined;
463 ec = $.easing.easeOutQuad( elapsed / duration,
464 elapsed, 0, 1, duration );
466 window.scrollTo( 0, top + ( sy * ec ) );
467 self._outerScrolling = setTimeout( tfunc, self._timerInterval );
470 this._outerScrolling = setTimeout( tfunc, self._timerInterval );
473 _scrollTo: function ( x, y, duration ) {
475 start = getCurrentTime(),
476 efunc = $.easing.easeOutQuad,
486 tfunc = function () {
487 var elapsed = getCurrentTime() - start,
490 if ( elapsed >= duration ) {
492 self._setScrollPosition( x, y );
494 ec = efunc( elapsed / duration, elapsed, 0, 1, duration );
496 self._setScrollPosition( sx + ( dx * ec ), sy + ( dy * ec ) );
497 self._timerID = setTimeout( tfunc, self._timerInterval );
501 this._timerID = setTimeout( tfunc, this._timerInterval );
504 scrollTo: function ( x, y, duration ) {
506 this._didDrag = false;
508 if ( !duration || this.options.scrollMethod === "translate" ) {
509 this._setScrollPosition( x, y, duration );
511 this._scrollTo( x, y, duration );
515 getScrollPosition: function () {
516 return { x: -this._sx, y: -this._sy };
519 skipDragging: function ( value ) {
520 this._skip_dragging = value;
523 _getScrollHierarchy: function () {
527 this._$clip.parents( ".ui-scrollview-clip").each( function () {
528 d = $( this ).jqmData("scrollview");
536 _getAncestorByDirection: function ( dir ) {
537 var svh = this._getScrollHierarchy(),
544 svdir = sv.options.direction;
546 if (!svdir || svdir === dir) {
553 _handleDragStart: function ( e, ex, ey ) {
556 this._didDrag = false;
557 this._skip_dragging = false;
559 var target = $( e.target ),
562 svdir = this.options.direction;
564 /* should prevent the default behavior when click the button */
565 this._is_button = target.is( '.ui-btn' ) ||
566 target.is( '.ui-btn-text' ) ||
567 target.is( '.ui-btn-inner' ) ||
568 target.is( '.ui-btn-inner .ui-icon' );
570 /* should prevent the default behavior when click the slider */
571 if ( target.parents('.ui-slider').length || target.is('.ui-slider') ) {
572 this._skip_dragging = true;
576 if ( target.is('textarea') ) {
577 target.bind( "scroll", function () {
578 self._skip_dragging = true;
579 target.unbind("scroll");
584 * We need to prevent the default behavior to
585 * suppress accidental selection of text, etc.
587 this._is_inputbox = target.is(':input') ||
588 target.parents(':input').length > 0;
590 if ( this._is_inputbox ) {
591 target.one( "resize.scrollview", function () {
592 if ( ey > $c.height() ) {
593 self.scrollTo( -ex, self._sy - ey + $c.height(),
594 self.options.snapbackDuration );
599 if ( this.options.eventType === "mouse" && !this._is_inputbox && !this._is_button ) {
606 this._doSnapBackX = false;
607 this._doSnapBackY = false;
610 this._directionLock = "";
613 this._enableTracking();
615 this._set_scrollbar_size();
618 _propagateDragMove: function ( sv, e, ex, ey, dir ) {
619 this._hideScrollBars();
620 this._hideOverflowIndicator();
621 this._disableTracking();
622 sv._handleDragStart( e, ex, ey );
623 sv._directionLock = dir;
624 sv._didDrag = this._didDrag;
627 _handleDragMove: function ( e, ex, ey ) {
628 if ( this._skip_dragging ) {
632 if ( !this._dragging ) {
636 if ( !this._is_inputbox && !this._is_button ) {
640 var mt = this.options.moveThreshold,
641 dx = ex - this._lastX,
642 dy = ey - this._lastY,
643 svdir = this.options.direction,
653 this._lastMove = getCurrentTime();
655 if ( !this._directionLock ) {
659 if ( x < mt && y < mt ) {
663 if ( x < y && (x / y) < 0.5 ) {
665 } else if ( x > y && (y / x) < 0.5 ) {
669 if ( svdir && dir && svdir !== dir ) {
671 * This scrollview can't handle the direction the user
672 * is attempting to scroll. Find an ancestor scrollview
673 * that can handle the request.
676 sv = this._getAncestorByDirection( dir );
678 this._propagateDragMove( sv, e, ex, ey, dir );
683 this._directionLock = svdir || (dir || "none");
688 dirLock = this._directionLock;
690 if ( dirLock !== "y" && this._hTracker ) {
695 this._doSnapBackX = false;
697 scope = ( newX > 0 || newX < this._maxX );
699 if ( scope && dirLock === "x" ) {
700 sv = this._getAncestorByDirection("x");
702 this._setScrollPosition( newX > 0 ?
703 0 : this._maxX, newY );
704 this._propagateDragMove( sv, e, ex, ey, dir );
708 newX = x + ( dx / 2 );
709 this._doSnapBackX = true;
713 if ( dirLock !== "x" && this._vTracker ) {
714 if ( Math.abs( this._startY - ey ) < mt && dirLock !== "xy" ) {
722 this._doSnapBackY = false;
724 scope = ( newY > 0 || newY < this._maxY );
726 if ( scope && dirLock === "y" ) {
727 sv = this._getAncestorByDirection("y");
729 this._setScrollPosition( newX,
730 newY > 0 ? 0 : this._maxY );
731 this._propagateDragMove( sv, e, ex, ey, dir );
735 newY = y + ( dy / 2 );
736 this._doSnapBackY = true;
740 if ( this.options.overshootEnable === false ) {
741 this._doSnapBackX = false;
742 this._doSnapBackY = false;
748 this._setScrollPosition( newX, newY );
750 if ( this._didDrag === false ) {
751 this._didDrag = true;
752 this._showScrollBars();
753 this._showOverflowIndicator();
755 this._$clip.parents(".ui-scrollview-clip").each( function () {
756 $( this ).scrollview( "skipDragging", true );
761 _handleDragStop: function ( e ) {
764 if ( this._skip_dragging ) {
768 var l = this._lastMove,
769 t = getCurrentTime(),
770 doScroll = (l && (t - l) <= this.options.moveIntervalThreshold),
771 sx = ( this._hTracker && this._speedX && doScroll ) ?
772 this._speedX : ( this._doSnapBackX ? 1 : 0 ),
773 sy = ( this._vTracker && this._speedY && doScroll ) ?
774 this._speedY : ( this._doSnapBackY ? 1 : 0 ),
775 svdir = this.options.direction,
780 if ( !this._setGestureScroll( sx, sy ) ) {
781 this._startMScroll( sx, sy );
784 this._hideScrollBars();
785 this._hideOverflowIndicator();
788 this._disableTracking();
790 if ( this._endEffect ) {
791 setTimeout( function () {
792 self._setEndEffect( "out" );
793 self._hideScrollBars();
794 self._hideOverflowIndicator();
798 return !this._didDrag;
801 _setGestureScroll: function ( sx, sy ) {
803 reset = function () {
804 clearTimeout( self._gesture_timer );
805 self._gesture_dir = 0;
806 self._gesture_count = 0;
807 self._gesture_timer = undefined;
814 dir = sy > 0 ? 1 : -1;
816 if ( !this._gesture_timer ) {
817 this._gesture_count = 1;
818 this._gesture_dir = dir;
820 this._gesture_timer = setTimeout( function () {
827 if ( this._gesture_dir !== dir ) {
832 this._gesture_count++;
834 if ( this._gesture_count === 3 ) {
836 this.scrollTo( 0, 0, this.options.overshootDuration );
838 this.scrollTo( 0, -( this._getViewHeight() - this._$clip.height() ),
839 this.options.overshootDuration );
849 _enableTracking: function () {
850 this._dragging = true;
853 _disableTracking: function () {
854 this._dragging = false;
857 _showScrollBars: function ( interval ) {
858 var vclass = "ui-scrollbar-visible",
861 if ( !this.options.showScrollBars ) {
864 if ( this._scrollbar_showed ) {
868 if ( this._$vScrollBar ) {
869 this._$vScrollBar.addClass( vclass );
871 if ( this._$hScrollBar ) {
872 this._$hScrollBar.addClass( vclass );
875 this._scrollbar_showed = true;
878 setTimeout( function () {
879 self._hideScrollBars();
884 _hideScrollBars: function () {
885 var vclass = "ui-scrollbar-visible";
887 if ( !this.options.showScrollBars ) {
890 if ( !this._scrollbar_showed ) {
894 if ( this._$vScrollBar ) {
895 this._$vScrollBar.removeClass( vclass );
897 if ( this._$hScrollBar ) {
898 this._$hScrollBar.removeClass( vclass );
901 this._scrollbar_showed = false;
904 _setOverflowIndicator: function ( dir ) {
906 this._opacity_top = "0";
907 this._opacity_bottom = "0.8";
908 } else if ( dir === 0 ) {
909 this._opacity_top = "0.8";
910 this._opacity_bottom = "0";
912 this._opacity_top = "0.5";
913 this._opacity_bottom = "0.5";
917 _showOverflowIndicator: function () {
918 if ( !this.options.overflowEnable || !this._overflowAvail || this._softkeyboard ) {
922 this._overflow_top.animate( { opacity: this._opacity_top }, 300 );
923 this._overflow_bottom.animate( { opacity: this._opacity_bottom }, 300 );
925 this._overflow_showed = true;
928 _hideOverflowIndicator: function () {
929 if ( !this.options.overflowEnable || !this._overflowAvail || this._softkeyboard ) {
933 if ( this._overflow_showed === false ) {
937 this._overflow_top.animate( { opacity: 0 }, 300 );
938 this._overflow_bottom.animate( { opacity: 0 }, 300 );
940 this._overflow_showed = false;
941 this._setOverflowIndicator();
944 _add_event: function () {
949 if ( this.options.eventType === "mouse" ) {
950 this._dragEvt = "mousedown mousemove mouseup click mousewheel";
952 this._dragCB = function ( e ) {
955 return self._handleDragStart( e,
956 e.clientX, e.clientY );
959 return self._handleDragMove( e,
960 e.clientX, e.clientY );
963 return self._handleDragStop( e );
966 return !self._didDrag;
969 var old = self.getScrollPosition();
970 self.scrollTo( -old.x,
971 -(old.y - e.originalEvent.wheelDelta) );
976 this._dragEvt = "touchstart touchmove touchend click";
978 this._dragCB = function ( e ) {
979 var touches = e.originalEvent.touches;
983 if ( touches.length != 1) {
987 return self._handleDragStart( e,
988 touches[0].pageX, touches[0].pageY );
991 if ( touches.length != 1) {
995 return self._handleDragMove( e,
996 touches[0].pageX, touches[0].pageY );
999 if ( touches.length != 0) {
1003 return self._handleDragStop( e );
1006 return !self._didDrag;
1011 $v.bind( this._dragEvt, this._dragCB );
1013 $v.bind( "keydown", function ( e ) {
1016 scroll_top = $( window ).scrollTop() - window.screenTop,
1019 if ( e.keyCode == 9 ) {
1023 elem = $c.find(".ui-focus");
1025 if ( elem === undefined ) {
1029 elem_top = elem.offset().top - scroll_top;
1030 screen_h = $c.offset().top + $c.height() - elem.height();
1032 if ( self._softkeyboard ) {
1033 screen_h -= self._softkeyboardHeight;
1036 if ( ( elem_top < $c.offset().top ) || ( elem_top > screen_h ) ) {
1037 self.scrollTo( 0, self._sy -
1038 ( elem_top - $c.offset().top - elem.height() ) );
1044 $v.bind( "keyup", function ( e ) {
1048 scroll_top = $( window ).scrollTop() - window.screenTop,
1051 if ( e.keyCode != 9 ) {
1057 input = $( this ).find(":input");
1059 for ( i = 0; i < input.length; i++ ) {
1060 if ( !$( input[i] ).hasClass("ui-focus") ) {
1064 if ( i + 1 == input.length ) {
1065 elem = $( input[0] );
1067 elem = $( input[i + 1] );
1070 elem_top = elem.offset().top - scroll_top;
1071 screen_h = $c.offset().top + $c.height() - elem.height();
1073 if ( self._softkeyboard ) {
1074 screen_h -= self._softkeyboardHeight;
1077 if ( ( elem_top < 0 ) || ( elem_top > screen_h ) ) {
1078 self.scrollTo( 0, self._sy - elem_top +
1079 elem.height() + $c.offset().top, 0);
1090 $c.bind( "updatelayout", function ( e ) {
1093 view_h = self._getViewHeight();
1095 if ( !$c.height() || !view_h ) {
1096 self.scrollTo( 0, 0, 0 );
1100 sy = $c.height() - view_h;
1101 vh = view_h - self._view_height;
1103 self._view_height = view_h;
1105 if ( vh == 0 || vh > $c.height() / 2 ) {
1110 self.scrollTo( 0, 0, 0 );
1111 } else if ( self._sy - sy <= -vh ) {
1112 self.scrollTo( 0, self._sy,
1113 self.options.snapbackDuration );
1114 } else if ( self._sy - sy <= vh + self.options.moveThreshold ) {
1115 self.scrollTo( 0, sy,
1116 self.options.snapbackDuration );
1120 $( window ).bind( "resize", function ( e ) {
1122 view_h = self._getViewHeight();
1124 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1128 if ( !$c.height() || !view_h ) {
1132 focused = $c.find(".ui-focus");
1135 focused.trigger("resize.scrollview");
1138 /* calibration - after triggered throttledresize */
1139 setTimeout( function () {
1140 if ( self._sy < $c.height() - self._getViewHeight() ) {
1141 self.scrollTo( 0, $c.height() - self._getViewHeight(),
1142 self.options.overshootDuration );
1146 self._view_height = view_h;
1149 $( window ).bind( "vmouseout", function ( e ) {
1150 var drag_stop = false;
1152 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1156 if ( !self._dragging ) {
1160 if ( e.pageX < 0 || e.pageX > $( window ).width() ) {
1164 if ( e.pageY < 0 || e.pageY > $( window ).height() ) {
1169 self._hideScrollBars();
1170 self._hideOverflowIndicator();
1171 self._disableTracking();
1175 this._softkeyboard = false;
1176 this._softkeyboardHeight = 0;
1178 window.addEventListener( "softkeyboardchange", function ( e ) {
1179 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1183 self._softkeyboard = ( e.state === "on" ? true : false );
1184 self._softkeyboardHeight = parseInt( e.height ) *
1185 ( $( window ).width() / window.screen.availWidth );
1188 $c.closest(".ui-page")
1189 .bind( "pageshow", function ( e ) {
1190 /* should be called after pagelayout */
1191 setTimeout( function () {
1192 self._view_height = self._getViewHeight();
1193 self._set_scrollbar_size();
1194 self._setScrollPosition( self._sx, self._sy );
1195 self._showScrollBars( 2000 );
1200 _add_scrollbar: function () {
1201 var $c = this._$clip,
1202 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
1203 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
1205 if ( !this.options.showScrollBars ) {
1209 if ( this._vTracker ) {
1210 $c.append( prefix + "y" + suffix );
1211 this._$vScrollBar = $c.children(".ui-scrollbar-y");
1213 if ( this._hTracker ) {
1214 $c.append( prefix + "x" + suffix );
1215 this._$hScrollBar = $c.children(".ui-scrollbar-x");
1218 this._scrollbar_showed = false;
1221 _add_scroll_jump: function () {
1222 var $c = this._$clip,
1227 if ( !this.options.scrollJump ) {
1231 if ( this._vTracker ) {
1232 top_btn = $( '<div class="ui-scroll-jump-top-bg">' +
1233 '<div data-role="button" data-inline="true" data-icon="scrolltop" data-style="box"></div></div>' );
1234 $c.append( top_btn ).trigger("create");
1236 top_btn.bind( "vclick", function () {
1237 self.scrollTo( 0, 0, self.options.overshootDuration );
1241 if ( this._hTracker ) {
1242 left_btn = $( '<div class="ui-scroll-jump-left-bg">' +
1243 '<div data-role="button" data-inline="true" data-icon="scrollleft" data-style="box"></div></div>' );
1244 $c.append( left_btn ).trigger("create");
1246 left_btn.bind( "vclick", function () {
1247 self.scrollTo( 0, 0, self.options.overshootDuration );
1252 _add_overflow_indicator: function () {
1253 if ( !this.options.overflowEnable ) {
1257 this._overflow_top = $( '<div class="ui-overflow-indicator-top"></div>' );
1258 this._overflow_bottom = $( '<div class="ui-overflow-indicator-bottom"></div>' );
1260 this._$clip.append( this._overflow_top );
1261 this._$clip.append( this._overflow_bottom );
1263 this._opacity_top = "0.5";
1264 this._opacity_bottom = "0.5";
1265 this._overflow_showed = false;
1268 _set_scrollbar_size: function () {
1269 var $c = this._$clip,
1277 if ( !this.options.showScrollBars ) {
1281 if ( this._hTracker ) {
1284 this._maxX = cw - vw;
1286 if ( this._maxX > 0 ) {
1289 if ( this._$hScrollBar && vw ) {
1290 thumb = this._$hScrollBar.find(".ui-scrollbar-thumb");
1291 thumb.css( "width", (cw >= vw ? "0" :
1292 (Math.floor(cw / vw * 100) || 1) + "%") );
1296 if ( this._vTracker ) {
1298 vh = this._getViewHeight();
1299 this._maxY = ch - vh;
1301 if ( this._maxY > 0 ) {
1304 if ( this._$vScrollBar && vh ) {
1305 thumb = this._$vScrollBar.find(".ui-scrollbar-thumb");
1306 thumb.css( "height", (ch >= vh ? "0" :
1307 (Math.floor(ch / vh * 100) || 1) + "%") );
1309 this._overflowAvail = !!thumb.height();
1315 $.extend( MomentumTracker.prototype, {
1316 start: function ( pos, speed, duration, minPos, maxPos ) {
1317 var tstate = ( pos < minPos || pos > maxPos ) ?
1318 tstates.snapback : tstates.scrolling,
1321 this.state = ( speed !== 0 ) ? tstate : tstates.done;
1324 this.duration = ( this.state === tstates.snapback ) ?
1325 this.options.snapbackDuration : duration;
1326 this.minPos = minPos;
1327 this.maxPos = maxPos;
1329 this.fromPos = ( this.state === tstates.snapback ) ? this.pos : 0;
1330 pos_temp = ( this.pos < this.minPos ) ? this.minPos : this.maxPos;
1331 this.toPos = ( this.state === tstates.snapback ) ? pos_temp : 0;
1333 this.startTime = getCurrentTime();
1336 reset: function () {
1337 this.state = tstates.done;
1346 update: function ( overshootEnable ) {
1347 var state = this.state,
1348 cur_time = getCurrentTime(),
1349 duration = this.duration,
1350 elapsed = cur_time - this.startTime,
1355 if ( state === tstates.done ) {
1359 elapsed = elapsed > duration ? duration : elapsed;
1361 this.remained = duration - elapsed;
1363 if ( state === tstates.scrolling || state === tstates.overshot ) {
1365 ( 1 - $.easing[this.easing]( elapsed / duration,
1366 elapsed, 0, 1, duration ) );
1370 didOverShoot = ( state === tstates.scrolling ) &&
1371 ( x < this.minPos || x > this.maxPos );
1373 if ( didOverShoot ) {
1374 x = ( x < this.minPos ) ? this.minPos : this.maxPos;
1379 if ( state === tstates.overshot ) {
1380 if ( !overshootEnable ) {
1381 this.state = tstates.done;
1383 if ( elapsed >= duration ) {
1384 this.state = tstates.snapback;
1385 this.fromPos = this.pos;
1386 this.toPos = ( x < this.minPos ) ?
1387 this.minPos : this.maxPos;
1388 this.duration = this.options.snapbackDuration;
1389 this.startTime = cur_time;
1392 } else if ( state === tstates.scrolling ) {
1393 if ( didOverShoot && overshootEnable ) {
1394 this.state = tstates.overshot;
1395 this.speed = dx / 2;
1396 this.duration = this.options.overshootDuration;
1397 this.startTime = cur_time;
1398 } else if ( elapsed >= duration ) {
1399 this.state = tstates.done;
1402 } else if ( state === tstates.snapback ) {
1403 if ( elapsed >= duration ) {
1404 this.pos = this.toPos;
1405 this.state = tstates.done;
1407 this.pos = this.fromPos + (( this.toPos - this.fromPos ) *
1408 $.easing[this.easing]( elapsed / duration,
1409 elapsed, 0, 1, duration ));
1417 return this.state === tstates.done;
1420 isMin: function () {
1421 return this.pos === this.minPos;
1424 isMax: function () {
1425 return this.pos === this.maxPos;
1428 isAvail: function () {
1429 return !( this.minPos === this.maxPos );
1432 getRemained: function () {
1433 return this.remained;
1436 getPosition: function () {
1441 $( document ).bind( 'pagecreate create', function ( e ) {
1442 var $page = $( e.target ),
1443 content_scroll = $page.find(".ui-content").jqmData("scroll");
1445 /* content scroll */
1446 if ( $.support.scrollview === undefined ) {
1447 $.support.scrollview = true;
1450 if ( $.support.scrollview === true && content_scroll === undefined ) {
1451 content_scroll = "y";
1454 if ( content_scroll !== "y" ) {
1455 content_scroll = "none";
1458 $page.find(".ui-content").attr( "data-scroll", content_scroll );
1460 $page.find(":jqmData(scroll)").not(".ui-scrollview-clip").each( function () {
1461 if ( $( this ).hasClass("ui-scrolllistview") ) {
1462 $( this ).scrolllistview();
1464 var st = $( this ).jqmData("scroll"),
1465 dir = st && ( st.search(/^[xy]/) !== -1 ) ? st : null,
1466 content = $(this).hasClass("ui-content"),
1469 if ( st === "none" ) {
1474 direction: dir || undefined,
1475 overflowEnable: content,
1476 scrollMethod: $( this ).jqmData("scroll-method") || undefined,
1477 scrollJump: $( this ).jqmData("scroll-jump") || undefined
1480 $( this ).scrollview( opts );
1485 $( document ).bind( 'pageshow', function ( e ) {
1486 var $page = $( e.target ),
1487 scroll = $page.find(".ui-content").jqmData("scroll");
1489 if ( scroll === "y" ) {
1490 resizePageContentHeight( e.target );
1494 }( jQuery, window, document ) );