1 /* ***************************************************************************
2 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software" ),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 * ***************************************************************************
24 // most of following codes are derived from jquery.mobile.scrollview.js
25 (function ( $, window, document, undefined ) {
27 function circularNum( num, total ) {
35 function setElementTransform( $ele, x, y ) {
36 var v = "translate3d( " + x + "," + y + ", 0px)";
39 "-webkit-transform": v,
44 function MomentumTracker( options ) {
45 this.options = $.extend( {}, options );
46 this.easing = "easeOutQuad";
55 function getCurrentTime() {
56 return ( new Date()).getTime();
59 $.extend( MomentumTracker.prototype, {
60 start: function ( pos, speed, duration ) {
61 this.state = ( speed != 0 ) ? tstates.scrolling : tstates.done;
64 this.duration = duration;
69 this.startTime = getCurrentTime();
73 this.state = tstates.done;
80 var state = this.state,
86 if ( state == tstates.done ) {
90 duration = this.duration;
91 elapsed = getCurrentTime() - this.startTime;
92 elapsed = elapsed > duration ? duration : elapsed;
94 dx = this.speed * ( 1 - $.easing[this.easing](elapsed / duration, elapsed, 0, 1, duration ) );
99 if ( elapsed >= duration ) {
100 this.state = tstates.done;
107 return this.state == tstates.done;
110 getPosition: function () {
115 jQuery.widget( "mobile.circularview", jQuery.mobile.widget, {
119 scrollDuration: 2000,
122 moveIntervalThreshold: 150,
124 startEventName: "scrollstart",
125 updateEventName: "scrollupdate",
126 stopEventName: "scrollstop",
128 eventType: $.support.touch ? "touch" : "mouse",
130 delayedClickSelector: "a, .ui-btn",
131 delayedClickEnabled: false
134 _makePositioned: function ( $ele ) {
135 if ( $ele.css( 'position' ) == 'static' ) {
136 $ele.css( 'position', 'relative' );
140 _create: function () {
141 this._$clip = $( this.element).addClass( "ui-scrollview-clip" );
142 var $child = this._$clip.children(),
144 //if ( $child.length > 1 ) {
145 $child = this._$clip.wrapInner( "<div></div>" ).children();
147 this._$view = $child.addClass( "ui-scrollview-view" );
148 this._$list = $child.children();
150 this._$clip.css( "overflow", "hidden" );
151 this._makePositioned( this._$clip );
153 this._$view.css( "overflow", "hidden" );
154 this._tracker = new MomentumTracker( this.options );
156 this._timerInterval = 1000 / this.options.fps;
160 this._timerCB = function () { self._handleMomentumScroll(); };
164 this._addBehaviors();
167 refresh: function () {
170 this._viewWidth = this._$view.width();
171 this._clipWidth = $( window ).width();
172 this._itemWidth = this._$list.children().first().outerWidth();
173 this._$items = this._$list.children().detach();
174 itemsPerView = this._clipWidth / this._itemWidth;
175 itemsPerView = Math.ceil( itemsPerView * 10 ) / 10;
176 this._itemsPerView = parseInt( itemsPerView, 10 );
178 this._rx = -this._itemWidth;
179 this._sx = -this._itemWidth;
183 _startMScroll: function ( speedX, speedY ) {
186 var keepGoing = false,
187 duration = this.options.scrollDuration,
192 this._$clip.trigger( this.options.startEventName);
194 t.start( this._rx, speedX, duration, (v > c ) ? -(v - c) : 0, 0 );
195 keepGoing = !t.done();
198 this._timerID = setTimeout( this._timerCB, this._timerInterval );
202 //console.log( "startmscroll" + this._rx + "," + this._sx );
205 _stopMScroll: function () {
206 if ( this._timerID ) {
207 this._$clip.trigger( this.options.stopEventName );
208 clearTimeout( this._timerID );
213 if ( this._tracker ) {
214 this._tracker.reset();
216 //console.log( "stopmscroll" + this._rx + "," + this._sx );
219 _handleMomentumScroll: function () {
220 var keepGoing = false,
230 keepGoing = !t.done();
234 this._setScrollPosition( x, y );
237 this._$clip.trigger( this.options.updateEventName, [ { x: x, y: y } ] );
240 this._timerID = setTimeout( this._timerCB, this._timerInterval );
246 _setItems: function () {
250 for ( i = -1; i < this._itemsPerView + 1; i++ ) {
251 $item = this._$items[ circularNum( i, this._$items.length ) ];
252 this._$list.append( $item );
254 setElementTransform( this._$view, this._sx + "px", 0 );
255 this._$view.width( this._itemWidth * ( this._itemsPerView + 2 ) );
256 this._viewWidth = this._$view.width();
259 _setScrollPosition: function ( x, y ) {
262 di = parseInt( dx / this._itemWidth, 10 ),
268 for ( i = 0; i < di; i++ ) {
269 this._$list.children().last().detach();
270 idx = -parseInt( ( sx / this._itemWidth ) + i + 3, 10 );
271 $item = this._$items[ circularNum( idx, this._$items.length ) ];
272 this._$list.prepend( $item );
273 //console.log( "di > 0 : " + idx );
275 } else if ( di < 0 ) {
276 for ( i = 0; i > di; i-- ) {
277 this._$list.children().first().detach();
278 idx = this._itemsPerView - parseInt( ( sx / this._itemWidth ) + i, 10 );
279 $item = this._$items[ circularNum( idx, this._$items.length ) ];
280 this._$list.append( $item );
281 //console.log( "di < 0 : " + idx );
285 this._sx += di * this._itemWidth;
287 setElementTransform( this._$view, ( x - this._sx - this._itemWidth ) + "px", 0 );
289 //console.log( "rx " + this._rx + "sx " + this._sx );
292 _enableTracking: function () {
293 $(document).bind( this._dragMoveEvt, this._dragMoveCB );
294 $(document).bind( this._dragStopEvt, this._dragStopCB );
297 _disableTracking: function () {
298 $(document).unbind( this._dragMoveEvt, this._dragMoveCB );
299 $(document).unbind( this._dragStopEvt, this._dragStopCB );
302 _getScrollHierarchy: function () {
305 this._$clip.parents( '.ui-scrollview-clip' ).each( function () {
306 d = $( this ).jqmData( 'circulaview' );
314 centerTo: function ( selector ) {
318 for ( i = 0; i < this._$items.length; i++ ) {
319 if ( $( this._$items[i]).is( selector ) ) {
320 newX = -( i * this._itemWidth - this._clipWidth / 2 + this._itemWidth * 2 );
321 this.scrollTo( newX, 0 );
322 console.log( i + "," + newX );
328 scrollTo: function ( x, y, duration ) {
331 this._setScrollPosition( x, y );
340 start = getCurrentTime(),
341 efunc = $.easing.easeOutQuad,
350 tfunc = function () {
351 elapsed = getCurrentTime() - start;
352 if ( elapsed >= duration ) {
354 self._setScrollPosition( x, y );
356 ec = efunc( elapsed / duration, elapsed, 0, 1, duration );
357 self._setScrollPosition( sx + ( dx * ec ), sy + ( dy * ec ) );
358 self._timerID = setTimeout( tfunc, self._timerInterval );
362 this._timerID = setTimeout( tfunc, this._timerInterval );
365 getScrollPosition: function () {
366 return { x: -this._rx, y: 0 };
369 _handleDragStart: function ( e, ex, ey ) {
370 $.each( this._getScrollHierarchy(), function ( i, sv ) {
376 if ( this.options.delayedClickEnabled ) {
377 this._$clickEle = $( e.target ).closest( this.options.delayedClickSelector );
383 this._didDrag = false;
386 this._enableTracking();
391 if ( this.options.eventType == "mouse" || this.options.delayedClickEnabled ) {
394 //console.log( "scrollstart" + this._rx + "," + this._sx );
398 _handleDragMove: function ( e, ex, ey ) {
399 this._lastMove = getCurrentTime();
401 var dx = ex - this._lastX,
402 dy = ey - this._lastY;
407 this._didDrag = true;
412 this._mx = ex - this._ox;
414 this._setScrollPosition( this._nx + this._mx, 0 );
416 //console.log( "scrollmove" + this._rx + "," + this._sx );
420 _handleDragStop: function ( e ) {
421 var l = this._lastMove,
422 t = getCurrentTime(),
423 doScroll = l && ( t - l ) <= this.options.moveIntervalThreshold,
424 sx = ( this._tracker && this._speedX && doScroll ) ? this._speedX : 0,
427 this._rx = this._mx ? this._nx + this._mx : this._rx;
430 this._startMScroll( sx, sy );
433 //console.log( "scrollstop" + this._rx + "," + this._sx );
435 this._disableTracking();
437 if ( !this._didDrag && this.options.delayedClickEnabled && this._$clickEle.length ) {
439 .trigger( "mousedown" )
440 .trigger( "mouseup" )
444 if ( this._didDrag ) {
449 return this._didDrag ? false : undefined;
452 _addBehaviors: function () {
455 if ( this.options.eventType === "mouse" ) {
456 this._dragStartEvt = "mousedown";
457 this._dragStartCB = function ( e ) {
458 return self._handleDragStart( e, e.clientX, e.clientY );
461 this._dragMoveEvt = "mousemove";
462 this._dragMoveCB = function ( e ) {
463 return self._handleDragMove( e, e.clientX, e.clientY );
466 this._dragStopEvt = "mouseup";
467 this._dragStopCB = function ( e ) {
468 return self._handleDragStop( e );
471 this._$view.bind( "vclick", function (e) {
472 return !self._didDrag;
476 this._dragStartEvt = "touchstart";
477 this._dragStartCB = function ( e ) {
478 var t = e.originalEvent.targetTouches[0];
479 return self._handleDragStart(e, t.pageX, t.pageY );
482 this._dragMoveEvt = "touchmove";
483 this._dragMoveCB = function ( e ) {
484 var t = e.originalEvent.targetTouches[0];
485 return self._handleDragMove(e, t.pageX, t.pageY );
488 this._dragStopEvt = "touchend";
489 this._dragStopCB = function ( e ) {
490 return self._handleDragStop( e );
493 this._$view.bind( this._dragStartEvt, this._dragStartCB );
497 $( document ).bind( "pagecreate create", function ( e ) {
498 $( $.mobile.circularview.prototype.options.initSelector, e.target ).circularview();
501 }( jQuery, window, document ) ); // End Component