[TemporaryStorage] add files required for SDK build
[samples/web/TemporaryStorage.git] / tizen-web-ui-fw / latest / js / src / jquery.mobile.tizen.scrollview.js
1
2 /*
3 * jQuery Mobile Framework : scrollview plugin
4 * Copyright (c) 2010 Adobe Systems Incorporated - Kin Blas (jblas@adobe.com)
5 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
6 * Note: Code is in draft form and is subject to change
7 * Modified by Koeun Choi <koeun.choi@samsung.com>
8 * Modified by Minkyu Kang <mk7.kang@samsung.com>
9 */
10
11 (function ( $, window, document, undefined ) {
12
13         function resizePageContentHeight( page ) {
14                 var $page = $( page ),
15                         $content = $page.children(".ui-content"),
16                         hh = $page.children(".ui-header").outerHeight() || 0,
17                         fh = $page.children(".ui-footer").outerHeight() || 0,
18                         pt = parseFloat( $content.css("padding-top") ),
19                         pb = parseFloat( $content.css("padding-bottom") ),
20                         wh = $( window ).height();
21
22                 $content.height( wh - (hh + fh) - (pt + pb) );
23         }
24
25         function MomentumTracker( options ) {
26                 this.options = $.extend( {}, options );
27                 this.easing = "easeOutQuad";
28                 this.reset();
29         }
30
31         var tstates = {
32                 scrolling: 0,
33                 overshot:  1,
34                 snapback:  2,
35                 done:      3
36         };
37
38         function getCurrentTime() {
39                 return Date.now();
40         }
41
42         jQuery.widget( "tizen.scrollview", jQuery.mobile.widget, {
43                 options: {
44                         direction:         null,  // "x", "y", or null for both.
45
46                         timerInterval:     10,
47                         scrollDuration:    1000,  // Duration of the scrolling animation in msecs.
48                         overshootDuration: 250,   // Duration of the overshoot animation in msecs.
49                         snapbackDuration:  500,   // Duration of the snapback animation in msecs.
50
51                         moveThreshold:     30,   // User must move this many pixels in any direction to trigger a scroll.
52                         moveIntervalThreshold:     150,   // Time between mousemoves must not exceed this threshold.
53
54                         scrollMethod:      "translate",  // "translate", "position"
55                         startEventName:    "scrollstart",
56                         updateEventName:   "scrollupdate",
57                         stopEventName:     "scrollstop",
58
59                         eventType:         $.support.touch ? "touch" : "mouse",
60
61                         showScrollBars:    true,
62                         overshootEnable:   false,
63                         outerScrollEnable: false,
64                         overflowEnable:    true,
65                         scrollJump:        false
66                 },
67
68                 _getViewHeight: function () {
69                         return this._$view.height();
70                 },
71
72                 _getViewWidth: function () {
73                         return this._$view.width();
74                 },
75
76                 _makePositioned: function ( $ele ) {
77                         if ( $ele.css("position") === "static" ) {
78                                 $ele.css( "position", "relative" );
79                         }
80                 },
81
82                 _create: function () {
83                         var direction,
84                                 self = this;
85
86                         this._$clip = $( this.element ).addClass("ui-scrollview-clip");
87
88                         if ( this._$clip.children(".ui-scrollview-view").length ) {
89                                 this._$view = this._$clip.children(".ui-scrollview-view");
90                         } else {
91                                 this._$view = this._$clip.wrapInner("<div></div>").children()
92                                                         .addClass("ui-scrollview-view");
93                         }
94
95                         if ( this.options.scrollMethod === "translate" ) {
96                                 if ( this._$view.css("transform") === undefined ) {
97                                         this.options.scrollMethod = "position";
98                                 }
99                         }
100
101                         this._$clip.css( "overflow", "hidden" );
102                         this._makePositioned( this._$clip );
103
104                         this._makePositioned( this._$view );
105                         this._$view.css( { left: 0, top: 0 } );
106
107                         this._view_height = this._getViewHeight();
108
109                         this._sx = 0;
110                         this._sy = 0;
111
112                         direction = this.options.direction;
113
114                         this._hTracker = ( direction !== "y" ) ?
115                                         new MomentumTracker( this.options ) : null;
116                         this._vTracker = ( direction !== "x" ) ?
117                                         new MomentumTracker( this.options ) : null;
118
119                         this._timerInterval = this.options.timerInterval;
120                         this._timerID = 0;
121
122                         this._timerCB = function () {
123                                 self._handleMomentumScroll();
124                         };
125
126                         this._add_event();
127                         this._add_scrollbar();
128                         this._add_scroll_jump();
129                         this._add_overflow_indicator();
130                 },
131
132                 _startMScroll: function ( speedX, speedY ) {
133                         var keepGoing = false,
134                                 duration = this.options.scrollDuration,
135                                 ht = this._hTracker,
136                                 vt = this._vTracker,
137                                 c,
138                                 v;
139
140                         this._$clip.trigger( this.options.startEventName );
141
142                         if ( ht ) {
143                                 c = this._$clip.width();
144                                 v = this._getViewWidth();
145
146                                 if ( (( this._sx === 0 && speedX > 0 ) ||
147                                         ( this._sx === -(v - c) && speedX < 0 )) &&
148                                                 v > c ) {
149                                         return;
150                                 }
151
152                                 ht.start( this._sx, speedX,
153                                         duration, (v > c) ? -(v - c) : 0, 0 );
154                                 keepGoing = !ht.done();
155                         }
156
157                         if ( vt ) {
158                                 c = this._$clip.height();
159                                 v = this._getViewHeight();
160
161                                 if ( (( this._sy === 0 && speedY > 0 ) ||
162                                         ( this._sy === -(v - c) && speedY < 0 )) &&
163                                                 v > c ) {
164                                         return;
165                                 }
166
167                                 vt.start( this._sy, speedY,
168                                         duration, (v > c) ? -(v - c) : 0, 0 );
169                                 keepGoing = keepGoing || !vt.done();
170                         }
171
172                         if ( keepGoing ) {
173                                 this._timerID = setTimeout( this._timerCB, this._timerInterval );
174                         } else {
175                                 this._stopMScroll();
176                         }
177                 },
178
179                 _stopMScroll: function () {
180                         if ( this._timerID ) {
181                                 this._$clip.trigger( this.options.stopEventName );
182                                 clearTimeout( this._timerID );
183                         }
184                         this._timerID = 0;
185
186                         if ( this._vTracker ) {
187                                 this._vTracker.reset();
188                         }
189
190                         if ( this._hTracker ) {
191                                 this._hTracker.reset();
192                         }
193
194                         this._hideScrollBars();
195                         this._hideOverflowIndicator();
196                 },
197
198                 _handleMomentumScroll: function () {
199                         var keepGoing = false,
200                                 x = 0,
201                                 y = 0,
202                                 scroll_height = 0,
203                                 self = this,
204                                 vt = this._vTracker,
205                                 ht = this._hTracker;
206
207                         if ( this._outerScrolling ) {
208                                 return;
209                         }
210
211                         if ( vt ) {
212                                 vt.update( this.options.overshootEnable );
213                                 y = vt.getPosition();
214                                 keepGoing = !vt.done();
215
216                                 if ( vt.getRemained() > this.options.overshootDuration ) {
217                                         scroll_height = this._getViewHeight() - this._$clip.height();
218
219                                         if ( !vt.isAvail() ) {
220                                                 if ( this._speedY > 0 ) {
221                                                         this._outerScroll( vt.getRemained() / 3, scroll_height );
222                                                 } else {
223                                                         this._outerScroll( y - vt.getRemained() / 3, scroll_height );
224                                                 }
225                                         } else if ( vt.isMin() ) {
226                                                 this._outerScroll( y - vt.getRemained() / 3, scroll_height );
227
228                                         } else if ( vt.isMax() ) {
229                                                 this._outerScroll( vt.getRemained() / 3, scroll_height );
230                                         }
231                                 }
232                         }
233
234                         if ( ht ) {
235                                 ht.update( this.options.overshootEnable );
236                                 x = ht.getPosition();
237                                 keepGoing = keepGoing || !ht.done();
238                         }
239
240                         this._setScrollPosition( x, y );
241                         this._$clip.trigger( this.options.updateEventName,
242                                         [ { x: x, y: y } ] );
243
244                         if ( keepGoing ) {
245                                 this._timerID = setTimeout( this._timerCB, this._timerInterval );
246                         } else {
247                                 this._stopMScroll();
248                         }
249                 },
250
251                 _setElementTransform: function ( $ele, x, y, duration ) {
252                         var translate,
253                                 transition;
254
255                         if ( !duration || duration === undefined ) {
256                                 transition = "none";
257                         } else {
258                                 transition =  "-webkit-transform " + duration / 1000 + "s ease-out";
259                         }
260
261                         if ( $.support.cssTransform3d ) {
262                                 translate = "translate3d(" + x + "," + y + ", 0px)";
263                         } else {
264                                 translate = "translate(" + x + "," + y + ")";
265                         }
266
267                         $ele.css({
268                                 "-moz-transform": translate,
269                                 "-webkit-transform": translate,
270                                 "-ms-transform": translate,
271                                 "-o-transform": translate,
272                                 "transform": translate,
273                                 "-webkit-transition": transition
274                         });
275                 },
276
277                 _setEndEffect: function ( dir ) {
278                         var scroll_height = this._getViewHeight() - this._$clip.height();
279
280                         if ( this._softkeyboard ) {
281                                 if ( this._effect_dir ) {
282                                         this._outerScroll( -scroll_height - this._softkeyboardHeight,
283                                                         scroll_height );
284                                 } else {
285                                         this._outerScroll( this._softkeyboardHeight, scroll_height );
286                                 }
287                                 return;
288                         }
289
290                         if ( dir === "in" ) {
291                                 if ( this._endEffect ) {
292                                         return;
293                                 }
294
295                                 this._endEffect = true;
296                                 this._setOverflowIndicator( this._effect_dir );
297                                 this._showOverflowIndicator();
298                         } else if ( dir === "out" ) {
299                                 if ( !this._endEffect ) {
300                                         return;
301                                 }
302
303                                 this._endEffect = false;
304                         } else {
305                                 this._endEffect = false;
306                                 this._setOverflowIndicator();
307                                 this._showOverflowIndicator();
308                         }
309                 },
310
311                 _setCalibration: function ( x, y ) {
312                         if ( this.options.overshootEnable ) {
313                                 this._sx = x;
314                                 this._sy = y;
315                                 return;
316                         }
317
318                         var $v = this._$view,
319                                 $c = this._$clip,
320                                 dirLock = this._directionLock,
321                                 scroll_height = 0,
322                                 scroll_width = 0;
323
324                         if ( dirLock !== "y" && this._hTracker ) {
325                                 scroll_width = $v.width() - $c.width();
326
327                                 if ( x >= 0 ) {
328                                         this._sx = 0;
329                                 } else if ( x < -scroll_width ) {
330                                         this._sx = -scroll_width;
331                                 } else {
332                                         this._sx = x;
333                                 }
334
335                                 if ( scroll_width < 0 ) {
336                                         this._sx = 0;
337                                 }
338                         }
339
340                         if ( dirLock !== "x" && this._vTracker ) {
341                                 scroll_height = this._getViewHeight() - $c.height();
342
343                                 if ( y > 0 ) {
344                                         this._sy = 0;
345
346                                         this._effect_dir = 0;
347                                         this._setEndEffect( "in" );
348                                 } else if ( y < -scroll_height ) {
349                                         this._sy = -scroll_height;
350
351                                         this._effect_dir = 1;
352                                         this._setEndEffect( "in" );
353                                 } else {
354                                         if ( this._endEffect && this._sy !== y ) {
355                                                 this._setEndEffect();
356                                         }
357
358                                         this._sy = y;
359                                 }
360
361                                 if ( scroll_height < 0 ) {
362                                         this._sy = 0;
363                                 }
364                         }
365                 },
366
367                 _setScrollPosition: function ( x, y, duration ) {
368                         var $v = this._$view,
369                                 sm = this.options.scrollMethod,
370                                 $vsb = this._$vScrollBar,
371                                 $hsb = this._$hScrollBar,
372                                 $sbt;
373
374                         this._setCalibration( x, y );
375
376                         x = this._sx;
377                         y = this._sy;
378
379                         if ( sm === "translate" ) {
380                                 this._setElementTransform( $v, x + "px", y + "px", duration );
381                         } else {
382                                 $v.css( {left: x + "px", top: y + "px"} );
383                         }
384
385                         if ( $vsb ) {
386                                 $sbt = $vsb.find(".ui-scrollbar-thumb");
387
388                                 if ( sm === "translate" ) {
389                                         this._setElementTransform( $sbt, "0px",
390                                                 -y / this._getViewHeight() * $sbt.parent().height() + "px",
391                                                 duration );
392                                 } else {
393                                         $sbt.css( "top", -y / this._getViewHeight() * 100 + "%" );
394                                 }
395                         }
396
397                         if ( $hsb ) {
398                                 $sbt = $hsb.find(".ui-scrollbar-thumb");
399
400                                 if ( sm === "translate" ) {
401                                         this._setElementTransform( $sbt,
402                                                 -x / $v.width() * $sbt.parent().width() + "px", "0px",
403                                                 duration);
404                                 } else {
405                                         $sbt.css("left", -x / $v.width() * 100 + "%");
406                                 }
407                         }
408                 },
409
410                 _outerScroll: function ( y, scroll_height ) {
411                         var self = this,
412                                 top = $( window ).scrollTop() - window.screenTop,
413                                 sy = 0,
414                                 duration = this.options.snapbackDuration,
415                                 start = getCurrentTime(),
416                                 tfunc;
417
418                         if ( !this.options.outerScrollEnable ) {
419                                 return;
420                         }
421
422                         if ( this._$clip.jqmData("scroll") !== "y" ) {
423                                 return;
424                         }
425
426                         if ( this._outerScrolling ) {
427                                 return;
428                         }
429
430                         if ( y > 0 ) {
431                                 sy = ( window.screenTop ? window.screenTop : -y );
432                         } else if ( y < -scroll_height ) {
433                                 sy = -y - scroll_height;
434                         } else {
435                                 return;
436                         }
437
438                         tfunc = function () {
439                                 var elapsed = getCurrentTime() - start;
440
441                                 if ( elapsed >= duration ) {
442                                         window.scrollTo( 0, top + sy );
443                                         self._outerScrolling = undefined;
444
445                                         self._stopMScroll();
446                                 } else {
447                                         ec = $.easing.easeOutQuad( elapsed / duration,
448                                                         elapsed, 0, 1, duration );
449
450                                         window.scrollTo( 0, top + ( sy * ec ) );
451                                         self._outerScrolling = setTimeout( tfunc, self._timerInterval );
452                                 }
453                         };
454                         this._outerScrolling = setTimeout( tfunc, self._timerInterval );
455                 },
456
457                 _scrollTo: function ( x, y, duration ) {
458                         var self = this,
459                                 start = getCurrentTime(),
460                                 efunc = $.easing.easeOutQuad,
461                                 sx = this._sx,
462                                 sy = this._sy,
463                                 dx = x - sx,
464                                 dy = y - sy,
465                                 tfunc;
466
467                         x = -x;
468                         y = -y;
469
470                         tfunc = function () {
471                                 var elapsed = getCurrentTime() - start,
472                                     ec;
473
474                                 if ( elapsed >= duration ) {
475                                         self._timerID = 0;
476                                         self._setScrollPosition( x, y );
477                                 } else {
478                                         ec = efunc( elapsed / duration, elapsed, 0, 1, duration );
479
480                                         self._setScrollPosition( sx + ( dx * ec ), sy + ( dy * ec ) );
481                                         self._timerID = setTimeout( tfunc, self._timerInterval );
482                                 }
483                         };
484
485                         this._timerID = setTimeout( tfunc, this._timerInterval );
486                 },
487
488                 scrollTo: function ( x, y, duration ) {
489                         this._stopMScroll();
490                         this._didDrag = false;
491
492                         if ( !duration || this.options.scrollMethod === "translate" ) {
493                                 this._setScrollPosition( x, y, duration );
494                         } else {
495                                 this._scrollTo( x, y, duration );
496                         }
497                 },
498
499                 getScrollPosition: function () {
500                         return { x: -this._sx, y: -this._sy };
501                 },
502
503                 skipDragging: function ( value ) {
504                         this._skip_dragging = value;
505                 },
506
507                 _getScrollHierarchy: function () {
508                         var svh = [],
509                                 d;
510
511                         this._$clip.parents( ".ui-scrollview-clip").each( function () {
512                                 d = $( this ).jqmData("scrollview");
513                                 if ( d ) {
514                                         svh.unshift( d );
515                                 }
516                         } );
517                         return svh;
518                 },
519
520                 _getAncestorByDirection: function ( dir ) {
521                         var svh = this._getScrollHierarchy(),
522                                 n = svh.length,
523                                 sv,
524                                 svdir;
525
526                         while ( 0 < n-- ) {
527                                 sv = svh[n];
528                                 svdir = sv.options.direction;
529
530                                 if (!svdir || svdir === dir) {
531                                         return sv;
532                                 }
533                         }
534                         return null;
535                 },
536
537                 _handleDragStart: function ( e, ex, ey ) {
538                         this._stopMScroll();
539
540                         this._didDrag = false;
541                         this._skip_dragging = false;
542
543                         var target = $( e.target ),
544                                 self = this,
545                                 $c = this._$clip,
546                                 svdir = this.options.direction;
547
548                         /* should prevent the default behavior when click the button */
549                         this._is_button = target.is( '.ui-btn' ) ||
550                                         target.is( '.ui-btn-text' ) ||
551                                         target.is( '.ui-btn-inner' ) ||
552                                         target.is( '.ui-btn-inner .ui-icon' );
553
554                         /* should prevent the default behavior when click the slider */
555                         if ( target.parents('.ui-slider').length || target.is('.ui-slider') ) {
556                                 this._skip_dragging = true;
557                                 return;
558                         }
559
560                         if ( target.is('textarea') ) {
561                                 target.bind( "scroll", function () {
562                                         self._skip_dragging = true;
563                                         target.unbind("scroll");
564                                 });
565                         }
566
567                         /*
568                          * We need to prevent the default behavior to
569                          * suppress accidental selection of text, etc.
570                          */
571                         this._is_inputbox = target.is(':input') ||
572                                         target.parents(':input').length > 0;
573
574                         if ( this._is_inputbox ) {
575                                 target.one( "resize.scrollview", function () {
576                                         if ( ey > $c.height() ) {
577                                                 self.scrollTo( -ex, self._sy - ey + $c.height(),
578                                                         self.options.snapbackDuration );
579                                         }
580                                 });
581                         }
582
583                         if ( this.options.eventType === "mouse" && !this._is_inputbox && !this._is_button ) {
584                                 e.preventDefault();
585                         }
586
587                         this._lastX = ex;
588                         this._lastY = ey;
589                         this._startY = ey;
590                         this._doSnapBackX = false;
591                         this._doSnapBackY = false;
592                         this._speedX = 0;
593                         this._speedY = 0;
594                         this._directionLock = "";
595
596                         this._lastMove = 0;
597                         this._enableTracking();
598
599                         this._set_scrollbar_size();
600                 },
601
602                 _propagateDragMove: function ( sv, e, ex, ey, dir ) {
603                         this._hideScrollBars();
604                         this._hideOverflowIndicator();
605                         this._disableTracking();
606                         sv._handleDragStart( e, ex, ey );
607                         sv._directionLock = dir;
608                         sv._didDrag = this._didDrag;
609                 },
610
611                 _handleDragMove: function ( e, ex, ey ) {
612                         if ( this._skip_dragging ) {
613                                 return;
614                         }
615
616                         if ( !this._dragging ) {
617                                 return;
618                         }
619
620                         if ( !this._is_inputbox && !this._is_button ) {
621                                 e.preventDefault();
622                         }
623
624                         var mt = this.options.moveThreshold,
625                                 dx = ex - this._lastX,
626                                 dy = ey - this._lastY,
627                                 svdir = this.options.direction,
628                                 dir = null,
629                                 x,
630                                 y,
631                                 sv,
632                                 scope,
633                                 newX,
634                                 newY,
635                                 dirLock;
636
637                         this._lastMove = getCurrentTime();
638
639                         if ( !this._directionLock ) {
640                                 x = Math.abs( dx );
641                                 y = Math.abs( dy );
642
643                                 if ( x < mt && y < mt ) {
644                                         return false;
645                                 }
646
647                                 if ( x < y && (x / y) < 0.5 ) {
648                                         dir = "y";
649                                 } else if ( x > y && (y / x) < 0.5 ) {
650                                         dir = "x";
651                                 }
652
653                                 if ( svdir && dir && svdir !== dir ) {
654                                         /*
655                                          * This scrollview can't handle the direction the user
656                                          * is attempting to scroll. Find an ancestor scrollview
657                                          * that can handle the request.
658                                          */
659
660                                         sv = this._getAncestorByDirection( dir );
661                                         if ( sv ) {
662                                                 this._propagateDragMove( sv, e, ex, ey, dir );
663                                                 return false;
664                                         }
665                                 }
666
667                                 this._directionLock = svdir || (dir || "none");
668                         }
669
670                         newX = this._sx;
671                         newY = this._sy;
672                         dirLock = this._directionLock;
673
674                         if ( dirLock !== "y" && this._hTracker ) {
675                                 x = this._sx;
676                                 this._speedX = dx;
677                                 newX = x + dx;
678
679                                 this._doSnapBackX = false;
680
681                                 scope = ( newX > 0 || newX < this._maxX );
682
683                                 if ( scope && dirLock === "x" ) {
684                                         sv = this._getAncestorByDirection("x");
685                                         if ( sv ) {
686                                                 this._setScrollPosition( newX > 0 ?
687                                                                 0 : this._maxX, newY );
688                                                 this._propagateDragMove( sv, e, ex, ey, dir );
689                                                 return false;
690                                         }
691
692                                         newX = x + ( dx / 2 );
693                                         this._doSnapBackX = true;
694                                 }
695                         }
696
697                         if ( dirLock !== "x" && this._vTracker ) {
698                                 if ( Math.abs( this._startY - ey ) < mt && dirLock !== "xy" ) {
699                                         return;
700                                 }
701
702                                 y = this._sy;
703                                 this._speedY = dy;
704                                 newY = y + dy;
705
706                                 this._doSnapBackY = false;
707
708                                 scope = ( newY > 0 || newY < this._maxY );
709
710                                 if ( scope && dirLock === "y" ) {
711                                         sv = this._getAncestorByDirection("y");
712                                         if ( sv ) {
713                                                 this._setScrollPosition( newX,
714                                                                 newY > 0 ? 0 : this._maxY );
715                                                 this._propagateDragMove( sv, e, ex, ey, dir );
716                                                 return false;
717                                         }
718
719                                         newY = y + ( dy / 2 );
720                                         this._doSnapBackY = true;
721                                 }
722                         }
723
724                         if ( this.options.overshootEnable === false ) {
725                                 this._doSnapBackX = false;
726                                 this._doSnapBackY = false;
727                         }
728
729                         this._lastX = ex;
730                         this._lastY = ey;
731
732                         this._setScrollPosition( newX, newY );
733
734                         if ( this._didDrag === false ) {
735                                 this._didDrag = true;
736                                 this._showScrollBars();
737                                 this._showOverflowIndicator();
738
739                                 this._$clip.parents(".ui-scrollview-clip").each( function () {
740                                         $( this ).scrollview( "skipDragging", true );
741                                 } );
742                         }
743                 },
744
745                 _handleDragStop: function ( e ) {
746                         var self = this;
747
748                         if ( this._skip_dragging ) {
749                                 return;
750                         }
751
752                         var l = this._lastMove,
753                                 t = getCurrentTime(),
754                                 doScroll = (l && (t - l) <= this.options.moveIntervalThreshold),
755                                 sx = ( this._hTracker && this._speedX && doScroll ) ?
756                                                 this._speedX : ( this._doSnapBackX ? 1 : 0 ),
757                                 sy = ( this._vTracker && this._speedY && doScroll ) ?
758                                                 this._speedY : ( this._doSnapBackY ? 1 : 0 ),
759                                 svdir = this.options.direction,
760                                 x,
761                                 y;
762
763                         if ( sx || sy ) {
764                                 if ( !this._setGestureScroll( sx, sy ) ) {
765                                         this._startMScroll( sx, sy );
766                                 }
767                         } else {
768                                 this._hideScrollBars();
769                                 this._hideOverflowIndicator();
770                         }
771
772                         this._disableTracking();
773
774                         if ( this._endEffect ) {
775                                 setTimeout( function () {
776                                         self._setEndEffect( "out" );
777                                         self._hideScrollBars();
778                                         self._hideOverflowIndicator();
779                                 }, 300 );
780                         }
781
782                         return !this._didDrag;
783                 },
784
785                 _setGestureScroll: function ( sx, sy ) {
786                         var self = this,
787                                 reset = function () {
788                                         clearTimeout( self._gesture_timer );
789                                         self._gesture_dir = 0;
790                                         self._gesture_timer = undefined;
791                                 },
792                                 direction = {
793                                         top: 0,
794                                         bottom: 1,
795                                         left: 2,
796                                         right: 3
797                                 };
798
799                         if ( !sy && !sx ) {
800                                 return false;
801                         }
802
803                         if ( Math.abs( sx ) > Math.abs( sy ) ) {
804                                 dir = sx > 0 ? direction.left : direction.right;
805                         } else {
806                                 dir = sy > 0 ? direction.top : direction.bottom;
807                         }
808
809                         if ( !this._gesture_timer ) {
810                                 this._gesture_dir = dir;
811
812                                 this._gesture_timer = setTimeout( function () {
813                                         reset();
814                                 }, 1000 );
815
816                                 return false;
817                         }
818
819                         if ( this._gesture_dir !== dir ) {
820                                 reset();
821                                 return false;
822                         }
823
824                         return false;
825                 },
826
827                 _enableTracking: function () {
828                         this._dragging = true;
829                 },
830
831                 _disableTracking: function () {
832                         this._dragging = false;
833                 },
834
835                 _showScrollBars: function ( interval ) {
836                         var vclass = "ui-scrollbar-visible",
837                                 self = this;
838
839                         if ( !this.options.showScrollBars ) {
840                                 return;
841                         }
842                         if ( this._scrollbar_showed ) {
843                                 return;
844                         }
845
846                         if ( this._$vScrollBar ) {
847                                 this._$vScrollBar.addClass( vclass );
848                         }
849                         if ( this._$hScrollBar ) {
850                                 this._$hScrollBar.addClass( vclass );
851                         }
852
853                         this._scrollbar_showed = true;
854
855                         if ( interval ) {
856                                 setTimeout( function () {
857                                         self._hideScrollBars();
858                                 }, interval );
859                         }
860                 },
861
862                 _hideScrollBars: function () {
863                         var vclass = "ui-scrollbar-visible";
864
865                         if ( !this.options.showScrollBars ) {
866                                 return;
867                         }
868                         if ( !this._scrollbar_showed ) {
869                                 return;
870                         }
871
872                         if ( this._$vScrollBar ) {
873                                 this._$vScrollBar.removeClass( vclass );
874                         }
875                         if ( this._$hScrollBar ) {
876                                 this._$hScrollBar.removeClass( vclass );
877                         }
878
879                         this._scrollbar_showed = false;
880                 },
881
882                 _setOverflowIndicator: function ( dir ) {
883                         if ( dir === 1 ) {
884                                 this._opacity_top = "0";
885                                 this._opacity_bottom = "0.8";
886                         } else if ( dir === 0 ) {
887                                 this._opacity_top = "0.8";
888                                 this._opacity_bottom = "0";
889                         } else {
890                                 this._opacity_top = "0.5";
891                                 this._opacity_bottom = "0.5";
892                         }
893                 },
894
895                 _showOverflowIndicator: function () {
896                         if ( !this.options.overflowEnable || !this._overflowAvail || this._softkeyboard ) {
897                                 return;
898                         }
899
900                         this._overflow_top.animate( { opacity: this._opacity_top }, 300 );
901                         this._overflow_bottom.animate( { opacity: this._opacity_bottom }, 300 );
902
903                         this._overflow_showed = true;
904                 },
905
906                 _hideOverflowIndicator: function () {
907                         if ( !this.options.overflowEnable || !this._overflowAvail || this._softkeyboard ) {
908                                 return;
909                         }
910
911                         if ( this._overflow_showed === false ) {
912                                 return;
913                         }
914
915                         this._overflow_top.animate( { opacity: 0 }, 300 );
916                         this._overflow_bottom.animate( { opacity: 0 }, 300 );
917
918                         this._overflow_showed = false;
919                         this._setOverflowIndicator();
920                 },
921
922                 _add_event: function () {
923                         var self = this,
924                                 $c = this._$clip,
925                                 $v = this._$view;
926
927                         if ( this.options.eventType === "mouse" ) {
928                                 this._dragEvt = "mousedown mousemove mouseup click mousewheel";
929
930                                 this._dragCB = function ( e ) {
931                                         switch ( e.type ) {
932                                         case "mousedown":
933                                                 return self._handleDragStart( e,
934                                                                 e.clientX, e.clientY );
935
936                                         case "mousemove":
937                                                 return self._handleDragMove( e,
938                                                                 e.clientX, e.clientY );
939
940                                         case "mouseup":
941                                                 return self._handleDragStop( e );
942
943                                         case "click":
944                                                 return !self._didDrag;
945
946                                         case "mousewheel":
947                                                 var old = self.getScrollPosition();
948                                                 self.scrollTo( -old.x,
949                                                         -(old.y - e.originalEvent.wheelDelta) );
950                                                 break;
951                                         }
952                                 };
953                         } else {
954                                 this._dragEvt = "touchstart touchmove touchend click";
955
956                                 this._dragCB = function ( e ) {
957                                         var touches = e.originalEvent.touches;
958
959                                         switch ( e.type ) {
960                                         case "touchstart":
961                                                 if ( touches.length != 1) {
962                                                         return;
963                                                 }
964
965                                                 return self._handleDragStart( e,
966                                                                 touches[0].pageX, touches[0].pageY );
967
968                                         case "touchmove":
969                                                 if ( touches.length != 1) {
970                                                         return;
971                                                 }
972
973                                                 return self._handleDragMove( e,
974                                                                 touches[0].pageX, touches[0].pageY );
975
976                                         case "touchend":
977                                                 if ( touches.length != 0) {
978                                                         return;
979                                                 }
980
981                                                 return self._handleDragStop( e );
982
983                                         case "click":
984                                                 return !self._didDrag;
985                                         }
986                                 };
987                         }
988
989                         $v.bind( this._dragEvt, this._dragCB );
990
991                         $v.bind( "keydown", function ( e ) {
992                                 var elem,
993                                         elem_top,
994                                         scroll_top = $( window ).scrollTop() - window.screenTop,
995                                         screen_h;
996
997                                 if ( e.keyCode == 9 ) {
998                                         return false;
999                                 }
1000
1001                                 elem = $c.find(".ui-focus");
1002
1003                                 if ( elem === undefined ) {
1004                                         return;
1005                                 }
1006
1007                                 elem_top = elem.offset().top - scroll_top;
1008                                 screen_h = $c.offset().top + $c.height() - elem.height();
1009
1010                                 if ( self._softkeyboard ) {
1011                                         screen_h -= self._softkeyboardHeight;
1012                                 }
1013
1014                                 if ( ( elem_top < $c.offset().top ) || ( elem_top > screen_h ) ) {
1015                                         self.scrollTo( 0, self._sy -
1016                                                         ( elem_top - $c.offset().top - elem.height() ) );
1017                                 }
1018
1019                                 return;
1020                         });
1021
1022                         $v.bind( "keyup", function ( e ) {
1023                                 var input,
1024                                         elem,
1025                                         elem_top,
1026                                         scroll_top = $( window ).scrollTop() - window.screenTop,
1027                                         screen_h;
1028
1029                                 if ( e.keyCode != 9 ) {
1030                                         return;
1031                                 }
1032
1033                                 /* Tab Key */
1034
1035                                 input = $( this ).find(":input");
1036
1037                                 for ( i = 0; i < input.length; i++ ) {
1038                                         if ( !$( input[i] ).hasClass("ui-focus") ) {
1039                                                 continue;
1040                                         }
1041
1042                                         if ( i + 1 == input.length ) {
1043                                                 elem = $( input[0] );
1044                                         } else {
1045                                                 elem = $( input[i + 1] );
1046                                         }
1047
1048                                         elem_top = elem.offset().top - scroll_top;
1049                                         screen_h = $c.offset().top + $c.height() - elem.height();
1050
1051                                         if ( self._softkeyboard ) {
1052                                                 screen_h -= self._softkeyboardHeight;
1053                                         }
1054
1055                                         if ( ( elem_top < 0 ) || ( elem_top > screen_h ) ) {
1056                                                 self.scrollTo( 0, self._sy - elem_top +
1057                                                         elem.height() + $c.offset().top, 0);
1058                                         }
1059
1060                                         elem.focus();
1061
1062                                         break;
1063                                 }
1064
1065                                 return false;
1066                         });
1067
1068                         $c.bind( "updatelayout", function ( e ) {
1069                                 var sy,
1070                                         vh,
1071                                         view_h = self._getViewHeight();
1072
1073                                 if ( !$c.height() || !view_h ) {
1074                                         self.scrollTo( 0, 0, 0 );
1075                                         return;
1076                                 }
1077
1078                                 sy = $c.height() - view_h;
1079                                 vh = view_h - self._view_height;
1080
1081                                 self._view_height = view_h;
1082
1083                                 if ( vh == 0 || vh > $c.height() / 2 ) {
1084                                         return;
1085                                 }
1086
1087                                 if ( sy > 0 ) {
1088                                         self.scrollTo( 0, 0, 0 );
1089                                 } else if ( self._sy - sy <= -vh ) {
1090                                         self.scrollTo( 0, self._sy,
1091                                                 self.options.snapbackDuration );
1092                                 } else if ( self._sy - sy <= vh + self.options.moveThreshold ) {
1093                                         self.scrollTo( 0, sy,
1094                                                 self.options.snapbackDuration );
1095                                 }
1096                         });
1097
1098                         $( window ).bind( "resize", function ( e ) {
1099                                 var focused,
1100                                         view_h = self._getViewHeight();
1101
1102                                 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1103                                         return;
1104                                 }
1105
1106                                 if ( !$c.height() || !view_h ) {
1107                                         return;
1108                                 }
1109
1110                                 focused = $c.find(".ui-focus");
1111
1112                                 if ( focused ) {
1113                                         focused.trigger("resize.scrollview");
1114                                 }
1115
1116                                 /* calibration - after triggered throttledresize */
1117                                 setTimeout( function () {
1118                                         if ( self._sy < $c.height() - self._getViewHeight() ) {
1119                                                 self.scrollTo( 0, $c.height() - self._getViewHeight(),
1120                                                         self.options.overshootDuration );
1121                                         }
1122                                 }, 260 );
1123
1124                                 self._view_height = view_h;
1125                         });
1126
1127                         $( window ).bind( "vmouseout", function ( e ) {
1128                                 var drag_stop = false;
1129
1130                                 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1131                                         return;
1132                                 }
1133
1134                                 if ( !self._dragging ) {
1135                                         return;
1136                                 }
1137
1138                                 if ( e.pageX < 0 || e.pageX > $( window ).width() ) {
1139                                         drag_stop = true;
1140                                 }
1141
1142                                 if ( e.pageY < 0 || e.pageY > $( window ).height() ) {
1143                                         drag_stop = true;
1144                                 }
1145
1146                                 if ( drag_stop ) {
1147                                         self._hideScrollBars();
1148                                         self._hideOverflowIndicator();
1149                                         self._disableTracking();
1150                                 }
1151                         });
1152
1153                         this._softkeyboard = false;
1154                         this._softkeyboardHeight = 0;
1155
1156                         window.addEventListener( "softkeyboardchange", function ( e ) {
1157                                 if ( $(".ui-page-active").get(0) !== $c.closest(".ui-page").get(0) ) {
1158                                         return;
1159                                 }
1160
1161                                 self._softkeyboard = ( e.state === "on" ? true : false );
1162                                 self._softkeyboardHeight = parseInt( e.height ) *
1163                                                 ( $( window ).width() / window.screen.availWidth );
1164                         });
1165
1166                         $c.closest(".ui-page")
1167                                 .bind( "pageshow", function ( e ) {
1168                                         /* should be called after pagelayout */
1169                                         setTimeout( function () {
1170                                                 self._view_height = self._getViewHeight();
1171                                                 self._set_scrollbar_size();
1172                                                 self._setScrollPosition( self._sx, self._sy );
1173                                                 self._showScrollBars( 2000 );
1174                                         }, 0 );
1175                                 });
1176                 },
1177
1178                 _add_scrollbar: function () {
1179                         var $c = this._$clip,
1180                                 prefix = "<div class=\"ui-scrollbar ui-scrollbar-",
1181                                 suffix = "\"><div class=\"ui-scrollbar-track\"><div class=\"ui-scrollbar-thumb\"></div></div></div>";
1182
1183                         if ( !this.options.showScrollBars ) {
1184                                 return;
1185                         }
1186
1187                         if ( this._vTracker ) {
1188                                 $c.append( prefix + "y" + suffix );
1189                                 this._$vScrollBar = $c.children(".ui-scrollbar-y");
1190                         }
1191                         if ( this._hTracker ) {
1192                                 $c.append( prefix + "x" + suffix );
1193                                 this._$hScrollBar = $c.children(".ui-scrollbar-x");
1194                         }
1195
1196                         this._scrollbar_showed = false;
1197                 },
1198
1199                 _add_scroll_jump: function () {
1200                         var $c = this._$clip,
1201                                 self = this,
1202                                 top_btn,
1203                                 left_btn;
1204
1205                         if ( !this.options.scrollJump ) {
1206                                 return;
1207                         }
1208
1209                         if ( this._vTracker ) {
1210                                 top_btn = $( '<div class="ui-scroll-jump-top-bg">' +
1211                                                 '<div data-role="button" data-inline="true" data-icon="scrolltop" data-style="box"></div></div>' );
1212                                 $c.append( top_btn ).trigger("create");
1213
1214                                 top_btn.bind( "vclick", function () {
1215                                         self.scrollTo( 0, 0, self.options.overshootDuration );
1216                                 } );
1217                         }
1218
1219                         if ( this._hTracker ) {
1220                                 left_btn = $( '<div class="ui-scroll-jump-left-bg">' +
1221                                                 '<div data-role="button" data-inline="true" data-icon="scrollleft" data-style="box"></div></div>' );
1222                                 $c.append( left_btn ).trigger("create");
1223
1224                                 left_btn.bind( "vclick", function () {
1225                                         self.scrollTo( 0, 0, self.options.overshootDuration );
1226                                 } );
1227                         }
1228                 },
1229
1230                 _add_overflow_indicator: function () {
1231                         if ( !this.options.overflowEnable ) {
1232                                 return;
1233                         }
1234
1235                         this._overflow_top = $( '<div class="ui-overflow-indicator-top"></div>' );
1236                         this._overflow_bottom = $( '<div class="ui-overflow-indicator-bottom"></div>' );
1237
1238                         this._$clip.append( this._overflow_top );
1239                         this._$clip.append( this._overflow_bottom );
1240
1241                         this._opacity_top = "0.5";
1242                         this._opacity_bottom = "0.5";
1243                         this._overflow_showed = false;
1244                 },
1245
1246                 _set_scrollbar_size: function () {
1247                         var $c = this._$clip,
1248                                 $v = this._$view,
1249                                 cw = 0,
1250                                 vw = 0,
1251                                 ch = 0,
1252                                 vh = 0,
1253                                 thumb;
1254
1255                         if ( !this.options.showScrollBars ) {
1256                                 return;
1257                         }
1258
1259                         if ( this._hTracker ) {
1260                                 cw = $c.width();
1261                                 vw = $v.width();
1262                                 this._maxX = cw - vw;
1263
1264                                 if ( this._maxX > 0 ) {
1265                                         this._maxX = 0;
1266                                 }
1267                                 if ( this._$hScrollBar && vw ) {
1268                                         thumb = this._$hScrollBar.find(".ui-scrollbar-thumb");
1269                                         thumb.css( "width", (cw >= vw ? "0" :
1270                                                         (Math.floor(cw / vw * 100) || 1) + "%") );
1271                                 }
1272                         }
1273
1274                         if ( this._vTracker ) {
1275                                 ch = $c.height();
1276                                 vh = this._getViewHeight();
1277                                 this._maxY = ch - vh;
1278
1279                                 if ( this._maxY > 0 || vh === 0 ) {
1280                                         this._maxY = 0;
1281                                 }
1282                                 if ( ( this._$vScrollBar && vh ) || vh === 0 ) {
1283                                         thumb = this._$vScrollBar.find(".ui-scrollbar-thumb");
1284                                         thumb.css( "height", (ch >= vh ? "0" :
1285                                                         (Math.floor(ch / vh * 100) || 1) + "%") );
1286
1287                                         this._overflowAvail = !!thumb.height();
1288                                 }
1289                         }
1290                 }
1291         });
1292
1293         $.extend( MomentumTracker.prototype, {
1294                 start: function ( pos, speed, duration, minPos, maxPos ) {
1295                         var tstate = ( pos < minPos || pos > maxPos ) ?
1296                                         tstates.snapback : tstates.scrolling,
1297                                 pos_temp;
1298
1299                         this.state = ( speed !== 0 ) ? tstate : tstates.done;
1300                         this.pos = pos;
1301                         this.speed = speed;
1302                         this.duration = ( this.state === tstates.snapback ) ?
1303                                         this.options.snapbackDuration : duration;
1304                         this.minPos = minPos;
1305                         this.maxPos = maxPos;
1306
1307                         this.fromPos = ( this.state === tstates.snapback ) ? this.pos : 0;
1308                         pos_temp = ( this.pos < this.minPos ) ? this.minPos : this.maxPos;
1309                         this.toPos = ( this.state === tstates.snapback ) ? pos_temp : 0;
1310
1311                         this.startTime = getCurrentTime();
1312                 },
1313
1314                 reset: function () {
1315                         this.state = tstates.done;
1316                         this.pos = 0;
1317                         this.speed = 0;
1318                         this.minPos = 0;
1319                         this.maxPos = 0;
1320                         this.duration = 0;
1321                         this.remained = 0;
1322                 },
1323
1324                 update: function ( overshootEnable ) {
1325                         var state = this.state,
1326                                 cur_time = getCurrentTime(),
1327                                 duration = this.duration,
1328                                 elapsed =  cur_time - this.startTime,
1329                                 dx,
1330                                 x,
1331                                 didOverShoot;
1332
1333                         if ( state === tstates.done ) {
1334                                 return this.pos;
1335                         }
1336
1337                         elapsed = elapsed > duration ? duration : elapsed;
1338
1339                         this.remained = duration - elapsed;
1340
1341                         if ( state === tstates.scrolling || state === tstates.overshot ) {
1342                                 dx = this.speed *
1343                                         ( 1 - $.easing[this.easing]( elapsed / duration,
1344                                                                 elapsed, 0, 1, duration ) );
1345
1346                                 x = this.pos + dx;
1347
1348                                 didOverShoot = ( state === tstates.scrolling ) &&
1349                                         ( x < this.minPos || x > this.maxPos );
1350
1351                                 if ( didOverShoot ) {
1352                                         x = ( x < this.minPos ) ? this.minPos : this.maxPos;
1353                                 }
1354
1355                                 this.pos = x;
1356
1357                                 if ( state === tstates.overshot ) {
1358                                         if ( !overshootEnable ) {
1359                                                 this.state = tstates.done;
1360                                         }
1361                                         if ( elapsed >= duration ) {
1362                                                 this.state = tstates.snapback;
1363                                                 this.fromPos = this.pos;
1364                                                 this.toPos = ( x < this.minPos ) ?
1365                                                                 this.minPos : this.maxPos;
1366                                                 this.duration = this.options.snapbackDuration;
1367                                                 this.startTime = cur_time;
1368                                                 elapsed = 0;
1369                                         }
1370                                 } else if ( state === tstates.scrolling ) {
1371                                         if ( didOverShoot && overshootEnable ) {
1372                                                 this.state = tstates.overshot;
1373                                                 this.speed = dx / 2;
1374                                                 this.duration = this.options.overshootDuration;
1375                                                 this.startTime = cur_time;
1376                                         } else if ( elapsed >= duration ) {
1377                                                 this.state = tstates.done;
1378                                         }
1379                                 }
1380                         } else if ( state === tstates.snapback ) {
1381                                 if ( elapsed >= duration ) {
1382                                         this.pos = this.toPos;
1383                                         this.state = tstates.done;
1384                                 } else {
1385                                         this.pos = this.fromPos + (( this.toPos - this.fromPos ) *
1386                                                 $.easing[this.easing]( elapsed / duration,
1387                                                         elapsed, 0, 1, duration ));
1388                                 }
1389                         }
1390
1391                         return this.pos;
1392                 },
1393
1394                 done: function () {
1395                         return this.state === tstates.done;
1396                 },
1397
1398                 isMin: function () {
1399                         return this.pos === this.minPos;
1400                 },
1401
1402                 isMax: function () {
1403                         return this.pos === this.maxPos;
1404                 },
1405
1406                 isAvail: function () {
1407                         return !( this.minPos === this.maxPos );
1408                 },
1409
1410                 getRemained: function () {
1411                         return this.remained;
1412                 },
1413
1414                 getPosition: function () {
1415                         return this.pos;
1416                 }
1417         });
1418
1419         $( document ).bind( 'pagecreate create', function ( e ) {
1420                 var $page = $( e.target ),
1421                         content_scroll = $page.find(".ui-content").jqmData("scroll");
1422
1423                 /* content scroll */
1424                 if ( $.support.scrollview === undefined ) {
1425                         $.support.scrollview = true;
1426                 }
1427
1428                 if ( $.support.scrollview === true && content_scroll === undefined ) {
1429                         content_scroll = "y";
1430                 }
1431
1432                 if ( content_scroll !== "y" ) {
1433                         content_scroll = "none";
1434                 }
1435
1436                 $page.find(".ui-content").attr( "data-scroll", content_scroll );
1437
1438                 $page.find(":jqmData(scroll)").not(".ui-scrollview-clip").each( function () {
1439                         if ( $( this ).hasClass("ui-scrolllistview") ) {
1440                                 $( this ).scrolllistview();
1441                         } else {
1442                                 var st = $( this ).jqmData("scroll"),
1443                                         dir = st && ( st.search(/^[xy]/) !== -1 ) ? st : null,
1444                                         content = $(this).hasClass("ui-content"),
1445                                         opts;
1446
1447                                 if ( st === "none" ) {
1448                                         return;
1449                                 }
1450
1451                                 opts = {
1452                                         direction: dir || undefined,
1453                                         overflowEnable: content,
1454                                         scrollMethod: $( this ).jqmData("scroll-method") || undefined,
1455                                         scrollJump: $( this ).jqmData("scroll-jump") || undefined
1456                                 };
1457
1458                                 $( this ).scrollview( opts );
1459                         }
1460                 });
1461         });
1462
1463         $( document ).bind( 'pageshow', function ( e ) {
1464                 var $page = $( e.target ),
1465                         scroll = $page.find(".ui-content").jqmData("scroll");
1466
1467                 if ( scroll === "y" ) {
1468                         resizePageContentHeight( e.target );
1469                 }
1470         });
1471
1472 }( jQuery, window, document ) );
1473