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