6bc7f0475b257dc6fc1629b6f8adf9fd15cd4cf0
[framework/web/web-ui-fw.git] / src / widgets / common / js / jquery.mobile.tizen.scrollview.js
1 /*
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>
8 */
9
10 (function ( $, window, document, undefined ) {
11
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();
20
21                 $content.height( wh - (hh + fh) - (pt + pb) );
22         }
23
24         function MomentumTracker( options ) {
25                 this.options = $.extend( {}, options );
26                 this.easing = "easeOutQuad";
27                 this.reset();
28         }
29
30         var tstates = {
31                 scrolling: 0,
32                 overshot:  1,
33                 snapback:  2,
34                 done:      3
35         };
36
37         function getCurrentTime() {
38                 return ( new Date() ).getTime();
39         }
40
41         jQuery.widget( "tizen.scrollview", jQuery.mobile.widget, {
42                 options: {
43                         fps:               60,    // Frames per second in msecs.
44                         direction:         null,  // "x", "y", or null for both.
45
46                         scrollDuration:    2000,  // 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.
49
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.
52
53                         scrollMethod:      "translate",  // "translate", "position"
54                         startEventName:    "scrollstart",
55                         updateEventName:   "scrollupdate",
56                         stopEventName:     "scrollstop",
57
58                         eventType:         $.support.touch ? "touch" : "mouse",
59
60                         showScrollBars:    true,
61                         overshootEnable:   false,
62                         scrollJump:        false,
63                 },
64
65                 _makePositioned: function ( $ele ) {
66                         if ( $ele.css("position") === "static" ) {
67                                 $ele.css( "position", "relative" );
68                         }
69                 },
70
71                 _create: function () {
72                         var $page = $('.ui-page'),
73                                 direction,
74                                 self = this;
75
76                         this._$clip = $( this.element ).addClass("ui-scrollview-clip");
77                         this._$view = this._$clip.wrapInner("<div></div>").children()
78                                                         .addClass("ui-scrollview-view");
79
80                         if ( this.options.scrollMethod === "translate" ) {
81                                 if ( this._$view.css("transform") === undefined ) {
82                                         this.options.scrollMethod = "position";
83                                 }
84                         }
85
86                         this._$clip.css( "overflow", "hidden" );
87                         this._makePositioned( this._$clip );
88
89                         this._makePositioned( this._$view );
90                         this._$view.css( { left: 0, top: 0 } );
91                         this._view_height = this._$view.height();
92
93                         this._sx = 0;
94                         this._sy = 0;
95
96                         direction = this.options.direction;
97
98                         this._hTracker = ( direction !== "y" ) ?
99                                         new MomentumTracker( this.options ) : null;
100                         this._vTracker = ( direction !== "x" ) ?
101                                         new MomentumTracker( this.options ) : null;
102
103                         this._timerInterval = 1000 / this.options.fps;
104                         this._timerID = 0;
105
106                         this._timerCB = function () {
107                                 self._handleMomentumScroll();
108                         };
109
110                         this._add_event();
111                         this._add_scrollbar();
112                         this._add_scroll_jump();
113                 },
114
115                 _startMScroll: function ( speedX, speedY ) {
116                         var keepGoing = false,
117                                 duration = this.options.scrollDuration,
118                                 ht = this._hTracker,
119                                 vt = this._vTracker,
120                                 c,
121                                 v;
122
123                         this._stopMScroll();
124                         this._showScrollBars();
125
126                         this._$clip.trigger( this.options.startEventName );
127
128                         if ( ht ) {
129                                 c = this._$clip.width();
130                                 v = this._$view.width();
131
132                                 ht.start( this._sx, speedX,
133                                         duration, (v > c) ? -(v - c) : 0, 0 );
134                                 keepGoing = !ht.done();
135                         }
136
137                         if ( vt ) {
138                                 c = this._$clip.height();
139                                 v = this._$view.height() +
140                                         parseFloat( this._$view.css("padding-top") );
141
142                                 vt.start( this._sy, speedY,
143                                         duration, (v > c) ? -(v - c) : 0, 0 );
144                                 keepGoing = keepGoing || !vt.done();
145                         }
146
147                         if ( keepGoing ) {
148                                 this._timerID = setTimeout( this._timerCB, this._timerInterval );
149                         } else {
150                                 this._stopMScroll();
151                         }
152                 },
153
154                 _stopMScroll: function () {
155                         if ( this._timerID ) {
156                                 this._$clip.trigger( this.options.stopEventName );
157                                 clearTimeout( this._timerID );
158                         }
159                         this._timerID = 0;
160
161                         if ( this._vTracker ) {
162                                 this._vTracker.reset();
163                         }
164
165                         if ( this._hTracker ) {
166                                 this._hTracker.reset();
167                         }
168
169                         this._hideScrollBars();
170                 },
171
172                 _handleMomentumScroll: function () {
173                         var keepGoing = false,
174                                 x = 0,
175                                 y = 0,
176                                 vt = this._vTracker,
177                                 ht = this._hTracker;
178
179                         if ( this._outerScrolling ) {
180                                 return;
181                         }
182
183                         if ( vt ) {
184                                 vt.update( this.options.overshootEnable );
185                                 y = vt.getPosition();
186                                 keepGoing = !vt.done();
187                         }
188
189                         if ( ht ) {
190                                 ht.update( this.options.overshootEnable );
191                                 x = ht.getPosition();
192                                 keepGoing = keepGoing || !ht.done();
193                         }
194
195                         this._setScrollPosition( x, y );
196                         this._$clip.trigger( this.options.updateEventName,
197                                         [ { x: x, y: y } ] );
198
199                         if ( keepGoing ) {
200                                 this._timerID = setTimeout( this._timerCB, this._timerInterval );
201                         } else {
202                                 this._stopMScroll();
203                         }
204                 },
205
206                 _setElementTransform: function ( $ele, x, y, duration ) {
207                         var translate,
208                                 transition;
209
210                         if ( !duration || duration === undefined ) {
211                                 transition = "none";
212                         } else {
213                                 transition =  "-webkit-transform " + duration / 1000 + "s";
214                         }
215
216                         if ( $.support.cssTransform3d ) {
217                                 translate = "translate3d(" + x + "," + y + ", 0px)";
218                         } else {
219                                 translate = "translate(" + x + "," + y + ")";
220                         }
221
222                         $ele.css({
223                                 "-moz-transform": translate,
224                                 "-webkit-transform": translate,
225                                 "-ms-transform": translate,
226                                 "-o-transform": translate,
227                                 "transform": translate,
228                                 "-webkit-transition": transition
229                         });
230                 },
231
232                 _setCalibration: function ( x, y ) {
233                         if ( this.options.overshootEnable ) {
234                                 this._sx = x;
235                                 this._sy = y;
236                                 return;
237                         }
238
239                         var $v = this._$view,
240                                 $c = this._$clip,
241                                 dirLock = this._directionLock,
242                                 scroll_height = 0,
243                                 scroll_width = 0;
244
245                         if ( dirLock !== "y" && this._hTracker ) {
246                                 scroll_width = $v.width() - $c.width();
247
248                                 if ( x >= 0 ) {
249                                         this._sx = 0;
250                                 } else if ( x < -scroll_width ) {
251                                         this._sx = -scroll_width;
252                                 } else {
253                                         this._sx = x;
254                                 }
255
256                                 if ( scroll_width < 0 ) {
257                                         this._sx = 0;
258                                 }
259                         }
260
261                         if ( dirLock !== "x" && this._vTracker ) {
262                                 scroll_height = $v.height() - $c.height() +
263                                         parseFloat( $c.css("padding-top") ) +
264                                         parseFloat( $c.css("padding-bottom") );
265
266                                 this._outerScroll( y, scroll_height );
267
268                                 if ( y >= 0 ) {
269                                         this._sy = 0;
270                                 } else if ( y < -scroll_height ) {
271                                         this._sy = -scroll_height;
272                                 } else {
273                                         this._sy = y;
274                                 }
275
276                                 if ( scroll_height < 0 ) {
277                                         this._sy = 0;
278                                 }
279                         }
280                 },
281
282                 _setScrollPosition: function ( x, y, duration ) {
283                         var $v = this._$view,
284                                 sm = this.options.scrollMethod,
285                                 $vsb = this._$vScrollBar,
286                                 $hsb = this._$hScrollBar,
287                                 $sbt;
288
289                         this._setCalibration( x, y );
290
291                         if ( this._outerScrolling ) {
292                                 return;
293                         }
294
295                         x = this._sx;
296                         y = this._sy;
297
298                         if ( sm === "translate" ) {
299                                 this._setElementTransform( $v, x + "px", y + "px", duration );
300                         } else {
301                                 $v.css( {left: x + "px", top: y + "px"} );
302                         }
303
304                         if ( $vsb ) {
305                                 $sbt = $vsb.find(".ui-scrollbar-thumb");
306
307                                 if ( sm === "translate" ) {
308                                         this._setElementTransform( $sbt, "0px",
309                                                 -y / $v.height() * $sbt.parent().height() + "px",
310                                                 duration );
311                                 } else {
312                                         $sbt.css( "top", -y / $v.height() * 100 + "%" );
313                                 }
314                         }
315
316                         if ( $hsb ) {
317                                 $sbt = $hsb.find(".ui-scrollbar-thumb");
318
319                                 if ( sm === "translate" ) {
320                                         this._setElementTransform( $sbt,
321                                                 -x / $v.width() * $sbt.parent().width() + "px", "0px",
322                                                 duration);
323                                 } else {
324                                         $sbt.css("left", -x / $v.width() * 100 + "%");
325                                 }
326                         }
327                 },
328
329                 _outerScroll: function ( y, scroll_height ) {
330                         var self = this,
331                                 top = $( window ).scrollTop(),
332                                 sy = 0,
333                                 duration = this.options.snapbackDuration,
334                                 start = getCurrentTime(),
335                                 tfunc;
336
337                         if ( this._$clip.jqmData("scroll") !== "y" ) {
338                                 return;
339                         }
340
341                         if ( this._outerScrolling ) {
342                                 return;
343                         }
344
345                         if ( !this._dragging ) {
346                                 return;
347                         }
348
349                         if ( scroll_height < 0 ) {
350                                 return;
351                         }
352
353                         if ( y > 0 ) {
354                                 sy = -y;
355                         } else if ( y < -scroll_height ) {
356                                 sy = -y - scroll_height;
357                         } else {
358                                 return;
359                         }
360
361                         sy *= 10;
362
363                         tfunc = function () {
364                                 var elapsed = getCurrentTime() - start;
365
366                                 if ( elapsed >= duration ) {
367                                         window.scrollTo( 0, top + sy );
368                                         self._outerScrolling = undefined;
369                                 } else {
370                                         ec = $.easing.easeOutQuad( elapsed / duration, elapsed, 0, 1, duration );
371
372                                         window.scrollTo( 0, top + ( sy * ec ) );
373                                         self._outerScrolling = setTimeout( tfunc, self._timerInterval );
374                                 }
375                         };
376                         this._outerScrolling = setTimeout( tfunc, self._timerInterval );
377
378                         /* skip the srollview dragging */
379                         this._skip_dragging = true;
380                 },
381
382                 _scrollTo: function ( x, y, duration ) {
383                         var self = this,
384                                 start = getCurrentTime(),
385                                 efunc = $.easing.easeOutQuad,
386                                 sx = this._sx,
387                                 sy = this._sy,
388                                 dx = x - sx,
389                                 dy = y - sy,
390                                 tfunc;
391
392                         x = -x;
393                         y = -y;
394
395                         tfunc = function () {
396                                 var elapsed = getCurrentTime() - start,
397                                     ec;
398
399                                 if ( elapsed >= duration ) {
400                                         self._timerID = 0;
401                                         self._setScrollPosition( x, y );
402                                 } else {
403                                         ec = efunc( elapsed / duration, elapsed, 0, 1, duration );
404
405                                         self._setScrollPosition( sx + ( dx * ec ), sy + ( dy * ec ) );
406                                         self._timerID = setTimeout( tfunc, self._timerInterval );
407                                 }
408                         };
409
410                         this._timerID = setTimeout( tfunc, this._timerInterval );
411                 },
412
413                 scrollTo: function ( x, y, duration ) {
414                         this._stopMScroll();
415
416                         if ( !duration || this.options.scrollMethod === "translate" ) {
417                                 this._setScrollPosition( x, y, duration );
418                         } else {
419                                 this._scrollTo( x, y, duration );
420                         }
421                 },
422
423                 getScrollPosition: function () {
424                         return { x: -this._sx, y: -this._sy };
425                 },
426
427                 _getScrollHierarchy: function () {
428                         var svh = [],
429                                 d;
430
431                         this._$clip.parents( ".ui-scrollview-clip").each( function () {
432                                 d = $( this ).jqmData("scrollview");
433                                 if ( d ) {
434                                         svh.unshift( d );
435                                 }
436                         } );
437                         return svh;
438                 },
439
440                 _getAncestorByDirection: function ( dir ) {
441                         var svh = this._getScrollHierarchy(),
442                                 n = svh.length,
443                                 sv,
444                                 svdir;
445
446                         while ( 0 < n-- ) {
447                                 sv = svh[n];
448                                 svdir = sv.options.direction;
449
450                                 if (!svdir || svdir === dir) {
451                                         return sv;
452                                 }
453                         }
454                         return null;
455                 },
456
457                 _handleDragStart: function ( e, ex, ey ) {
458                         this._stopMScroll();
459
460                         this._didDrag = false;
461                         this._skip_dragging = false;
462
463                         var target = $( e.target ),
464                                 self = this,
465                                 $c = this._$clip,
466                                 svdir = this.options.direction;
467
468                         /* should prevent the default behavior when click the button */
469                         this._is_button = target.is( '.ui-btn-text' ) ||
470                                         target.is( '.ui-btn-inner' ) ||
471                                         target.is( '.ui-btn-inner .ui-icon' );
472
473                         /*
474                          * We need to prevent the default behavior to
475                          * suppress accidental selection of text, etc.
476                          */
477                         this._is_inputbox = target.is(':input') ||
478                                         target.parents(':input').length > 0;
479
480                         if ( this._is_inputbox ) {
481                                 target.one( "resize.scrollview", function () {
482                                         if ( ey > $c.height() ) {
483                                                 self.scrollTo( -ex, self._sy - ey + $c.height(),
484                                                         self.options.snapbackDuration );
485                                         }
486                                 });
487                         }
488
489                         this._lastX = ex;
490                         this._lastY = ey;
491                         this._startY = ey;
492                         this._doSnapBackX = false;
493                         this._doSnapBackY = false;
494                         this._speedX = 0;
495                         this._speedY = 0;
496                         this._directionLock = "";
497
498                         this._lastMove = 0;
499                         this._enableTracking();
500
501                         this._set_scrollbar_size();
502                 },
503
504                 _propagateDragMove: function ( sv, e, ex, ey, dir ) {
505                         this._hideScrollBars();
506                         this._disableTracking();
507                         sv._handleDragStart( e, ex, ey );
508                         sv._directionLock = dir;
509                         sv._didDrag = this._didDrag;
510                 },
511
512                 _handleDragMove: function ( e, ex, ey ) {
513                         if ( this._skip_dragging ) {
514                                 return;
515                         }
516
517                         if ( !this._dragging ) {
518                                 return;
519                         }
520
521                         if ( !this._is_inputbox && !this._is_button ) {
522                                 e.preventDefault();
523                         }
524
525                         var mt = this.options.moveThreshold,
526                                 dx = ex - this._lastX,
527                                 dy = ey - this._lastY,
528                                 svdir = this.options.direction,
529                                 dir = null,
530                                 x,
531                                 y,
532                                 sv,
533                                 scope,
534                                 newX,
535                                 newY,
536                                 dirLock;
537
538                         if ( Math.abs( this._startY - ey ) < mt && !this._didDrag ) {
539                                 return;
540                         }
541
542                         this._lastMove = getCurrentTime();
543
544                         if ( !this._directionLock ) {
545                                 x = Math.abs( dx );
546                                 y = Math.abs( dy );
547
548                                 if ( x < mt && y < mt ) {
549                                         return false;
550                                 }
551
552                                 if ( x < y && (x / y) < 0.5 ) {
553                                         dir = "y";
554                                 } else if ( x > y && (y / x) < 0.5 ) {
555                                         dir = "x";
556                                 }
557
558                                 if ( svdir && dir && svdir !== dir ) {
559                                         /*
560                                          * This scrollview can't handle the direction the user
561                                          * is attempting to scroll. Find an ancestor scrollview
562                                          * that can handle the request.
563                                          */
564
565                                         sv = this._getAncestorByDirection( dir );
566                                         if ( sv ) {
567                                                 this._propagateDragMove( sv, e, ex, ey, dir );
568                                                 return false;
569                                         }
570                                 }
571
572                                 this._directionLock = svdir || (dir || "none");
573                         }
574
575                         newX = this._sx;
576                         newY = this._sy;
577                         dirLock = this._directionLock;
578
579                         if ( dirLock !== "y" && this._hTracker ) {
580                                 x = this._sx;
581                                 this._speedX = dx;
582                                 newX = x + dx;
583
584                                 this._doSnapBackX = false;
585
586                                 scope = ( newX > 0 || newX < this._maxX );
587
588                                 if ( scope && dirLock === "x" ) {
589                                         sv = this._getAncestorByDirection("x");
590                                         if ( sv ) {
591                                                 this._setScrollPosition( newX > 0 ?
592                                                                 0 : this._maxX, newY );
593                                                 this._propagateDragMove( sv, e, ex, ey, dir );
594                                                 return false;
595                                         }
596
597                                         newX = x + ( dx / 2 );
598                                         this._doSnapBackX = true;
599                                 }
600                         }
601
602                         if ( dirLock !== "x" && this._vTracker ) {
603                                 y = this._sy;
604                                 this._speedY = dy;
605                                 newY = y + dy;
606
607                                 this._doSnapBackY = false;
608
609                                 scope = ( newY > 0 || newY < this._maxY );
610
611                                 if ( scope && dirLock === "y" ) {
612                                         sv = this._getAncestorByDirection("y");
613                                         if ( sv ) {
614                                                 this._setScrollPosition( newX,
615                                                                 newY > 0 ? 0 : this._maxY );
616                                                 this._propagateDragMove( sv, e, ex, ey, dir );
617                                                 return false;
618                                         }
619
620                                         newY = y + ( dy / 2 );
621                                         this._doSnapBackY = true;
622                                 }
623                         }
624
625                         if ( this.options.overshootEnable === false ) {
626                                 this._doSnapBackX = false;
627                                 this._doSnapBackY = false;
628                         }
629
630                         this._didDrag = true;
631                         this._lastX = ex;
632                         this._lastY = ey;
633
634                         this._setScrollPosition( newX, newY );
635
636                         this._showScrollBars();
637                 },
638
639                 _handleDragStop: function ( e ) {
640                         if ( this._skip_dragging ) {
641                                 return;
642                         }
643
644                         var l = this._lastMove,
645                                 t = getCurrentTime(),
646                                 doScroll = (l && (t - l) <= this.options.moveIntervalThreshold),
647                                 sx = ( this._hTracker && this._speedX && doScroll ) ?
648                                                 this._speedX : ( this._doSnapBackX ? 1 : 0 ),
649                                 sy = ( this._vTracker && this._speedY && doScroll ) ?
650                                                 this._speedY : ( this._doSnapBackY ? 1 : 0 ),
651                                 svdir = this.options.direction,
652                                 x,
653                                 y;
654
655                         if ( sx || sy ) {
656                                 this._startMScroll( sx, sy );
657                         } else {
658                                 this._hideScrollBars();
659                         }
660
661                         this._disableTracking();
662
663                         return !this._didDrag;
664                 },
665
666                 _enableTracking: function () {
667                         this._dragging = true;
668                 },
669
670                 _disableTracking: function () {
671                         this._dragging = false;
672                 },
673
674                 _showScrollBars: function () {
675                         var vclass = "ui-scrollbar-visible";
676
677                         if ( !this.options.showScrollBars ) {
678                                 return;
679                         }
680                         if ( this._scrollbar_showed ) {
681                                 return;
682                         }
683
684                         if ( this._$vScrollBar ) {
685                                 this._$vScrollBar.addClass( vclass );
686                         }
687                         if ( this._$hScrollBar ) {
688                                 this._$hScrollBar.addClass( vclass );
689                         }
690
691                         this._scrollbar_showed = true;
692                 },
693
694                 _hideScrollBars: function () {
695                         var vclass = "ui-scrollbar-visible";
696
697                         if ( !this.options.showScrollBars ) {
698                                 return;
699                         }
700                         if ( !this._scrollbar_showed ) {
701                                 return;
702                         }
703
704                         if ( this._$vScrollBar ) {
705                                 this._$vScrollBar.removeClass( vclass );
706                         }
707                         if ( this._$hScrollBar ) {
708                                 this._$hScrollBar.removeClass( vclass );
709                         }
710
711                         this._scrollbar_showed = false;
712                 },
713
714                 _add_event: function () {
715                         var self = this,
716                                 $c = this._$clip,
717                                 $v = this._$view;
718
719                         if ( this.options.eventType === "mouse" ) {
720                                 this._dragEvt = "mousedown mousemove mouseup click mousewheel";
721
722                                 this._dragCB = function ( e ) {
723                                         switch ( e.type ) {
724                                         case "mousedown":
725                                                 return self._handleDragStart( e,
726                                                                 e.clientX, e.clientY );
727
728                                         case "mousemove":
729                                                 return self._handleDragMove( e,
730                                                                 e.clientX, e.clientY );
731
732                                         case "mouseup":
733                                                 return self._handleDragStop( e );
734
735                                         case "click":
736                                                 return !self._didDrag;
737
738                                         case "mousewheel":
739                                                 var old = self.getScrollPosition();
740                                                 self.scrollTo( -old.x,
741                                                         -(old.y - e.originalEvent.wheelDelta) );
742                                                 break;
743                                         }
744                                 };
745                         } else {
746                                 this._dragEvt = "touchstart touchmove touchend click";
747
748                                 this._dragCB = function ( e ) {
749                                         var t;
750
751                                         switch ( e.type ) {
752                                         case "touchstart":
753                                                 t = e.originalEvent.targetTouches[0];
754                                                 return self._handleDragStart( e,
755                                                                 t.pageX, t.pageY );
756
757                                         case "touchmove":
758                                                 t = e.originalEvent.targetTouches[0];
759                                                 return self._handleDragMove( e,
760                                                                 t.pageX, t.pageY );
761
762                                         case "touchend":
763                                                 return self._handleDragStop( e );
764
765                                         case "click":
766                                                 return !self._didDrag;
767                                         }
768                                 };
769                         }
770
771                         $v.bind( this._dragEvt, this._dragCB );
772
773                         if ( $c.jqmData("scroll") !== "y" ) {
774                                 return;
775                         }
776
777                         $c.bind( "updatelayout", function ( e ) {
778                                 var $page = $c.parentsUntil("ui-page"),
779                                         sy,
780                                         vh;
781
782                                 if ( !$c.height() || !$v.height() ) {
783                                         self.scrollTo( 0, 0, 0 );
784                                         return;
785                                 }
786
787                                 sy = $c.height() - $v.height();
788                                 vh = $v.height() - self._view_height;
789
790                                 self._view_height = $v.height();
791
792                                 if ( vh == 0 || vh > $c.height() / 2 ) {
793                                         return;
794                                 }
795
796                                 if ( self._sy - sy <= -vh ) {
797                                         self.scrollTo( 0, self._sy,
798                                                 self.options.snapbackDuration );
799                                 } else if ( self._sy - sy <= vh + self.options.moveThreshold ) {
800                                         self.scrollTo( 0, sy,
801                                                 self.options.snapbackDuration );
802                                 }
803                         });
804
805                         $( window ).bind( "resize", function ( e ) {
806                                 var $page = $c.parentsUntil("ui-page"),
807                                         focused;
808
809                                 if ( !$c.height() || !$v.height() ) {
810                                         return;
811                                 }
812
813                                 focused = $c.find(".ui-focus");
814
815                                 if ( focused ) {
816                                         focused.trigger("resize.scrollview");
817                                 }
818
819                                 /* calibration */
820                                 if ( self._sy < $c.height() - $v.height() ) {
821                                         self.scrollTo( 0, self._sy,
822                                                 self.options.snapbackDuration );
823                                 }
824
825                                 self._view_height = $v.height();
826                         });
827                 },
828
829                 _add_scrollbar: function () {
830                         var $c = this._$clip,
831                                 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
832                                 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
833
834                         if ( !this.options.showScrollBars ) {
835                                 return;
836                         }
837
838                         if ( this._vTracker ) {
839                                 $c.append( prefix + "y" + suffix );
840                                 this._$vScrollBar = $c.children(".ui-scrollbar-y");
841                         }
842                         if ( this._hTracker ) {
843                                 $c.append( prefix + "x" + suffix );
844                                 this._$hScrollBar = $c.children(".ui-scrollbar-x");
845                         }
846
847                         this._scrollbar_showed = false;
848                 },
849
850                 _add_scroll_jump: function () {
851                         var $c = this._$clip,
852                                 self = this,
853                                 top_btn,
854                                 left_btn;
855
856                         if ( !this.options.scrollJump ) {
857                                 return;
858                         }
859
860                         if ( this._vTracker ) {
861                                 top_btn = $( '<div class="ui-scroll-jump-top-bg ui-btn" data-theme="s">' +
862                                                 '<div class="ui-scroll-jump-top"></div></div>' );
863                                 $c.append( top_btn );
864
865                                 top_btn.bind( "vclick", function () {
866                                         self.scrollTo( 0, 0, self.options.overshootDuration );
867                                 } );
868                         }
869
870                         if ( this._hTracker ) {
871                                 left_btn = $( '<div class="ui-scroll-jump-left-bg ui-btn" data-theme="s">' +
872                                                 '<div class="ui-scroll-jump-left"></div></div>' );
873                                 $c.append( left_btn );
874
875                                 left_btn.bind( "vclick", function () {
876                                         self.scrollTo( 0, 0, self.options.overshootDuration );
877                                 } );
878                         }
879                 },
880
881                 _set_scrollbar_size: function () {
882                         var $c = this._$clip,
883                                 $v = this._$view,
884                                 cw = 0,
885                                 vw = 0,
886                                 ch = 0,
887                                 vh = 0,
888                                 thumb;
889
890                         if ( !this.options.showScrollBars ) {
891                                 return;
892                         }
893
894                         if ( this._hTracker ) {
895                                 cw = $c.width();
896                                 vw = $v.width();
897                                 this._maxX = cw - vw;
898
899                                 if ( this._maxX > 0 ) {
900                                         this._maxX = 0;
901                                 }
902                                 if ( this._$hScrollBar && vw ) {
903                                         thumb = this._$hScrollBar.find(".ui-scrollbar-thumb");
904                                         thumb.css( "width", (cw >= vw ? "100%" :
905                                                         (Math.floor(cw / vw * 100) || 1) + "%") );
906                                 }
907                         }
908
909                         if ( this._vTracker ) {
910                                 ch = $c.height();
911                                 vh = $v.height();
912                                 this._maxY = ch - vh;
913
914                                 if ( this._maxY > 0 ) {
915                                         this._maxY = 0;
916                                 }
917                                 if ( this._$vScrollBar && vh ) {
918                                         thumb = this._$vScrollBar.find(".ui-scrollbar-thumb");
919                                         thumb.css( "height", (ch >= vh ? "100%" :
920                                                         (Math.floor(ch / vh * 100) || 1) + "%") );
921                                 }
922                         }
923                 }
924         });
925
926         $.extend( MomentumTracker.prototype, {
927                 start: function ( pos, speed, duration, minPos, maxPos ) {
928                         var tstate = ( pos < minPos || pos > maxPos ) ?
929                                         tstates.snapback : tstates.scrolling,
930                                 pos_temp;
931
932                         this.state = ( speed !== 0 ) ? tstate : tstates.done;
933                         this.pos = pos;
934                         this.speed = speed;
935                         this.duration = ( this.state === tstates.snapback ) ?
936                                         this.options.snapbackDuration : duration;
937                         this.minPos = minPos;
938                         this.maxPos = maxPos;
939
940                         this.fromPos = ( this.state === tstates.snapback ) ? this.pos : 0;
941                         pos_temp = ( this.pos < this.minPos ) ? this.minPos : this.maxPos;
942                         this.toPos = ( this.state === tstates.snapback ) ? pos_temp : 0;
943
944                         this.startTime = getCurrentTime();
945                 },
946
947                 reset: function () {
948                         this.state = tstates.done;
949                         this.pos = 0;
950                         this.speed = 0;
951                         this.minPos = 0;
952                         this.maxPos = 0;
953                         this.duration = 0;
954                 },
955
956                 update: function ( overshootEnable ) {
957                         var state = this.state,
958                                 cur_time = getCurrentTime(),
959                                 duration = this.duration,
960                                 elapsed =  cur_time - this.startTime,
961                                 dx,
962                                 x,
963                                 didOverShoot;
964
965                         if ( state === tstates.done ) {
966                                 return this.pos;
967                         }
968
969                         elapsed = elapsed > duration ? duration : elapsed;
970
971                         if ( state === tstates.scrolling || state === tstates.overshot ) {
972                                 dx = this.speed *
973                                         ( 1 - $.easing[this.easing]( elapsed / duration,
974                                                                 elapsed, 0, 1, duration ) );
975
976                                 x = this.pos + dx;
977
978                                 didOverShoot = ( state === tstates.scrolling ) &&
979                                         ( x < this.minPos || x > this.maxPos );
980
981                                 if ( didOverShoot ) {
982                                         x = ( x < this.minPos ) ? this.minPos : this.maxPos;
983                                 }
984
985                                 this.pos = x;
986
987                                 if ( state === tstates.overshot ) {
988                                         if ( elapsed >= duration ) {
989                                                 this.state = tstates.snapback;
990                                                 this.fromPos = this.pos;
991                                                 this.toPos = ( x < this.minPos ) ?
992                                                                 this.minPos : this.maxPos;
993                                                 this.duration = this.options.snapbackDuration;
994                                                 this.startTime = cur_time;
995                                                 elapsed = 0;
996                                         }
997                                 } else if ( state === tstates.scrolling ) {
998                                         if ( didOverShoot && overshootEnable ) {
999                                                 this.state = tstates.overshot;
1000                                                 this.speed = dx / 2;
1001                                                 this.duration = this.options.overshootDuration;
1002                                                 this.startTime = cur_time;
1003                                         } else if ( elapsed >= duration ) {
1004                                                 this.state = tstates.done;
1005                                         }
1006                                 }
1007                         } else if ( state === tstates.snapback ) {
1008                                 if ( elapsed >= duration ) {
1009                                         this.pos = this.toPos;
1010                                         this.state = tstates.done;
1011                                 } else {
1012                                         this.pos = this.fromPos + (( this.toPos - this.fromPos ) *
1013                                                 $.easing[this.easing]( elapsed / duration,
1014                                                         elapsed, 0, 1, duration ));
1015                                 }
1016                         }
1017
1018                         return this.pos;
1019                 },
1020
1021                 done: function () {
1022                         return this.state === tstates.done;
1023                 },
1024
1025                 getPosition: function () {
1026                         return this.pos;
1027                 }
1028         });
1029
1030         $( document ).bind( 'pagecreate create', function ( e ) {
1031                 var $page = $( e.target ),
1032                         content_scroll = $page.find(".ui-content").jqmData("scroll");
1033
1034                 /* content scroll */
1035                 if ( $.support.scrollview === undefined ) {
1036                         $.support.scrollview = true;
1037                 }
1038
1039                 if ( $.support.scrollview === true && content_scroll === undefined ) {
1040                         content_scroll = "y";
1041                 }
1042
1043                 if ( content_scroll !== "y" ) {
1044                         content_scroll = "none";
1045                 }
1046
1047                 $page.find(".ui-content").attr( "data-scroll", content_scroll );
1048
1049                 $page.find(":jqmData(scroll):not(.ui-scrollview-clip)").each( function () {
1050                         if ( $( this ).hasClass("ui-scrolllistview") ) {
1051                                 $( this ).scrolllistview();
1052                         } else {
1053                                 var st = $( this ).jqmData("scroll"),
1054                                         dir = st && ( st.search(/^[xy]/) !== -1 ) ? st.charAt(0) : null,
1055                                         opts;
1056
1057                                 if ( st === "none" ) {
1058                                         return;
1059                                 }
1060
1061                                 opts = {
1062                                         direction: dir || undefined,
1063                                         scrollMethod: $( this ).jqmData("scroll-method") || undefined,
1064                                         scrollJump: $( this ).jqmData("scroll-jump") || undefined
1065                                 };
1066
1067                                 $( this ).scrollview( opts );
1068                         }
1069                 });
1070         });
1071
1072         $( document ).bind( 'pageshow', function ( e ) {
1073                 var $page = $( e.target ),
1074                         scroll = $page.find(".ui-content").jqmData("scroll");
1075
1076                 if ( scroll === "y" ) {
1077                         resizePageContentHeight( e.target );
1078                 }
1079         });
1080
1081 }( jQuery, window, document ) );