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