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 var scroll_height = this._getViewHeight() - this._$clip.height();
301 if ( this._softkeyboard ) {
302 if ( this._bouncing_dir ) {
303 this._outerScroll( -scroll_height - this._softkeyboardHeight,
306 this._outerScroll( this._softkeyboardHeight, scroll_height );
311 if ( dir === "in" ) {
312 if ( this._bouncing ) {
316 this._bouncing = true;
317 this._bouncing_count = 1;
319 this._bouncing_org_x = 1;
320 this._bouncing_org_y = 1;
322 this._bouncing_x = 0.99;
323 this._bouncing_y = 0.99;
325 this._setOverflowIndicator( this._bouncing_dir );
326 } else if ( dir === "out" ) {
327 if ( !this._bouncing ) {
331 this._bouncing = false;
332 this._bouncing_count = 1;
334 this._bouncing_org_x = this._bouncing_x;
335 this._bouncing_org_y = this._bouncing_y;
337 this._bouncing_x = 1;
338 this._bouncing_y = 1;
339 this._setOverflowIndicator( this._bouncing_dir );
344 this._doBouncing( $ele, dir );
347 _doBouncing: function ( $ele, dir ) {
355 if ( $.support.cssTransform3d ) {
356 translate = "translate3d(" + this._sx + "px," + this._sy + "px, 0px)";
358 translate = "translate(" + this._sx + "px," + this._sy + "px)";
361 if ( dir === "in" ) {
362 x_rate = this._bouncing_org_x - ( this._bouncing_org_x -
363 this._bouncing_x ) / frame * this._bouncing_count;
364 y_rate = this._bouncing_org_y - ( this._bouncing_org_y -
365 this._bouncing_y ) / frame * this._bouncing_count;
367 translate += " scale(" + x_rate + "," + y_rate + ")";
368 } else if ( dir === "out" ) {
369 x_rate = this._bouncing_org_x + ( this._bouncing_x -
370 this._bouncing_org_x ) / frame * this._bouncing_count;
371 y_rate = this._bouncing_org_y + ( this._bouncing_y -
372 this._bouncing_org_y ) / frame * this._bouncing_count;
374 translate += " scale(" + x_rate + "," + y_rate + ")";
379 if ( this._bouncing_dir ) {
380 origin = "50% " + ( this._bouncing_y * 100 - 10 ) + "%";
386 "-moz-transform": translate,
387 "-webkit-transform": translate,
388 "-ms-transform": translate,
389 "-o-transform": translate,
390 "transform": translate,
391 "-webkit-transform-origin": origin,
394 this._bouncing_count++;
396 if ( this._bouncing_count > frame ) {
400 setTimeout( function () {
401 self._doBouncing( $ele, dir );
402 }, this._timerInterval );
405 _setCalibration: function ( x, y ) {
406 if ( this.options.overshootEnable ) {
412 var $v = this._$view,
414 dirLock = this._directionLock,
418 if ( dirLock !== "y" && this._hTracker ) {
419 scroll_width = $v.width() - $c.width();
423 } else if ( x < -scroll_width ) {
424 this._sx = -scroll_width;
429 if ( scroll_width < 0 ) {
434 if ( dirLock !== "x" && this._vTracker ) {
435 scroll_height = this._getViewHeight() - $c.height();
440 if ( scroll_height > 0 ) {
441 this._bouncing_dir = 0;
442 this._setBouncing( this._$view, "in" );
444 } else if ( y < -scroll_height ) {
445 this._sy = -scroll_height;
447 if ( scroll_height > 0 ) {
448 this._bouncing_dir = 1;
449 this._setBouncing( this._$view, "in" );
452 if ( this._bouncing && this._sy !== y ) {
453 this._bouncing = false;
459 if ( scroll_height < 0 ) {
465 _setScrollPosition: function ( x, y, duration ) {
466 var $v = this._$view,
467 sm = this.options.scrollMethod,
468 $vsb = this._$vScrollBar,
469 $hsb = this._$hScrollBar,
472 this._setCalibration( x, y );
477 if ( sm === "translate" ) {
478 this._setElementTransform( $v, x + "px", y + "px", duration );
480 $v.css( {left: x + "px", top: y + "px"} );
484 $sbt = $vsb.find(".ui-scrollbar-thumb");
486 if ( sm === "translate" ) {
487 this._setElementTransform( $sbt, "0px",
488 -y / this._getViewHeight() * $sbt.parent().height() + "px",
491 $sbt.css( "top", -y / this._getViewHeight() * 100 + "%" );
496 $sbt = $hsb.find(".ui-scrollbar-thumb");
498 if ( sm === "translate" ) {
499 this._setElementTransform( $sbt,
500 -x / $v.width() * $sbt.parent().width() + "px", "0px",
503 $sbt.css("left", -x / $v.width() * 100 + "%");
508 _outerScroll: function ( y, scroll_height ) {
510 top = $( window ).scrollTop() - window.screenTop,
512 duration = this.options.snapbackDuration,
513 start = getCurrentTime(),
516 if ( !this.options.outerScrollEnable ) {
520 if ( this._$clip.jqmData("scroll") !== "y" ) {
524 if ( this._outerScrolling ) {
529 sy = ( window.screenTop ? window.screenTop : -y );
530 } else if ( y < -scroll_height ) {
531 sy = -y - scroll_height;
536 tfunc = function () {
537 var elapsed = getCurrentTime() - start;
539 if ( elapsed >= duration ) {
540 window.scrollTo( 0, top + sy );
541 self._outerScrolling = undefined;
545 ec = $.easing.easeOutQuad( elapsed / duration,
546 elapsed, 0, 1, duration );
548 window.scrollTo( 0, top + ( sy * ec ) );
549 self._outerScrolling = setTimeout( tfunc, self._timerInterval );
552 this._outerScrolling = setTimeout( tfunc, self._timerInterval );
555 _scrollTo: function ( x, y, duration ) {
557 start = getCurrentTime(),
558 efunc = $.easing.easeOutQuad,
568 tfunc = function () {
569 var elapsed = getCurrentTime() - start,
572 if ( elapsed >= duration ) {
574 self._setScrollPosition( x, y );
576 ec = efunc( elapsed / duration, elapsed, 0, 1, duration );
578 self._setScrollPosition( sx + ( dx * ec ), sy + ( dy * ec ) );
579 self._timerID = setTimeout( tfunc, self._timerInterval );
583 this._timerID = setTimeout( tfunc, this._timerInterval );
586 scrollTo: function ( x, y, duration ) {
589 if ( !duration || this.options.scrollMethod === "translate" ) {
590 this._setScrollPosition( x, y, duration );
592 this._scrollTo( x, y, duration );
596 getScrollPosition: function () {
597 return { x: -this._sx, y: -this._sy };
600 _getScrollHierarchy: function () {
604 this._$clip.parents( ".ui-scrollview-clip").each( function () {
605 d = $( this ).jqmData("scrollview");
613 _getAncestorByDirection: function ( dir ) {
614 var svh = this._getScrollHierarchy(),
621 svdir = sv.options.direction;
623 if (!svdir || svdir === dir) {
630 _handleDragStart: function ( e, ex, ey ) {
633 this._didDrag = false;
634 this._skip_dragging = false;
636 var target = $( e.target ),
639 svdir = this.options.direction;
641 /* should prevent the default behavior when click the button */
642 this._is_button = target.is( '.ui-btn-text' ) ||
643 target.is( '.ui-btn-inner' ) ||
644 target.is( '.ui-btn-inner .ui-icon' );
646 if ( this._is_button ) {
647 if ( target.parents('.ui-slider-handle').length ) {
648 this._skip_dragging = true;
654 * We need to prevent the default behavior to
655 * suppress accidental selection of text, etc.
657 this._is_inputbox = target.is(':input') ||
658 target.parents(':input').length > 0;
660 if ( this._is_inputbox ) {
661 target.one( "resize.scrollview", function () {
662 if ( ey > $c.height() ) {
663 self.scrollTo( -ex, self._sy - ey + $c.height(),
664 self.options.snapbackDuration );
669 if ( this.options.eventType === "mouse" && !this._is_inputbox && !this._is_button ) {
676 this._doSnapBackX = false;
677 this._doSnapBackY = false;
680 this._directionLock = "";
683 this._enableTracking();
685 this._set_scrollbar_size();
688 _propagateDragMove: function ( sv, e, ex, ey, dir ) {
689 this._hideScrollBars();
690 this._hideOverflowIndicator();
691 this._disableTracking();
692 sv._handleDragStart( e, ex, ey );
693 sv._directionLock = dir;
694 sv._didDrag = this._didDrag;
697 _handleDragMove: function ( e, ex, ey ) {
698 if ( this._skip_dragging ) {
702 if ( !this._dragging ) {
706 if ( !this._is_inputbox && !this._is_button ) {
710 var mt = this.options.moveThreshold,
711 dx = ex - this._lastX,
712 dy = ey - this._lastY,
713 svdir = this.options.direction,
723 this._lastMove = getCurrentTime();
725 if ( !this._directionLock ) {
729 if ( x < mt && y < mt ) {
733 if ( x < y && (x / y) < 0.5 ) {
735 } else if ( x > y && (y / x) < 0.5 ) {
739 if ( svdir && dir && svdir !== dir ) {
741 * This scrollview can't handle the direction the user
742 * is attempting to scroll. Find an ancestor scrollview
743 * that can handle the request.
746 sv = this._getAncestorByDirection( dir );
748 this._propagateDragMove( sv, e, ex, ey, dir );
753 this._directionLock = svdir || (dir || "none");
758 dirLock = this._directionLock;
760 if ( dirLock !== "y" && this._hTracker ) {
765 this._doSnapBackX = false;
767 scope = ( newX > 0 || newX < this._maxX );
769 if ( scope && dirLock === "x" ) {
770 sv = this._getAncestorByDirection("x");
772 this._setScrollPosition( newX > 0 ?
773 0 : this._maxX, newY );
774 this._propagateDragMove( sv, e, ex, ey, dir );
778 newX = x + ( dx / 2 );
779 this._doSnapBackX = true;
783 if ( dirLock !== "x" && this._vTracker ) {
784 if ( Math.abs( this._startY - ey ) < mt && dirLock !== "xy" ) {
792 this._doSnapBackY = false;
794 scope = ( newY > 0 || newY < this._maxY );
796 if ( scope && dirLock === "y" ) {
797 sv = this._getAncestorByDirection("y");
799 this._setScrollPosition( newX,
800 newY > 0 ? 0 : this._maxY );
801 this._propagateDragMove( sv, e, ex, ey, dir );
805 newY = y + ( dy / 2 );
806 this._doSnapBackY = true;
810 if ( this.options.overshootEnable === false ) {
811 this._doSnapBackX = false;
812 this._doSnapBackY = false;
815 this._didDrag = true;
819 this._setScrollPosition( newX, newY );
821 this._showScrollBars();
822 this._showOverflowIndicator();
825 _handleDragStop: function ( e ) {
826 if ( this._skip_dragging ) {
830 var l = this._lastMove,
831 t = getCurrentTime(),
832 doScroll = (l && (t - l) <= this.options.moveIntervalThreshold),
833 sx = ( this._hTracker && this._speedX && doScroll ) ?
834 this._speedX : ( this._doSnapBackX ? 1 : 0 ),
835 sy = ( this._vTracker && this._speedY && doScroll ) ?
836 this._speedY : ( this._doSnapBackY ? 1 : 0 ),
837 svdir = this.options.direction,
842 if ( !this._setGestureScroll( sx, sy ) ) {
843 this._startMScroll( sx, sy );
846 this._hideScrollBars();
847 this._hideOverflowIndicator();
850 this._disableTracking();
852 if ( this._bouncing ) {
853 this._setBouncing( this._$view, "out" );
854 this._hideScrollBars();
855 this._hideOverflowIndicator();
858 return !this._didDrag;
861 _setGestureScroll: function ( sx, sy ) {
863 reset = function () {
864 clearTimeout( self._gesture_timer );
865 self._gesture_dir = 0;
866 self._gesture_count = 0;
867 self._gesture_timer = undefined;
874 dir = sy > 0 ? 1 : -1;
876 if ( !this._gesture_timer ) {
877 this._gesture_count = 1;
878 this._gesture_dir = dir;
880 this._gesture_timer = setTimeout( function () {
887 if ( this._gesture_dir !== dir ) {
892 this._gesture_count++;
894 if ( this._gesture_count === 3 ) {
896 this.scrollTo( 0, 0, this.options.overshootDuration );
898 this.scrollTo( 0, -( this._getViewHeight() - this._$clip.height() ),
899 this.options.overshootDuration );
909 _enableTracking: function () {
910 this._dragging = true;
913 _disableTracking: function () {
914 this._dragging = false;
917 _showScrollBars: function ( interval ) {
918 var vclass = "ui-scrollbar-visible",
921 if ( !this.options.showScrollBars ) {
924 if ( this._scrollbar_showed ) {
928 if ( this._$vScrollBar ) {
929 this._$vScrollBar.addClass( vclass );
931 if ( this._$hScrollBar ) {
932 this._$hScrollBar.addClass( vclass );
935 this._scrollbar_showed = true;
938 setTimeout( function () {
939 self._hideScrollBars();
944 _hideScrollBars: function () {
945 var vclass = "ui-scrollbar-visible";
947 if ( !this.options.showScrollBars ) {
950 if ( !this._scrollbar_showed ) {
954 if ( this._$vScrollBar ) {
955 this._$vScrollBar.removeClass( vclass );
957 if ( this._$hScrollBar ) {
958 this._$hScrollBar.removeClass( vclass );
961 this._scrollbar_showed = false;
964 _resetOverflowIndicator: function () {
965 if ( !this.options.overflowEnable || !this._overflowAvail || this._softkeyboard ) {
969 this._overflow_top.css( "-webkit-animation", "" );
970 this._overflow_bottom.css( "-webkit-animation", "" );
973 _setOverflowIndicator: function ( dir ) {
975 this._opacity_top = "0.2";
976 this._opacity_bottom = "0.8";
977 } else if ( dir === 0 ) {
978 this._opacity_top = "0.8";
979 this._opacity_bottom = "0.2";
981 this._opacity_top = "0.5";
982 this._opacity_bottom = "0.5";
986 _getOverflowIndicator: function ( opacity ) {
987 if ( opacity === "0.2" ) {
989 } else if ( opacity === "0.8" ) {
995 _showOverflowIndicator: function () {
996 if ( !this.options.overflowEnable || !this._overflowAvail || this._softkeyboard ) {
1000 this._overflow_top.css( "opacity", this._opacity_top );
1001 this._overflow_bottom.css( "opacity", this._opacity_bottom );
1003 if ( this._overflow_showed === true ) {
1007 this._overflow_top.css( "-webkit-animation", "ui-overflow-show" +
1008 this._getOverflowIndicator( this._opacity_top ) + " 0.3s 1 ease" );
1009 this._overflow_bottom.css( "-webkit-animation", "ui-overflow-show" +
1010 this._getOverflowIndicator( this._opacity_bottom ) + " 0.3s 1 ease" );
1012 this._overflow_showed = true;
1015 _hideOverflowIndicator: function () {
1019 if ( !this.options.overflowEnable || !this._overflowAvail || this._softkeyboard ) {
1023 if ( this._overflow_showed === false ) {
1027 opacity_top = this._overflow_top.css( "opacity" );
1028 opacity_bottom = this._overflow_bottom.css( "opacity" );
1030 this._overflow_top.css( "opacity", "0" );
1031 this._overflow_bottom.css( "opacity", "0" );
1033 this._overflow_top.css( "-webkit-animation", "ui-overflow-hide" +
1034 this._getOverflowIndicator( opacity_top ) + " 0.5s 1 ease" );
1035 this._overflow_bottom.css( "-webkit-animation", "ui-overflow-hide" +
1036 this._getOverflowIndicator( opacity_bottom ) + " 0.5s 1 ease" );
1038 this._overflow_showed = false;
1039 this._setOverflowIndicator();
1042 _add_event: function () {
1047 if ( this.options.eventType === "mouse" ) {
1048 this._dragEvt = "mousedown mousemove mouseup click mousewheel";
1050 this._dragCB = function ( e ) {
1053 return self._handleDragStart( e,
1054 e.clientX, e.clientY );
1057 return self._handleDragMove( e,
1058 e.clientX, e.clientY );
1061 return self._handleDragStop( e );
1064 return !self._didDrag;
1067 var old = self.getScrollPosition();
1068 self.scrollTo( -old.x,
1069 -(old.y - e.originalEvent.wheelDelta) );
1074 this._dragEvt = "touchstart touchmove touchend click";
1076 this._dragCB = function ( e ) {
1081 t = e.originalEvent.targetTouches[0];
1082 return self._handleDragStart( e,
1086 t = e.originalEvent.targetTouches[0];
1087 return self._handleDragMove( e,
1091 return self._handleDragStop( e );
1094 return !self._didDrag;
1099 $v.bind( this._dragEvt, this._dragCB );
1101 $c.bind( "updatelayout", function ( e ) {
1104 view_h = self._getViewHeight();
1106 if ( !$c.height() || !view_h ) {
1107 self.scrollTo( 0, 0, 0 );
1111 sy = $c.height() - view_h;
1112 vh = view_h - self._view_height;
1114 self._view_height = view_h;
1116 if ( vh == 0 || vh > $c.height() / 2 ) {
1120 if ( self._sy - sy <= -vh ) {
1121 self.scrollTo( 0, sy,
1122 self.options.snapbackDuration );
1123 } else if ( self._sy - sy <= vh + self.options.moveThreshold ) {
1124 self.scrollTo( 0, sy,
1125 self.options.snapbackDuration );
1129 $( window ).bind( "resize", function ( e ) {
1131 view_h = self._getViewHeight();
1133 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1137 if ( !$c.height() || !view_h ) {
1141 focused = $c.find(".ui-focus");
1144 focused.trigger("resize.scrollview");
1147 /* calibration - after triggered throttledresize */
1148 setTimeout( function () {
1149 if ( self._sy < $c.height() - self._getViewHeight() ) {
1150 self.scrollTo( 0, $c.height() - self._getViewHeight(),
1151 self.options.overshootDuration );
1155 self._view_height = view_h;
1158 $( window ).bind( "vmouseout", function ( e ) {
1159 var drag_stop = false;
1161 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1165 if ( !self._dragging ) {
1169 if ( e.pageX < 0 || e.pageX > $( window ).width() ) {
1173 if ( e.pageY < 0 || e.pageY > $( window ).height() ) {
1178 self._hideScrollBars();
1179 self._hideOverflowIndicator();
1180 self._disableTracking();
1184 this._softkeyboard = false;
1185 this._softkeyboardHeight = 0;
1187 window.addEventListener( "softkeyboardchange", function ( e ) {
1188 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1192 self._softkeyboard = ( e.state === "on" ? true : false );
1193 self._softkeyboardHeight = e.height;
1196 $c.closest(".ui-page")
1197 .one( "pageshow", function ( e ) {
1198 self._view_offset = self._$view.offset().top - self._$clip.offset().top;
1199 self._view_height = self._getViewHeight();
1201 .bind( "pageshow", function ( e ) {
1202 /* should be called after pagelayout */
1203 setTimeout( function () {
1204 self._set_scrollbar_size();
1205 self._setScrollPosition( self._sx, self._sy );
1206 self._showScrollBars( 2000 );
1207 self._resetOverflowIndicator();
1212 _add_scrollbar: function () {
1213 var $c = this._$clip,
1214 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
1215 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
1217 if ( !this.options.showScrollBars ) {
1221 if ( this._vTracker ) {
1222 $c.append( prefix + "y" + suffix );
1223 this._$vScrollBar = $c.children(".ui-scrollbar-y");
1225 if ( this._hTracker ) {
1226 $c.append( prefix + "x" + suffix );
1227 this._$hScrollBar = $c.children(".ui-scrollbar-x");
1230 this._scrollbar_showed = false;
1233 _add_scroll_jump: function () {
1234 var $c = this._$clip,
1239 if ( !this.options.scrollJump ) {
1243 if ( this._vTracker ) {
1244 top_btn = $( '<div class="ui-scroll-jump-top-bg">' +
1245 '<div data-role="button" data-inline="true" data-icon="jumptop" style="width:37px;height:37px">.</div></div>' );
1246 $c.append( top_btn ).trigger("create");
1248 top_btn.bind( "vclick", function () {
1249 self.scrollTo( 0, 0, self.options.overshootDuration );
1253 if ( this._hTracker ) {
1254 left_btn = $( '<div class="ui-scroll-jump-left-bg">' +
1255 '<div data-role="button" data-inline="true" data-icon="jumpleft" style="width:37px;height:37px">.</div></div>' );
1256 $c.append( left_btn ).trigger("create");
1258 left_btn.bind( "vclick", function () {
1259 self.scrollTo( 0, 0, self.options.overshootDuration );
1264 _add_overflow_indicator: function () {
1265 if ( !this.options.overflowEnable ) {
1269 this._overflow_top = $( '<div class="ui-overflow-indicator-top"></div>' );
1270 this._overflow_bottom = $( '<div class="ui-overflow-indicator-bottom"></div>' );
1272 this._$clip.append( this._overflow_top );
1273 this._$clip.append( this._overflow_bottom );
1275 this._opacity_top = "0.5";
1276 this._opacity_bottom = "0.5";
1277 this._overflow_showed = false;
1280 _set_scrollbar_size: function () {
1281 var $c = this._$clip,
1289 if ( !this.options.showScrollBars ) {
1293 if ( this._hTracker ) {
1296 this._maxX = cw - vw;
1298 if ( this._maxX > 0 ) {
1301 if ( this._$hScrollBar && vw ) {
1302 thumb = this._$hScrollBar.find(".ui-scrollbar-thumb");
1303 thumb.css( "width", (cw >= vw ? "0" :
1304 (Math.floor(cw / vw * 100) || 1) + "%") );
1308 if ( this._vTracker ) {
1310 vh = this._getViewHeight();
1311 this._maxY = ch - vh;
1313 if ( this._maxY > 0 ) {
1316 if ( this._$vScrollBar && vh ) {
1317 thumb = this._$vScrollBar.find(".ui-scrollbar-thumb");
1318 thumb.css( "height", (ch >= vh ? "0" :
1319 (Math.floor(ch / vh * 100) || 1) + "%") );
1321 this._overflowAvail = !!thumb.height();
1327 $.extend( MomentumTracker.prototype, {
1328 start: function ( pos, speed, duration, minPos, maxPos ) {
1329 var tstate = ( pos < minPos || pos > maxPos ) ?
1330 tstates.snapback : tstates.scrolling,
1333 this.state = ( speed !== 0 ) ? tstate : tstates.done;
1336 this.duration = ( this.state === tstates.snapback ) ?
1337 this.options.snapbackDuration : duration;
1338 this.minPos = minPos;
1339 this.maxPos = maxPos;
1341 this.fromPos = ( this.state === tstates.snapback ) ? this.pos : 0;
1342 pos_temp = ( this.pos < this.minPos ) ? this.minPos : this.maxPos;
1343 this.toPos = ( this.state === tstates.snapback ) ? pos_temp : 0;
1345 this.startTime = getCurrentTime();
1348 reset: function () {
1349 this.state = tstates.done;
1358 update: function ( overshootEnable ) {
1359 var state = this.state,
1360 cur_time = getCurrentTime(),
1361 duration = this.duration,
1362 elapsed = cur_time - this.startTime,
1367 if ( state === tstates.done ) {
1371 elapsed = elapsed > duration ? duration : elapsed;
1373 this.remained = duration - elapsed;
1375 if ( state === tstates.scrolling || state === tstates.overshot ) {
1377 ( 1 - $.easing[this.easing]( elapsed / duration,
1378 elapsed, 0, 1, duration ) );
1382 didOverShoot = ( state === tstates.scrolling ) &&
1383 ( x < this.minPos || x > this.maxPos );
1385 if ( didOverShoot ) {
1386 x = ( x < this.minPos ) ? this.minPos : this.maxPos;
1391 if ( state === tstates.overshot ) {
1392 if ( !overshootEnable ) {
1393 this.state = tstates.done;
1395 if ( elapsed >= duration ) {
1396 this.state = tstates.snapback;
1397 this.fromPos = this.pos;
1398 this.toPos = ( x < this.minPos ) ?
1399 this.minPos : this.maxPos;
1400 this.duration = this.options.snapbackDuration;
1401 this.startTime = cur_time;
1404 } else if ( state === tstates.scrolling ) {
1405 if ( didOverShoot && overshootEnable ) {
1406 this.state = tstates.overshot;
1407 this.speed = dx / 2;
1408 this.duration = this.options.overshootDuration;
1409 this.startTime = cur_time;
1410 } else if ( elapsed >= duration ) {
1411 this.state = tstates.done;
1414 } else if ( state === tstates.snapback ) {
1415 if ( elapsed >= duration ) {
1416 this.pos = this.toPos;
1417 this.state = tstates.done;
1419 this.pos = this.fromPos + (( this.toPos - this.fromPos ) *
1420 $.easing[this.easing]( elapsed / duration,
1421 elapsed, 0, 1, duration ));
1429 return this.state === tstates.done;
1432 isMin: function () {
1433 return this.pos === this.minPos;
1436 isMax: function () {
1437 return this.pos === this.maxPos;
1440 isAvail: function () {
1441 return !( this.minPos === this.maxPos );
1444 getRemained: function () {
1445 return this.remained;
1448 getPosition: function () {
1453 $( document ).bind( 'pagecreate create', function ( e ) {
1454 var $page = $( e.target ),
1455 content_scroll = $page.find(".ui-content").jqmData("scroll");
1457 /* content scroll */
1458 if ( $.support.scrollview === undefined ) {
1459 $.support.scrollview = true;
1462 if ( $.support.scrollview === true && content_scroll === undefined ) {
1463 content_scroll = "y";
1466 if ( content_scroll !== "y" ) {
1467 content_scroll = "none";
1470 $page.find(".ui-content").attr( "data-scroll", content_scroll );
1472 $page.find(":jqmData(scroll):not(.ui-scrollview-clip)").each( function () {
1473 if ( $( this ).hasClass("ui-scrolllistview") ) {
1474 $( this ).scrolllistview();
1476 var st = $( this ).jqmData("scroll"),
1477 dir = st && ( st.search(/^[xy]/) !== -1 ) ? st : null,
1478 content = $(this).hasClass("ui-content"),
1481 if ( st === "none" ) {
1486 direction: dir || undefined,
1487 overflowEnable: content,
1488 scrollMethod: $( this ).jqmData("scroll-method") || undefined,
1489 scrollJump: $( this ).jqmData("scroll-jump") || undefined
1492 $( this ).scrollview( opts );
1497 $( document ).bind( 'pageshow', function ( e ) {
1498 var $page = $( e.target ),
1499 scroll = $page.find(".ui-content").jqmData("scroll");
1501 if ( scroll === "y" ) {
1502 resizePageContentHeight( e.target );
1506 }( jQuery, window, document ) );