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