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