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