Revert "Export"
[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 Date.now();
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                         if ( this._is_button ) {
474                                 if ( target.parents('.ui-slider-handle') ) {
475                                         this._skip_dragging = true;
476                                         return;
477                                 }
478                         }
479
480                         /*
481                          * We need to prevent the default behavior to
482                          * suppress accidental selection of text, etc.
483                          */
484                         this._is_inputbox = target.is(':input') ||
485                                         target.parents(':input').length > 0;
486
487                         if ( this._is_inputbox ) {
488                                 target.one( "resize.scrollview", function () {
489                                         if ( ey > $c.height() ) {
490                                                 self.scrollTo( -ex, self._sy - ey + $c.height(),
491                                                         self.options.snapbackDuration );
492                                         }
493                                 });
494                         }
495
496                         this._lastX = ex;
497                         this._lastY = ey;
498                         this._startY = ey;
499                         this._doSnapBackX = false;
500                         this._doSnapBackY = false;
501                         this._speedX = 0;
502                         this._speedY = 0;
503                         this._directionLock = "";
504
505                         this._lastMove = 0;
506                         this._enableTracking();
507
508                         this._set_scrollbar_size();
509                 },
510
511                 _propagateDragMove: function ( sv, e, ex, ey, dir ) {
512                         this._hideScrollBars();
513                         this._disableTracking();
514                         sv._handleDragStart( e, ex, ey );
515                         sv._directionLock = dir;
516                         sv._didDrag = this._didDrag;
517                 },
518
519                 _handleDragMove: function ( e, ex, ey ) {
520                         if ( this._skip_dragging ) {
521                                 return;
522                         }
523
524                         if ( !this._dragging ) {
525                                 return;
526                         }
527
528                         if ( !this._is_inputbox && !this._is_button ) {
529                                 e.preventDefault();
530                         }
531
532                         var mt = this.options.moveThreshold,
533                                 dx = ex - this._lastX,
534                                 dy = ey - this._lastY,
535                                 svdir = this.options.direction,
536                                 dir = null,
537                                 x,
538                                 y,
539                                 sv,
540                                 scope,
541                                 newX,
542                                 newY,
543                                 dirLock;
544
545                         if ( Math.abs( this._startY - ey ) < mt && !this._didDrag ) {
546                                 return;
547                         }
548
549                         this._lastMove = getCurrentTime();
550
551                         if ( !this._directionLock ) {
552                                 x = Math.abs( dx );
553                                 y = Math.abs( dy );
554
555                                 if ( x < mt && y < mt ) {
556                                         return false;
557                                 }
558
559                                 if ( x < y && (x / y) < 0.5 ) {
560                                         dir = "y";
561                                 } else if ( x > y && (y / x) < 0.5 ) {
562                                         dir = "x";
563                                 }
564
565                                 if ( svdir && dir && svdir !== dir ) {
566                                         /*
567                                          * This scrollview can't handle the direction the user
568                                          * is attempting to scroll. Find an ancestor scrollview
569                                          * that can handle the request.
570                                          */
571
572                                         sv = this._getAncestorByDirection( dir );
573                                         if ( sv ) {
574                                                 this._propagateDragMove( sv, e, ex, ey, dir );
575                                                 return false;
576                                         }
577                                 }
578
579                                 this._directionLock = svdir || (dir || "none");
580                         }
581
582                         newX = this._sx;
583                         newY = this._sy;
584                         dirLock = this._directionLock;
585
586                         if ( dirLock !== "y" && this._hTracker ) {
587                                 x = this._sx;
588                                 this._speedX = dx;
589                                 newX = x + dx;
590
591                                 this._doSnapBackX = false;
592
593                                 scope = ( newX > 0 || newX < this._maxX );
594
595                                 if ( scope && dirLock === "x" ) {
596                                         sv = this._getAncestorByDirection("x");
597                                         if ( sv ) {
598                                                 this._setScrollPosition( newX > 0 ?
599                                                                 0 : this._maxX, newY );
600                                                 this._propagateDragMove( sv, e, ex, ey, dir );
601                                                 return false;
602                                         }
603
604                                         newX = x + ( dx / 2 );
605                                         this._doSnapBackX = true;
606                                 }
607                         }
608
609                         if ( dirLock !== "x" && this._vTracker ) {
610                                 y = this._sy;
611                                 this._speedY = dy;
612                                 newY = y + dy;
613
614                                 this._doSnapBackY = false;
615
616                                 scope = ( newY > 0 || newY < this._maxY );
617
618                                 if ( scope && dirLock === "y" ) {
619                                         sv = this._getAncestorByDirection("y");
620                                         if ( sv ) {
621                                                 this._setScrollPosition( newX,
622                                                                 newY > 0 ? 0 : this._maxY );
623                                                 this._propagateDragMove( sv, e, ex, ey, dir );
624                                                 return false;
625                                         }
626
627                                         newY = y + ( dy / 2 );
628                                         this._doSnapBackY = true;
629                                 }
630                         }
631
632                         if ( this.options.overshootEnable === false ) {
633                                 this._doSnapBackX = false;
634                                 this._doSnapBackY = false;
635                         }
636
637                         this._didDrag = true;
638                         this._lastX = ex;
639                         this._lastY = ey;
640
641                         this._setScrollPosition( newX, newY );
642
643                         this._showScrollBars();
644                 },
645
646                 _handleDragStop: function ( e ) {
647                         if ( this._skip_dragging ) {
648                                 return;
649                         }
650
651                         var l = this._lastMove,
652                                 t = getCurrentTime(),
653                                 doScroll = (l && (t - l) <= this.options.moveIntervalThreshold),
654                                 sx = ( this._hTracker && this._speedX && doScroll ) ?
655                                                 this._speedX : ( this._doSnapBackX ? 1 : 0 ),
656                                 sy = ( this._vTracker && this._speedY && doScroll ) ?
657                                                 this._speedY : ( this._doSnapBackY ? 1 : 0 ),
658                                 svdir = this.options.direction,
659                                 x,
660                                 y;
661
662                         if ( sx || sy ) {
663                                 this._startMScroll( sx, sy );
664                         } else {
665                                 this._hideScrollBars();
666                         }
667
668                         this._disableTracking();
669
670                         return !this._didDrag;
671                 },
672
673                 _enableTracking: function () {
674                         this._dragging = true;
675                 },
676
677                 _disableTracking: function () {
678                         this._dragging = false;
679                 },
680
681                 _showScrollBars: function () {
682                         var vclass = "ui-scrollbar-visible";
683
684                         if ( !this.options.showScrollBars ) {
685                                 return;
686                         }
687                         if ( this._scrollbar_showed ) {
688                                 return;
689                         }
690
691                         if ( this._$vScrollBar ) {
692                                 this._$vScrollBar.addClass( vclass );
693                         }
694                         if ( this._$hScrollBar ) {
695                                 this._$hScrollBar.addClass( vclass );
696                         }
697
698                         this._scrollbar_showed = true;
699                 },
700
701                 _hideScrollBars: function () {
702                         var vclass = "ui-scrollbar-visible";
703
704                         if ( !this.options.showScrollBars ) {
705                                 return;
706                         }
707                         if ( !this._scrollbar_showed ) {
708                                 return;
709                         }
710
711                         if ( this._$vScrollBar ) {
712                                 this._$vScrollBar.removeClass( vclass );
713                         }
714                         if ( this._$hScrollBar ) {
715                                 this._$hScrollBar.removeClass( vclass );
716                         }
717
718                         this._scrollbar_showed = false;
719                 },
720
721                 _add_event: function () {
722                         var self = this,
723                                 $c = this._$clip,
724                                 $v = this._$view;
725
726                         if ( this.options.eventType === "mouse" ) {
727                                 this._dragEvt = "mousedown mousemove mouseup click mousewheel";
728
729                                 this._dragCB = function ( e ) {
730                                         switch ( e.type ) {
731                                         case "mousedown":
732                                                 return self._handleDragStart( e,
733                                                                 e.clientX, e.clientY );
734
735                                         case "mousemove":
736                                                 return self._handleDragMove( e,
737                                                                 e.clientX, e.clientY );
738
739                                         case "mouseup":
740                                                 return self._handleDragStop( e );
741
742                                         case "click":
743                                                 return !self._didDrag;
744
745                                         case "mousewheel":
746                                                 var old = self.getScrollPosition();
747                                                 self.scrollTo( -old.x,
748                                                         -(old.y - e.originalEvent.wheelDelta) );
749                                                 break;
750                                         }
751                                 };
752                         } else {
753                                 this._dragEvt = "touchstart touchmove touchend click";
754
755                                 this._dragCB = function ( e ) {
756                                         var t;
757
758                                         switch ( e.type ) {
759                                         case "touchstart":
760                                                 t = e.originalEvent.targetTouches[0];
761                                                 return self._handleDragStart( e,
762                                                                 t.pageX, t.pageY );
763
764                                         case "touchmove":
765                                                 t = e.originalEvent.targetTouches[0];
766                                                 return self._handleDragMove( e,
767                                                                 t.pageX, t.pageY );
768
769                                         case "touchend":
770                                                 return self._handleDragStop( e );
771
772                                         case "click":
773                                                 return !self._didDrag;
774                                         }
775                                 };
776                         }
777
778                         $v.bind( this._dragEvt, this._dragCB );
779
780                         if ( $c.jqmData("scroll") !== "y" ) {
781                                 return;
782                         }
783
784                         $c.bind( "updatelayout", function ( e ) {
785                                 var $page = $c.parentsUntil("ui-page"),
786                                         sy,
787                                         vh;
788
789                                 if ( !$c.height() || !$v.height() ) {
790                                         self.scrollTo( 0, 0, 0 );
791                                         return;
792                                 }
793
794                                 sy = $c.height() - $v.height();
795                                 vh = $v.height() - self._view_height;
796
797                                 self._view_height = $v.height();
798
799                                 if ( vh == 0 || vh > $c.height() / 2 ) {
800                                         return;
801                                 }
802
803                                 if ( self._sy - sy <= -vh ) {
804                                         self.scrollTo( 0, self._sy,
805                                                 self.options.snapbackDuration );
806                                 } else if ( self._sy - sy <= vh + self.options.moveThreshold ) {
807                                         self.scrollTo( 0, sy,
808                                                 self.options.snapbackDuration );
809                                 }
810                         });
811
812                         $( window ).bind( "resize", function ( e ) {
813                                 var $page = $c.parentsUntil("ui-page"),
814                                         focused;
815
816                                 if ( !$c.height() || !$v.height() ) {
817                                         return;
818                                 }
819
820                                 focused = $c.find(".ui-focus");
821
822                                 if ( focused ) {
823                                         focused.trigger("resize.scrollview");
824                                 }
825
826                                 /* calibration - after triggered throttledresize */
827                                 setTimeout( function () {
828                                         if ( self._sy < $c.height() - $v.height() ) {
829                                                 self.scrollTo( 0, self._sy,
830                                                         self.options.snapbackDuration );
831                                         }
832                                 }, 260 );
833
834                                 self._view_height = $v.height();
835                         });
836                 },
837
838                 _add_scrollbar: function () {
839                         var $c = this._$clip,
840                                 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
841                                 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
842
843                         if ( !this.options.showScrollBars ) {
844                                 return;
845                         }
846
847                         if ( this._vTracker ) {
848                                 $c.append( prefix + "y" + suffix );
849                                 this._$vScrollBar = $c.children(".ui-scrollbar-y");
850                         }
851                         if ( this._hTracker ) {
852                                 $c.append( prefix + "x" + suffix );
853                                 this._$hScrollBar = $c.children(".ui-scrollbar-x");
854                         }
855
856                         this._scrollbar_showed = false;
857                 },
858
859                 _add_scroll_jump: function () {
860                         var $c = this._$clip,
861                                 self = this,
862                                 top_btn,
863                                 left_btn;
864
865                         if ( !this.options.scrollJump ) {
866                                 return;
867                         }
868
869                         if ( this._vTracker ) {
870                                 top_btn = $( '<div class="ui-scroll-jump-top-bg ui-btn" data-theme="s">' +
871                                                 '<div class="ui-scroll-jump-top"></div></div>' );
872                                 $c.append( top_btn );
873
874                                 top_btn.bind( "vclick", function () {
875                                         self.scrollTo( 0, 0, self.options.overshootDuration );
876                                 } );
877                         }
878
879                         if ( this._hTracker ) {
880                                 left_btn = $( '<div class="ui-scroll-jump-left-bg ui-btn" data-theme="s">' +
881                                                 '<div class="ui-scroll-jump-left"></div></div>' );
882                                 $c.append( left_btn );
883
884                                 left_btn.bind( "vclick", function () {
885                                         self.scrollTo( 0, 0, self.options.overshootDuration );
886                                 } );
887                         }
888                 },
889
890                 _set_scrollbar_size: function () {
891                         var $c = this._$clip,
892                                 $v = this._$view,
893                                 cw = 0,
894                                 vw = 0,
895                                 ch = 0,
896                                 vh = 0,
897                                 thumb;
898
899                         if ( !this.options.showScrollBars ) {
900                                 return;
901                         }
902
903                         if ( this._hTracker ) {
904                                 cw = $c.width();
905                                 vw = $v.width();
906                                 this._maxX = cw - vw;
907
908                                 if ( this._maxX > 0 ) {
909                                         this._maxX = 0;
910                                 }
911                                 if ( this._$hScrollBar && vw ) {
912                                         thumb = this._$hScrollBar.find(".ui-scrollbar-thumb");
913                                         thumb.css( "width", (cw >= vw ? "100%" :
914                                                         (Math.floor(cw / vw * 100) || 1) + "%") );
915                                 }
916                         }
917
918                         if ( this._vTracker ) {
919                                 ch = $c.height();
920                                 vh = $v.height();
921                                 this._maxY = ch - vh;
922
923                                 if ( this._maxY > 0 ) {
924                                         this._maxY = 0;
925                                 }
926                                 if ( this._$vScrollBar && vh ) {
927                                         thumb = this._$vScrollBar.find(".ui-scrollbar-thumb");
928                                         thumb.css( "height", (ch >= vh ? "100%" :
929                                                         (Math.floor(ch / vh * 100) || 1) + "%") );
930                                 }
931                         }
932                 }
933         });
934
935         $.extend( MomentumTracker.prototype, {
936                 start: function ( pos, speed, duration, minPos, maxPos ) {
937                         var tstate = ( pos < minPos || pos > maxPos ) ?
938                                         tstates.snapback : tstates.scrolling,
939                                 pos_temp;
940
941                         this.state = ( speed !== 0 ) ? tstate : tstates.done;
942                         this.pos = pos;
943                         this.speed = speed;
944                         this.duration = ( this.state === tstates.snapback ) ?
945                                         this.options.snapbackDuration : duration;
946                         this.minPos = minPos;
947                         this.maxPos = maxPos;
948
949                         this.fromPos = ( this.state === tstates.snapback ) ? this.pos : 0;
950                         pos_temp = ( this.pos < this.minPos ) ? this.minPos : this.maxPos;
951                         this.toPos = ( this.state === tstates.snapback ) ? pos_temp : 0;
952
953                         this.startTime = getCurrentTime();
954                 },
955
956                 reset: function () {
957                         this.state = tstates.done;
958                         this.pos = 0;
959                         this.speed = 0;
960                         this.minPos = 0;
961                         this.maxPos = 0;
962                         this.duration = 0;
963                 },
964
965                 update: function ( overshootEnable ) {
966                         var state = this.state,
967                                 cur_time = getCurrentTime(),
968                                 duration = this.duration,
969                                 elapsed =  cur_time - this.startTime,
970                                 dx,
971                                 x,
972                                 didOverShoot;
973
974                         if ( state === tstates.done ) {
975                                 return this.pos;
976                         }
977
978                         elapsed = elapsed > duration ? duration : elapsed;
979
980                         if ( state === tstates.scrolling || state === tstates.overshot ) {
981                                 dx = this.speed *
982                                         ( 1 - $.easing[this.easing]( elapsed / duration,
983                                                                 elapsed, 0, 1, duration ) );
984
985                                 x = this.pos + dx;
986
987                                 didOverShoot = ( state === tstates.scrolling ) &&
988                                         ( x < this.minPos || x > this.maxPos );
989
990                                 if ( didOverShoot ) {
991                                         x = ( x < this.minPos ) ? this.minPos : this.maxPos;
992                                 }
993
994                                 this.pos = x;
995
996                                 if ( state === tstates.overshot ) {
997                                         if ( elapsed >= duration ) {
998                                                 this.state = tstates.snapback;
999                                                 this.fromPos = this.pos;
1000                                                 this.toPos = ( x < this.minPos ) ?
1001                                                                 this.minPos : this.maxPos;
1002                                                 this.duration = this.options.snapbackDuration;
1003                                                 this.startTime = cur_time;
1004                                                 elapsed = 0;
1005                                         }
1006                                 } else if ( state === tstates.scrolling ) {
1007                                         if ( didOverShoot && overshootEnable ) {
1008                                                 this.state = tstates.overshot;
1009                                                 this.speed = dx / 2;
1010                                                 this.duration = this.options.overshootDuration;
1011                                                 this.startTime = cur_time;
1012                                         } else if ( elapsed >= duration ) {
1013                                                 this.state = tstates.done;
1014                                         }
1015                                 }
1016                         } else if ( state === tstates.snapback ) {
1017                                 if ( elapsed >= duration ) {
1018                                         this.pos = this.toPos;
1019                                         this.state = tstates.done;
1020                                 } else {
1021                                         this.pos = this.fromPos + (( this.toPos - this.fromPos ) *
1022                                                 $.easing[this.easing]( elapsed / duration,
1023                                                         elapsed, 0, 1, duration ));
1024                                 }
1025                         }
1026
1027                         return this.pos;
1028                 },
1029
1030                 done: function () {
1031                         return this.state === tstates.done;
1032                 },
1033
1034                 getPosition: function () {
1035                         return this.pos;
1036                 }
1037         });
1038
1039         $( document ).bind( 'pagecreate create', function ( e ) {
1040                 var $page = $( e.target ),
1041                         content_scroll = $page.find(".ui-content").jqmData("scroll");
1042
1043                 /* content scroll */
1044                 if ( $.support.scrollview === undefined ) {
1045                         $.support.scrollview = true;
1046                 }
1047
1048                 if ( $.support.scrollview === true && content_scroll === undefined ) {
1049                         content_scroll = "y";
1050                 }
1051
1052                 if ( content_scroll !== "y" ) {
1053                         content_scroll = "none";
1054                 }
1055
1056                 $page.find(".ui-content").attr( "data-scroll", content_scroll );
1057
1058                 $page.find(":jqmData(scroll):not(.ui-scrollview-clip)").each( function () {
1059                         if ( $( this ).hasClass("ui-scrolllistview") ) {
1060                                 $( this ).scrolllistview();
1061                         } else {
1062                                 var st = $( this ).jqmData("scroll"),
1063                                         dir = st && ( st.search(/^[xy]/) !== -1 ) ? st.charAt(0) : null,
1064                                         opts;
1065
1066                                 if ( st === "none" ) {
1067                                         return;
1068                                 }
1069
1070                                 opts = {
1071                                         direction: dir || undefined,
1072                                         scrollMethod: $( this ).jqmData("scroll-method") || undefined,
1073                                         scrollJump: $( this ).jqmData("scroll-jump") || undefined
1074                                 };
1075
1076                                 $( this ).scrollview( opts );
1077                         }
1078                 });
1079         });
1080
1081         $( document ).bind( 'pageshow', function ( e ) {
1082                 var $page = $( e.target ),
1083                         scroll = $page.find(".ui-content").jqmData("scroll");
1084
1085                 if ( scroll === "y" ) {
1086                         resizePageContentHeight( e.target );
1087                 }
1088         });
1089
1090 }( jQuery, window, document ) );