\r
-(function(){\r
\r
-var m = Math,\r
- ABS = new Function( 'v', 'return v<0?-v:v' );\r
-\r
- function Options(){};\r
- \r
- X_Class_override( Options.prototype, {\r
- hScroll : true,\r
- vScroll : true,\r
- x : 0,\r
- y : 0,\r
- bounce : true,\r
- bounceLock : false,\r
- momentum : true,\r
- lockDirection : true,\r
- useTransform : true,\r
- useTransition : true,\r
- topOffset : 0,\r
- checkDOMChanges : false, // Experimental\r
- handleClick : true,\r
- \r
- // Scrollbar\r
- hScrollbar : true,\r
- vScrollbar : true,\r
- fixedScrollbar : X_UA.Android,\r
- hideScrollbar : X_UA.iOS,\r
- fadeScrollbar : X_UA.iOS, //&& has3d,\r
- scrollbarClass : '',\r
- \r
- // Zoom\r
- zoom : false,\r
- zoomMin : 1,\r
- zoomMax : 4,\r
- doubleTapZoom : 2,\r
- wheelAction : 'scroll',\r
- \r
- // Snap\r
- snap : false,\r
- snapThreshold : 1 //,\r
- });\r
-\r
- function Scrollbar( owner, dir ){\r
- this.owner = owner;\r
- this.options = owner.options;\r
- this.dir = dir;\r
- if( dir === 'h' ){\r
- this.XorY = 'x';\r
- this.widthOrHeight = 'width';\r
- this.transrateXorY = 'translateX(';\r
- };\r
+function X_UI_ScrollBox_momentum( current, start, time, lowerMargin, wrapperSize, deceleration ){\r
+ var distance = current - start,\r
+ speed = Math.abs( distance ) / time,\r
+ destination,\r
+ duration;\r
+\r
+ deceleration = deceleration === undefined ? 0.0006 : deceleration;\r
+\r
+ destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );\r
+ duration = speed / deceleration;\r
+\r
+ if( destination < lowerMargin ){\r
+ destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;\r
+ distance = Math.abs( destination - current );\r
+ duration = distance / speed;\r
+ } else\r
+ if ( destination > 0 ) {\r
+ destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;\r
+ distance = Math.abs( current ) + destination;\r
+ duration = distance / speed;\r
};\r
- X_Class_override( Scrollbar.prototype, {\r
- owner : null,\r
- dir : null,\r
- options : null,\r
- XorY : 'y',\r
- widthOrHeight : 'height',\r
- transrateXorY : 'translateY(',\r
- active : false,\r
- xnodeWrapper : null,\r
- xnodeIndicator : null,\r
- wrapperSize : 0,\r
- indicatorSize : 0,\r
- maxScroll : 0,\r
- scrollPercent : 0,\r
- \r
- update : function(){\r
- // remove scrollbar\r
- if( !this.active ){\r
- if( this.xnodeWrapper ){\r
- X.CSS.transform && this.xnodeIndicator.css( 'transform', '' );\r
- this.xnodeWrapper.css( 'display', 'none' );\r
- };\r
- return;\r
- };\r
- \r
- // create scrollbar\r
- if( !this.xnodeWrapper ){\r
- // Create the scrollbar wrapper\r
- this.xnodeWrapper = this.owner.xnodeTarget.create( 'div' )\r
- .className(\r
- this.options.scrollbarClass ?\r
- this.options.scrollbarClass + this.dir.toUpperCase() :\r
- this.dir === 'h' ?\r
- ( this.vScrollbar && this.vScrollbar.active ? 'ScrollBox-Scrollbar-Wrapper-V-withH' : 'ScrollBox-Scrollbar-Wrapper-H' ) :\r
- ( this.hScrollbar && this.hScrollbar.active ? 'ScrollBox-Scrollbar-Wrapper-H-withV' : 'ScrollBox-Scrollbar-Wrapper-V' ) \r
- );\r
- \r
- this.options.fadeScrollbar &&\r
- this.xnodeWrapper.css(\r
- {\r
- opacity : 0,\r
- transitionProperty : 'opacity',\r
- transitionDuration : '350ms'\r
- }\r
- );\r
- \r
- // Create the scrollbar indicator\r
- \r
- this.xnodeIndicator = this.xnodeWrapper.create( 'div' );\r
- \r
- !this.options.scrollbarClass &&\r
- this.xnodeIndicator.className(\r
- this.dir === 'h' ?\r
- 'ScrollBox-Scrollbar-Indicator-H' :\r
- 'ScrollBox-Scrollbar-Indicator-V'\r
- );\r
- //if (this.options.useTransition) bar.style.cssText += ';' + cssVendor + 'transition-timing-function:cubic-bezier(0.33,0.66,0.66,1)';\r
- };\r
- \r
- if( this.dir === 'h' ){\r
- this.wrapperSize = this.hScrollbarWrapper.clientWidth;\r
- this.indicatorSize = m.max( m.round( wrapperSize * wrapperSize / this.owner.scrollerW ), 8 );\r
- //this.hScrollbarIndicator.style.width = this.hScrollbarIndicatorSize + 'px';\r
- this.maxScroll = wrapperSize - indicatorSize;\r
- this.scrollPercent = maxScroll / this.owner.maxScrollX;\r
- } else {\r
- this.wrapperSize = this.vScrollbarWrapper.clientHeight;\r
- this.indicatorSize = m.max( m.round( wrapperSize * wrapperSize / this.owner.scrollerH ), 8);\r
- // this.vScrollbarIndicator.style.height = indicatorSize + 'px';\r
- this.maxScroll = this.vScrollbarSize - indicatorSize;\r
- this.scrollPercent = maxScroll / this.owner.maxScrollY;\r
- }; \r
- this.xnodeIndicator.css( this.widthOrHeight, size + 'px' );\r
- // Reset position\r
- this.updatePosition( this.owner[ this.XorY ], true );\r
- },\r
- \r
- updatePosition : function( pos, hidden ){\r
- var size;\r
- pos = this.scrollPercent * pos;\r
- \r
- if( pos < 0 ){\r
- if( !this.options.fixedScrollbar ){\r
- size = this.indicatorSize + m.round( pos * 3 );\r
- if( size < 8 ) size = 8;\r
- //this.xnodeIndicator.style[dir == 'h' ? 'width' : 'height'] = size + 'px';\r
- this.xnodeIndicator.css( this.widthOrHeight, size + 'px' );\r
- };\r
- pos = 0;\r
- } else\r
- if( this.maxScroll < pos ){\r
- if( !this.options.fixedScrollbar ){\r
- size = this.indicatorSize - m.round( ( pos - this.maxScroll ) * 3 );\r
- if( size < 8 ) size = 8;\r
- //this.xnodeIndicator.style[dir == 'h' ? 'width' : 'height'] = size + 'px';\r
- this.xnodeIndicator.css( this.widthOrHeight, size + 'px' );\r
- pos = this.maxScroll + this.indicatorSize - size;\r
- } else {\r
- pos = this.maxScroll;\r
- };\r
- };\r
- \r
- if (this.options.useTransition){\r
- this.xnodeWrapper.css( {\r
- transitionDelay : '0',\r
- opacity : hidden && this.options.hideScrollbar ? '0' : '1'\r
- });\r
- //this.xnodeIndicator.style[transform] = 'translate(' + (dir == 'h' ? pos + 'px,0)' : '0,' + pos + 'px)') + translateZ;\r
- this.xnodeIndicator.anime( this.dir === 'h' ? { x : pos } : { y : pos } ); \r
- };\r
- }\r
- });\r
-\r
- // Constructor\r
- function iScroll( uinodeRoot, uinodeTarget, xnodeTarget, xnodeScroller, options ){\r
- var i;\r
- \r
- this.uinodeRoot = uinodeRoot;\r
- this.uinodeTarget = uinodeTarget;\r
- this.xnodeTarget = xnodeTarget;\r
- this.xnodeScroller = xnodeScroller;\r
-\r
- // Default options\r
- this.options = new Options();\r
-\r
- // User defined options\r
- if( options ) for (i in options) X_EMPTY_OBJECT[ k ] || ( this.options[i] = options[i] );\r
- \r
- this.options.hScroll && ( this.hScrollbar = new Scrollbar( 'h', this ) );\r
- this.options.vScroll && ( this.vScrollbar = new Scrollbar( 'v', this ) );\r
- \r
- // Set starting position\r
- this.x = this.options.x;\r
- this.y = this.options.y;\r
-\r
- // Normalize options\r
- this.options.useTransform = X.CSS.transform && this.options.useTransform;\r
- this.options.hScrollbar = this.options.hScroll && this.options.hScrollbar;\r
- this.options.vScrollbar = this.options.vScroll && this.options.vScrollbar;\r
- this.options.zoom = this.options.useTransform && this.options.zoom;\r
- this.options.useTransition = X.CSS.transition && this.options.useTransition;\r
-\r
- // Helpers FIX ANDROID BUG!\r
- // translate3d and scale doesn't work together!\r
- // Ignoring 3d ONLY WHEN YOU SET this.options.zoom\r
- //if ( this.options.zoom && X_UA.isAndroid ){\r
- // translateZ = '';\r
- //}\r
- \r
- // Set some default styles\r
\r
- if (this.options.useTransform){\r
- this.scroller.style[X.CSS.transform] = 'translate(' + this.x + 'px,' + this.y + 'px)' + translateZ;\r
- this.scroller.style[X.CSS.transformOrigin] = '0 0';\r
- } else {\r
- this.scroller.style.cssText += ';position:absolute;top:' + this.y + 'px;left:' + this.x + 'px';\r
- };\r
-\r
- if (this.options.useTransition){\r
- this.scroller.style[X.CSS.transition.Property] = this.options.useTransform ? X.CSS.cssVendor + 'transform' : 'top left';\r
- this.scroller.style[X.CSS.transition.Duration] = '0'; \r
- this.scroller.style[X.CSS.transition.TimingFunction] = 'cubic-bezier(0.33,0.66,0.66,1)';\r
- this.options.fixedScrollbar = true;\r
- };\r
-\r
- this.refresh();\r
-\r
- //this._bind(RESIZE_EV, window);\r
- X.Dom.Event.add( window, RESIZE_EV, this );\r
- //this._bind(START_EV);\r
- uinodeTarget.listen( X.UI.Event.DRAG_START, this );\r
+ return {\r
+ destination : Math.round( destination ),\r
+ duration : duration\r
+ };\r
+};\r
+\r
+var X_UI_ScrollBox_SUPPORT_ATTRS = {\r
+ // スクロール開始するために必要な移動距離、縦か横、どちらか制限する場合、より重要\r
+ directionLockThreshold : [ 10, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.LENGTH ],\r
+ scrollX : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
+ scrollY : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
+ enabled : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
+ bounce : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
+ bounceTime : [ 600, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.TIME ],\r
+ useWheel : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
+ useKey : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
};\r
-\r
-// Prototype\r
-X_Class_override( iScroll.prototype, {\r
- uinodeRoot : null,\r
- uinodeTarget : null,\r
- xnodeTarget : null,\r
- xnodeScroller : null,\r
- options : null,\r
- enabled : true,\r
- x : 0,\r
- y : 0,\r
- steps : [],\r
- scale : 1,\r
- currPageX : 0,\r
- currPageY : 0,\r
- pagesX : [],\r
- pagesY : [],\r
- animeTimerID : 0,\r
- wheelZoomCount : 0,\r
- \r
- wrapperW : 0,\r
- wrapperH : 0,\r
- minScrollY : 0,\r
- scrollerW : 0,\r
- scrollerH : 0,\r
- maxScrollX : 0,\r
- maxScrollY : 0,\r
- dirX : 0,\r
- dirY : 0,\r
- hScrollbar : false,\r
- vScrollbar : false,\r
- wrapperOffsetLeft : 0,\r
- wrapperOffsetTop : 0,\r
- \r
- \r
- currPageX : 0,\r
- currPageY : 0,\r
- \r
- moved : false,\r
- animating : false,\r
- zoomed : false,\r
- distX : false,\r
- distY : false,\r
- absDistX : false,\r
- absDistY : false,\r
- absStartX : false, // Needed by snap threshold\r
- absStartY : false,\r
- startX : false,\r
- startY : false,\r
- pointX : false,\r
- pointY : false,\r
- startTime : false,\r
- \r
- handleEvent: function (e) {\r
- switch(e.type) {\r
- case X.UI.Event.DRAG :\r
- return this._move(e);\r
- case X.UI.Event.WHEEL :\r
- return this._wheel(e);\r
- case X.UI.Event.DRAG_START :\r
- //if (!hasTouch && e.button !== 0) return;\r
- return this._start(e);\r
- case X.UI.Event.DRAG_END :\r
- return this._end(e);\r
- case X.UI.Event.ANIME_END :\r
- return this._transitionEnd(e);\r
- case RESIZE_EV :\r
- return this._resize(); \r
- }\r
- },\r
- \r
- _trigger : function( type, e ){\r
- \r
- return this.uinodeTarget.dispatch( );\r
- },\r
- \r
- _resize: function () {\r
- X.Timer.once( X_UA.Android ? 200 : 0, this, this.refresh );\r
- // setTimeout( this.refresh(), isAndroid ? 200 : 0);\r
- },\r
- \r
- _updateScrollPosition: function( x, y, time ){\r
- if (this.zoomed) return;\r
-\r
- this.xnodeScroller.anime({\r
- x : this.x = this.hScrollbar && this.hScrollbar.active ? m.round(x) : 0,\r
- y : this.y = this.vScrollbar && this.vScrollbar.active ? m.round(y) : 0\r
- }, time );\r
-\r
- this.hScrollbar && this.hScrollbar.active && this.hScrollbar.updatePosition( this.x );\r
- this.vScrollbar && this.vScrollbar.active && this.vScrollbar.updatePosition( this.y );\r
- },\r
- \r
- _start: function (e) {\r
- var point = hasTouch ? e.touches[0] : e,\r
- matrix, x, y,\r
- ret;\r
- //c1, c2;\r
-\r
- if (!this.enabled) return;\r
-\r
- //if (this.options.onBeforeScrollStart) this.options.onBeforeScrollStart.call(this, e);\r
- if( ( ret = this._trigger( X.UI.Event.SCROLL_BEFORE_START ) ) & X.Callback.PREVENT_DEFAULT ){\r
- return ret;\r
- };\r
-\r
- //if (this.options.useTransition || this.options.zoom) this._transitionTime(0);\r
-\r
- this.moved = false;\r
- this.animating = false;\r
- this.zoomed = false;\r
- this.distX = 0;\r
- this.distY = 0;\r
- this.absDistX = 0;\r
- this.absDistY = 0;\r
- this.dirX = 0;\r
- this.dirY = 0;\r
-\r
- if (this.options.momentum) {\r
- if (this.options.useTransform) {\r
- // Very lame general purpose alternative to CSSMatrix\r
- matrix = getComputedStyle(this.scroller, null)[transform].replace(/[^0-9\-.,]/g, '').split(',');\r
- x = +(matrix[12] || matrix[4]);\r
- y = +(matrix[13] || matrix[5]);\r
- } else {\r
- x = +getComputedStyle(this.scroller, null).left.replace(/[^0-9-]/g, '');\r
- y = +getComputedStyle(this.scroller, null).top.replace(/[^0-9-]/g, '');\r
- };\r
- \r
- if (x !== this.x || y !== this.y) {\r
- if (this.options.useTransition){\r
- //this._unbind(TRNEND_EV);\r
- X.Dom.Event.remove( this.scroller, TRNEND_EV, this );\r
- } else {\r
- X.Timer.cancelFrame(this.animeTimerID);\r
- };\r
- this.steps = this.steps ? ( this.steps.length = 0 ) : [];\r
- this._updateScrollPosition( x, y, 0 );\r
- //if (this.options.onScrollEnd) this.options.onScrollEnd.call(this);\r
- return this._trigger( X.UI.Event.SCROLL_END, e );\r
- };\r
- };\r
-\r
- this.absStartX = this.x; // Needed by snap threshold\r
- this.absStartY = this.y;\r
-\r
- this.startX = this.x;\r
- this.startY = this.y;\r
- this.pointX = point.pageX;\r
- this.pointY = point.pageY;\r
-\r
- this.startTime = e.timeStamp || X.Timer.now();\r
-\r
- this.uinodeRoot.listen( X.UI.Event.DRAG, this );\r
- this.uinodeRoot.listen( X.UI.Event.DRAG_END, this );\r
-\r
- return this._trigger( X.UI.Event.SCROLL_START, e );\r
- },\r
- \r
- _move: function (e) {\r
- var point = hasTouch ? e.touches[0] : e,\r
- deltaX = point.pageX - this.pointX,\r
- deltaY = point.pageY - this.pointY,\r
- newX = this.x + deltaX,\r
- newY = this.y + deltaY,\r
- c1, c2, scale,\r
- timestamp = e.timeStamp ||X.Timer.now(), ret;\r
-\r
- //if (this.options.onBeforeScrollMove) this.options.onBeforeScrollMove.call(this, e);\r
- if( ( ret = this._trigger( X.UI.Event.SCROLL_BEFORE_MOVE ) ) & X.Callback.PREVENT_DEFAULT ){\r
- return ret;\r
- };\r
-\r
- this.pointX = point.pageX;\r
- this.pointY = point.pageY;\r
-\r
- // Slow down if outside of the boundaries\r
- if (newX > 0 || newX < this.maxScrollX) {\r
- newX = this.options.bounce ? this.x + (deltaX / 2) : newX >= 0 || this.maxScrollX >= 0 ? 0 : this.maxScrollX;\r
- };\r
- if (newY > this.minScrollY || newY < this.maxScrollY) {\r
- newY = this.options.bounce ? this.y + (deltaY / 2) : newY >= this.minScrollY || this.maxScrollY >= 0 ? this.minScrollY : this.maxScrollY;\r
- };\r
-\r
- this.distX += deltaX;\r
- this.distY += deltaY;\r
- this.absDistX = ABS(this.distX);\r
- this.absDistY = ABS(this.distY);\r
-\r
- if (this.absDistX < 6 && this.absDistY < 6) {\r
- return;\r
- };\r
-\r
- // Lock direction\r
- if (this.options.lockDirection) {\r
- if (this.absDistX > this.absDistY + 5) {\r
- newY = this.y;\r
- deltaY = 0;\r
- } else if (this.absDistY > this.absDistX + 5) {\r
- newX = this.x;\r
- deltaX = 0;\r
- };\r
- };\r
-\r
- this.moved = true;\r
- this._updateScrollPosition(newX, newY, 0);\r
- this.dirX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;\r
- this.dirY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;\r
-\r
- if (timestamp - this.startTime > 300) {\r
- this.startTime = timestamp;\r
- this.startX = this.x;\r
- this.startY = this.y;\r
- };\r
- \r
- //if (this.options.onScrollMove) this.options.onScrollMove.call(this, e);\r
- return this._trigger( X.UI.Event.SCROLL_MOVE, e );\r
- },\r
- \r
- _end: function (e) {\r
- if (hasTouch && e.touches.length !== 0) return;\r
-\r
- var point = hasTouch ? e.changedTouches[0] : e,\r
- momentumX = { dist:0, time:0 },\r
- momentumY = { dist:0, time:0 },\r
- duration = ( e.timeStamp ||X.Timer.now() ) - this.startTime,\r
- newPosX = this.x,\r
- newPosY = this.y,\r
- distX, distY,\r
- newDuration,\r
- snap,\r
- scale;\r
-\r
- this.uinodeRoot.unlisten( X.UI.Event.DRAG, this );\r
- this.uinodeRoot.unlisten( X.UI.Event.DRAG_END, this );\r
-\r
- //this._unbind(MOVE_EV, window);\r
- //this._unbind(END_EV, window);\r
- //this._unbind(CANCEL_EV, window);\r
-\r
- //if (this.options.onBeforeScrollEnd) this.options.onBeforeScrollEnd.call(this, e);\r
- \r
-\r
- if (!this.moved) {\r
-\r
-\r
- this._resetPos(400);\r
-\r
- //if (this.options.onTouchEnd) this.options.onTouchEnd.call(this, e);\r
- return;\r
- };\r
-\r
- if (duration < 300 && this.options.momentum) {\r
- momentumX = newPosX ? this._momentum(newPosX - this.startX, duration, -this.x, this.scrollerW - this.wrapperW + this.x, this.options.bounce ? this.wrapperW : 0) : momentumX;\r
- momentumY = newPosY ? this._momentum(newPosY - this.startY, duration, -this.y, (this.maxScrollY < 0 ? this.scrollerH - this.wrapperH + this.y - this.minScrollY : 0), this.options.bounce ? this.wrapperH : 0) : momentumY;\r
-\r
- newPosX = this.x + momentumX.dist;\r
- newPosY = this.y + momentumY.dist;\r
-\r
- if ((this.x > 0 && newPosX > 0) || (this.x < this.maxScrollX && newPosX < this.maxScrollX)) momentumX = { dist:0, time:0 };\r
- if ((this.y > this.minScrollY && newPosY > this.minScrollY) || (this.y < this.maxScrollY && newPosY < this.maxScrollY)) momentumY = { dist:0, time:0 };\r
- \r
- if (momentumX.dist || momentumY.dist) {\r
- newDuration = m.max(m.max(momentumX.time, momentumY.time), 10);\r
- \r
- this.scrollTo(m.round(newPosX), m.round(newPosY), newDuration);\r
- \r
- //if (this.options.onTouchEnd) this.options.onTouchEnd.call(this, e);\r
- return;\r
- }; \r
- };\r
-\r
- this._resetPos(200);\r
- //if (this.options.onTouchEnd) this.options.onTouchEnd.call(this, e);\r
- },\r
- \r
- /*\r
- _onZoomEndEvent : null,\r
- _onZoomEndTimerComplete : function(){\r
- this.options.onZoomEnd.call( this, this._onZoomEndEvent );\r
- },\r
- */\r
- \r
- /*\r
- _onDobleTapTimerPoint : null,\r
- _onDobleTapTimerComplete : function () {\r
- var point = this._onDobleTapTimerPoint,\r
- target, ev;\r
- this.doubleTapTimer = null;\r
-\r
- // Find the last touched element\r
- target = point.target;\r
- while (target.nodeType !== 1) target = target.parentNode;\r
-\r
- if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') {\r
- ev = document.createEvent('MouseEvents');\r
- ev.initMouseEvent('click', true, true, e.view, 1,\r
- point.screenX, point.screenY, point.clientX, point.clientY,\r
- e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,\r
- 0, null);\r
- ev._fake = true;\r
- target.dispatchEvent(ev);\r
- }\r
- },*/\r
- \r
- _resetPos: function (time) {\r
- var resetX =\r
- 0 <= this.x ?\r
- 0 :\r
- this.x < this.maxScrollX ?\r
- this.maxScrollX :\r
- this.x,\r
- resetY =\r
- this.minScrollY <= this.y || 0 < this.maxScrollY ?\r
- this.minScrollY :\r
- this.y < this.maxScrollY ?\r
- this.maxScrollY :\r
- this.y;\r
-\r
- if( resetX === this.x && resetY === this.y ){\r
- if( this.moved ){\r
- this.moved = false;\r
- this._trigger( X.UI.Event.SCROLL_END );\r
- //if (this.options.onScrollEnd) this.options.onScrollEnd.call(this); // Execute custom code on scroll end\r
- };\r
- if( this.options.hideScrollbar ){\r
- this.hScrollbar && this.hScrollbar.active &&\r
- \r
- //if (vendor == 'webkit') this.hScrollbarWrapper.style[transitionDelay] = '300ms';\r
- //this.hScrollbarWrapper.style.opacity = '0';\r
- this.hScrollbar.xnodeWraper.anime( {\r
- opacity : 0\r
- }, 300 );\r
- this.vScrollbar && this.vScrollbar.active &&\r
- //if (vendor == 'webkit') this.vScrollbarWrapper.style[transitionDelay] = '300ms';\r
- //this.vScrollbarWrapper.style.opacity = '0';\r
- this.hScrollbar.xnodeWraper.anime( {\r
- opacity : 0\r
- }, 300 ); \r
- };\r
- return;\r
- };\r
- this.scrollTo(resetX, resetY, time || 0);\r
- },\r
- \r
- _wheel: function (e) {\r
- var wheelDeltaX, wheelDeltaY,\r
- deltaX, deltaY,\r
- deltaScale;\r
-/*\r
- if ('wheelDeltaX' in e) {\r
- wheelDeltaX = e.wheelDeltaX / 12;\r
- wheelDeltaY = e.wheelDeltaY / 12;\r
- } else if('wheelDelta' in e) {\r
- wheelDeltaX = wheelDeltaY = e.wheelDelta / 12;\r
- } else if ('detail' in e) {\r
- wheelDeltaX = wheelDeltaY = -e.detail * 3;\r
- } else {\r
- return;\r
- } */\r
- \r
- deltaX = this.x + e.wheelDeltaX;\r
- deltaY = this.y + e.wheelDeltaY;\r
-\r
- deltaX =\r
- 0 < deltaX ?\r
- 0 :\r
- deltaX < this.maxScrollX ?\r
- this.maxScrollX : deltaX;\r
-\r
- deltaY =\r
- this.minScrollY < deltaY ? \r
- this.minScrollY :\r
- deltaY < this.maxScrollY ?\r
- this.maxScrollY : deltaY;\r
- \r
- this.maxScrollY < 0 && this.scrollTo(deltaX, deltaY, 0);\r
- },\r
- \r
- /*\r
- _wheelTimerCompleteEvent : null,\r
- _wheelTimerComplete : function() {\r
- this.wheelZoomCount--;\r
- if (!this.wheelZoomCount && this.options.onZoomEnd) this.options.onZoomEnd.call(this, this._wheelTimerCompleteEvent );\r
- },\r
- */\r
- \r
- _transitionEnd: function( e ){\r
- if( e.target !== this.xnodeScroller ) return;\r
-\r
- //this._unbind(TRNEND_EV);\r
- //X.Dom.Event.remove( this.scroller, TRNEND_EV, this );\r
- this.animating = false;\r
- \r
- this._startAnime();\r
- \r
- return X.Callback.UN_LISTEN;\r
- },\r
-\r
-\r
- /**\r
- *\r
- * Utilities\r
- *\r
- */\r
- _startAnime: function () {\r
- var startX = this.x,\r
- startY = this.y,\r
- step, animate;\r
-\r
- if (this.animating) return;\r
- \r
- if (!this.steps.length) {\r
- this._resetPos(400);\r
- return;\r
- };\r
- \r
- step = this.steps.shift();\r
- \r
- if( step.x === startX && step.y === startY ) step.time = 0;\r
-\r
- this.animating = true;\r
- this.moved = true;\r
- \r
- //if (this.options.useTransition) {\r
- //this._transitionTime(step.time);\r
- this._updateScrollPosition( step.x, step.y, step.time );\r
- //this.animating = false;\r
- this.xnodeScroller.listenOnce( X.UI.Event.ANIME_END, this );\r
- //step.time ? X.Dom.Event.add( this.scroller, TRNEND_EV, this ) /* this._bind(TRNEND_EV) */ : this._resetPos(0);\r
- //return;\r
- //}\r
- //this._doAnimate( X.Timer.now(), step, startX, startY );\r
- },\r
-\r
-/*\r
- _doAnimate : function( startTime, step, startX, startY ){\r
- var now =X.Timer.now(),\r
- easeOut, newX, newY;\r
-\r
- if (now >= startTime + step.time) {\r
- this._updateScrollPosition(step.x, step.y);\r
- this.animating = false;\r
- //if (this.options.onAnimationEnd) this.options.onAnimationEnd.call( this ); // Execute custom code on animation end\r
- this._startAnime();\r
- return;\r
- };\r
-\r
- now = (now - startTime) / step.time - 1;\r
- easeOut = m.sqrt(1 - now * now);\r
- newX = (step.x - startX) * easeOut + startX;\r
- newY = (step.y - startY) * easeOut + startY;\r
- this._updateScrollPosition(newX, newY);\r
- if( this.animating ) this.animeTimerID = X.Timer.requestFrame( this, this._doAnimate, [ startTime, step, startX, startY ] );\r
- },\r
-*/\r
-\r
- _momentum: function (dist, time, maxDistUpper, maxDistLower, size) {\r
- var deceleration = 0.0006,\r
- speed = ABS(dist) / time,\r
- newDist = (speed * speed) / (2 * deceleration),\r
- newTime = 0, outsideDist = 0;\r
-\r
- // Proportinally reduce speed if we are outside of the boundaries\r
- if (dist > 0 && newDist > maxDistUpper) {\r
- outsideDist = size / (6 / (newDist / speed * deceleration));\r
- maxDistUpper = maxDistUpper + outsideDist;\r
- speed = speed * maxDistUpper / newDist;\r
- newDist = maxDistUpper;\r
- } else if (dist < 0 && newDist > maxDistLower) {\r
- outsideDist = size / (6 / (newDist / speed * deceleration));\r
- maxDistLower = maxDistLower + outsideDist;\r
- speed = speed * maxDistLower / newDist;\r
- newDist = maxDistLower;\r
- }\r
-\r
- newDist = newDist * (dist < 0 ? -1 : 1);\r
- newTime = speed / deceleration;\r
-\r
- return { dist: newDist, time: m.round(newTime) };\r
- },\r
-\r
- _offset: function (el) {\r
- var left = -el.offsetLeft,\r
- top = -el.offsetTop;\r
- \r
- while (el = el.offsetParent) {\r
- left -= el.offsetLeft;\r
- top -= el.offsetTop;\r
- }\r
- \r
- if (el != this.wrapper) {\r
- left *= this.scale;\r
- top *= this.scale;\r
- }\r
-\r
- return { left: left, top: top };\r
- },\r
-\r
- /*\r
- _bind: function (type, el, bubble) {\r
- X.Dom.Event.add( el || this.scroller, type, this );\r
- },\r
-\r
- _unbind: function (type, el, bubble) {\r
- X.Dom.Event.remove( el || this.scroller, type, this );\r
- },\r
- */\r
-\r
- /**\r
- *\r
- * Public methods\r
- *\r
- */\r
- destroy: function () {\r
- this.scroller.style[transform] = '';\r
-\r
- // Remove the scrollbars\r
- this.hScrollbar && this.hScrollbar.destroy();\r
- this.vScrollbar && this.vScrollbar.destroy();\r
-\r
- // Remove the event listeners\r
- X.Dom.Event.add( window, RESIZE_EV, this );\r
- this.uinodeTarget.unlisten( X.UI.Event.DRAG_START, this );\r
- this.uinodeRoot.unlisten( X.UI.Event.DRAG, this );\r
- this.uinodeRoot.unlisten( X.UI.Event.DRAG_END, this );\r
- \r
- //this._unbind(RESIZE_EV, window);\r
- //this._unbind(START_EV);\r
- //this._unbind(MOVE_EV, window);\r
- //this._unbind(END_EV, window);\r
- //this._unbind(CANCEL_EV, window);\r
- \r
- if (!this.options.hasTouch) {\r
- //this._unbind('wheel');\r
- }\r
- \r
- // if (this.options.useTransition) this._unbind(TRNEND_EV);\r
- this.options.useTransition && X.Dom.Event.remove( this.scroller, TRNEND_EV, this );\r
- \r
- //if (this.options.checkDOMChanges) clearInterval(this.checkDOMTime);\r
- \r
- //if (this.options.onDestroy) this.options.onDestroy.call(this);\r
- },\r
-\r
- refresh: function () {\r
- var offset,\r
- i, l,\r
- els,\r
- pos = 0,\r
- page = 0;\r
-\r
- if (this.scale < this.options.zoomMin) this.scale = this.options.zoomMin;\r
- this.wrapperW = this.wrapper.clientWidth || 1;\r
- this.wrapperH = this.wrapper.clientHeight || 1;\r
-\r
- this.minScrollY = -this.options.topOffset || 0;\r
- this.scrollerW = m.round(this.scroller.offsetWidth * this.scale);\r
- this.scrollerH = m.round((this.scroller.offsetHeight + this.minScrollY) * this.scale);\r
- this.maxScrollX = this.wrapperW - this.scrollerW;\r
- this.maxScrollY = this.wrapperH - this.scrollerH + this.minScrollY;\r
- this.dirX = 0;\r
- this.dirY = 0;\r
-\r
- // if (this.options.onRefresh) this.options.onRefresh.call(this);\r
- this._trigger( X.UI.Event.SCROLL_REFRESH, {} );\r
-\r
- this.hScrollbar && ( this.hScrollbar.active = this.maxScrollX < 0 );\r
- this.vScrollbar && ( this.vScrollbar.active = !this.options.bounceLock && !this.hScroll || this.scrollerH > this.wrapperH );\r
- \r
- offset = this._offset(this.wrapper);\r
- this.wrapperOffsetLeft = -offset.left;\r
- this.wrapperOffsetTop = -offset.top;\r
-\r
- // Prepare snap\r
- if (typeof this.options.snap == 'string') {\r
- this.pagesX = [];\r
- this.pagesY = [];\r
- els = this.scroller.querySelectorAll(this.options.snap);\r
- for (i=0, l=els.length; i<l; i++) {\r
- pos = this._offset(els[i]);\r
- pos.left += this.wrapperOffsetLeft;\r
- pos.top += this.wrapperOffsetTop;\r
- this.pagesX[i] = pos.left < this.maxScrollX ? this.maxScrollX : pos.left * this.scale;\r
- this.pagesY[i] = pos.top < this.maxScrollY ? this.maxScrollY : pos.top * this.scale;\r
- }\r
- } else if (this.options.snap) {\r
- this.pagesX = [];\r
- while (pos >= this.maxScrollX) {\r
- this.pagesX[page] = pos;\r
- pos = pos - this.wrapperW;\r
- page++;\r
- }\r
- if (this.maxScrollX%this.wrapperW) this.pagesX[this.pagesX.length] = this.maxScrollX - this.pagesX[this.pagesX.length-1] + this.pagesX[this.pagesX.length-1];\r
-\r
- pos = 0;\r
- page = 0;\r
- this.pagesY = [];\r
- while (pos >= this.maxScrollY) {\r
- this.pagesY[page] = pos;\r
- pos = pos - this.wrapperH;\r
- page++;\r
- }\r
- if (this.maxScrollY%this.wrapperH) this.pagesY[this.pagesY.length] = this.maxScrollY - this.pagesY[this.pagesY.length-1] + this.pagesY[this.pagesY.length-1];\r
- }\r
-\r
- // Prepare the scrollbars\r
- this._scrollbar('h');\r
- this._scrollbar('v');\r
-\r
- if (!this.zoomed) {\r
- this.scroller.style[transitionDuration] = '0';\r
- this._resetPos(400);\r
- }\r
- },\r
-\r
- scrollTo: function (x, y, time, relative) {\r
- var step = x,\r
- i, l;\r
-\r
- this.stop();\r
-\r
- if( !step.length ) step = [{ x: x, y: y, time: time, relative: relative }];\r
- \r
- for( i = 0, l = step.length; i < l; ++i ){\r
- if( step[ i ].relative ){\r
- step[ i ].x = this.x - step[ i ].x;\r
- step[ i ].y = this.y - step[ i ].y;\r
- };\r
- this.steps.push( {\r
- x : step[i].x,\r
- y : step[i].y,\r
- time : step[i].time || 0\r
- });\r
- };\r
-\r
- this._startAnime();\r
- },\r
-\r
- disable: function () {\r
- this.stop();\r
- this._resetPos(0);\r
- this.enabled = false;\r
-\r
- // If disabled after touchstart we make sure that there are no left over events\r
- //this._unbind(MOVE_EV, window);\r
- //this._unbind(END_EV, window);\r
- //this._unbind(CANCEL_EV, window);\r
- this.uinodeRoot.unlisten( X.UI.Event.DRAG, this );\r
- this.uinodeRoot.unlisten( X.UI.Event.DRAG_END, this );\r
- },\r
- \r
- enable: function () {\r
- this.enabled = true;\r
- },\r
- \r
- stop: function () {\r
- //if (this.options.useTransition) this._unbind(TRNEND_EV);\r
- //else X.Timer.cancelFrame( this.animeTimerID );\r
- /*\r
- if (this.options.useTransition){\r
- X.Dom.Event.remove( this.scroller, TRNEND_EV, this );\r
- } else {\r
- X.Timer.cancelFrame(this.animeTimerID);\r
- };\r
- */\r
- this.xnodeScroller.stop();\r
- if( this.steps ) this.steps.length = 0;\r
- this.moved = false;\r
- this.animating = false;\r
- },\r
- \r
- isReady: function () {\r
- return !this.moved && !this.zoomed && !this.animating;\r
- }\r
-} );\r
-\r
\r
\r
X.UI._ScrollBox = X.UI._ChromeBox.inherits(\r
//elmScroller : null,\r
//elmScrollbar : null,\r
\r
+ supportAttrs : X.UI.Attr.createAttrDef( X.UI.Attr.Support, X_UI_ScrollBox_SUPPORT_ATTRS ),\r
+ \r
scrolling : false,\r
- _scrollX : 0,\r
- _scrollY : 0,\r
- scrollXPercent : 0,\r
- scrollYPercent : 0,\r
+ \r
+ initiated : '',\r
+ moved : false,\r
+ distX : 0,\r
+ distY : 0,\r
+ directionX : 0,\r
+ directionY : 0,\r
+ directionLocked : '',\r
+ startTime : 0,\r
+ endTime : 0,\r
+ isAnimating : false,\r
+ startX : 0,\r
+ startY : 0,\r
+ absStartX : 0,\r
+ absStartY : 0,\r
+ pointX : 0,\r
+ pointY : 0,\r
+ maxScrollX : 0,\r
+ maxScrollY : 0,\r
+ hasHScroll : false,\r
+ hasVScroll : false,\r
+\r
+ wrapperOffset : 0,\r
+ wheelTimeout : 0,\r
+ requestFrameID : 0,\r
+ \r
+ _scrollX : 0,\r
+ _scrollY : 0,\r
+ scrollXPercent : 0,\r
+ scrollYPercent : 0,\r
+ \r
+ lastScrollWidth : 0,\r
+ lastScrollHeight : 0,\r
+ lastBoxWidth : 0,\r
+ lastBoxHeight : 0,\r
\r
_containerNode : null,\r
scrollManager : null,\r
\r
Constructor : function( layout, args ){\r
this.Super( layout, args );\r
- this._containerNode = _X_Class_getPrivate( this.containerNode );\r
+ this._containerNode = X_Class_getPrivate( this.containerNode );\r
},\r
\r
creationComplete : function(){\r
- X.UI._AbstractUINode.prototype.creationComplete.call( this, arguments );\r
- this.scrollManager = new iScroll( this.root, this.User, this.rawElement, this._containerNode.rawElement );\r
+ X.UI._Box.prototype.creationComplete.apply( this, arguments );\r
+ this.scrollManager;\r
this._check();\r
},\r
\r
calculate : function(){\r
- X.UI._AbstractUINode.prototype.calculate.call( this, arguments );\r
- this._check();\r
+ this.lastScrollWidth = this.scrollWidth;\r
+ this.lastScrollHeight = this.scrollHeight;\r
+ this.lastBoxWidth = this.boxWidth;\r
+ this.lastBoxHeight = this.boxHeight;\r
+ \r
+ X.UI._Box.prototype.calculate.apply( this, arguments );\r
+ \r
+ if(\r
+ this.lastScrollWidth !== this.scrollWidth || this.lastScrollHeight !== this.scrollHeight ||\r
+ this.lastBoxWidth !== this.boxWidth || this.lastBoxHeight !== this.boxHeight\r
+ ){\r
+ // scroll の停止、GPU の解除\r
+ this._check();\r
+ };\r
+ \r
+ },\r
+ \r
+ scrollBy : function( x, y, opt_time, opt_easing ){\r
+ this.scrollTo( this.x + x, this.y + y, opt_time, opt_easing );\r
+ },\r
+ \r
+ scrollTo : function( x, y, opt_time, opt_easing ){\r
+ opt_time = 0 <= opt_time ? opt_time : 0;\r
+ opt_easing = opt_easing || 'circular';\r
+ \r
+ this.isInTransition = !!opt_time;\r
+ \r
+ this.containerNode.animate(\r
+ {\r
+ x : this.x,\r
+ y : this.y\r
+ },\r
+ {\r
+ x : x,\r
+ y : y\r
+ },\r
+ opt_time, opt_easing, 1000\r
+ );\r
+ \r
+ this.x = x;\r
+ this.y = y;\r
+ \r
+ if( this.indicators ){\r
+ for( i = this.indicators.length; i--; ){\r
+ this.indicators[ i ].updatePosition( opt_time, opt_easing );\r
+ };\r
+ };\r
},\r
\r
_check : function(){\r
- if( this.w < this._containerNode.w || this.h < this._containerNode.h ){\r
+ var needVScroll, needHScroll;\r
+ if( this.boxWidth < this._containerNode.scrollWidth || this.boxHeight < this._containerNode.scrollHeight ){\r
// scroll\r
if( this.scrolling ){\r
// fix scroll position from scrollXPercent, scrollYPercent\r
+ //\r
\r
} else {\r
// create scroller\r
- this.listen( X.UI.Event.POINTER_START, this );\r
+ \r
+ \r
+ this.listen( X.UI.Event.POINTER_START, X_UI_ScrollBox_onStart );\r
\r
\r
this._move( 0, 0 );\r
// no scroll\r
if( this.scrolling ){\r
// remove scroller\r
+ this.unlisten( X.UI.Event.POINTER_START );\r
\r
( this._scrollX !== 0 || this._scrollY !== 0 ) && this._move( 0, 0 );\r
\r
};\r
},\r
\r
- handleEvent : function( e ){\r
- switch( e.type ){\r
- case X.UI.Event.POINTER_START :\r
- this.listen( X.UI.Event.POINTER_MOVE, this );\r
- this.listen( X.UI.Event.POINTER_END, this );\r
- \r
- break;\r
- case X.UI.Event.POINTER_MOVE :\r
- \r
- break;\r
- case X.UI.Event.POINTER_END :\r
- this.unlisten( X.UI.Event.POINTER_MOVE, this );\r
- this.unlisten( X.UI.Event.POINTER_END, this );\r
- \r
- break;\r
- };\r
- },\r
- \r
_move : function( x, y ){\r
\r
},\r
\r
_remove : function(){\r
- X.UI._AbstractUINode.prototype._remove.call( this, arguments );\r
+ X.UI._AbstractUINode.prototype._remove.apply( this, arguments );\r
if( this.scrolling ){\r
// remove scroll\r
};\r
}\r
);\r
\r
-})();\r
+\r
+function X_UI_ScrollBox_onStart( e ){\r
+ var ret = X.Callback.NONE;\r
+ \r
+ // React to left mouse button only\r
+ if( e.pointerType === 'mouse' && e.button !== 0 ){\r
+ return ret;\r
+ };\r
+\r
+ if( !this.enabled || ( this.initiated && e.pointerType !== this.initiated ) ){\r
+ return ret;\r
+ };\r
+\r
+ this.initiated = e.pointerType;\r
+ this.moved = false;\r
+ this.distX = 0;\r
+ this.distY = 0;\r
+ this.directionX = 0;\r
+ this.directionY = 0;\r
+ this.directionLocked = '';\r
+ this.startTime = X_Timer_now();\r
+\r
+ // スクロール中の停止\r
+ if( this.isAnimating ){\r
+ delete this.isAnimating;\r
+ this.dispatch( X.UI.Event.SCROLL_END );\r
+ }; \r
+\r
+ this.startX = this.x;\r
+ this.startY = this.y;\r
+ this.absStartX = this.x;\r
+ this.absStartY = this.y;\r
+ this.pointX = e.pageX;\r
+ this.pointY = e.pageY;\r
+ \r
+ this.listen( X.UI.Event.POINTER_MOVE, X_UI_ScrollBox_onMove );\r
+ this.listen( X.UI.Event.POINTER_END , X_UI_ScrollBox_onEnd );\r
+\r
+ //console.log( 'start : 3' );\r
+ return ret | X.Callback.PREVENT_DEFAULT;\r
+};\r
+\r
+function X_UI_ScrollBox_onMove( e ){\r
+ var ret = X.Callback.NONE,\r
+ deltaX, deltaY, timestamp,\r
+ newX, newY,\r
+ absDistX, absDistY;\r
+ // 規定以上の move でスクロール開始\r
+ \r
+ if( !this.enabled || e.pointerType !== this.initiated ){\r
+ return ret;\r
+ };\r
+\r
+ // gpu の用意\r
+ if( !this.containerNode._anime ){\r
+ console.log( 'gpuレイヤーの用意' );\r
+ this._translate( this.x, this.y );\r
+ return ret;\r
+ };\r
+\r
+ deltaX = e.pageX - this.pointX;\r
+ deltaY = e.pageY - this.pointY;\r
+ timestamp = X_Timer_now();\r
+\r
+ this.pointX = e.pageX;\r
+ this.pointY = e.pageY;\r
+\r
+ this.distX += deltaX;\r
+ this.distY += deltaY;\r
+ absDistX = Math.abs(this.distX);\r
+ absDistY = Math.abs(this.distY);\r
+ \r
+ // We need to move at least 10 pixels for the scrolling to initiate\r
+ if( 300 < timestamp - this.endTime && ( absDistX < 10 && absDistY < 10 ) ){\r
+ return ret;\r
+ };\r
+\r
+ // If you are scrolling in one direction lock the other\r
+ if( !this.directionLocked ){\r
+ if( absDistX > absDistY + this.directionLockThreshold ){\r
+ this.directionLocked = 'h'; // lock horizontally\r
+ } else\r
+ if( absDistY >= absDistX + this.directionLockThreshold ){\r
+ this.directionLocked = 'v'; // lock vertically\r
+ } else {\r
+ this.directionLocked = 'n'; // no lock\r
+ };\r
+ };\r
+\r
+ if( this.directionLocked === 'h' ){\r
+ deltaY = 0;\r
+ } else\r
+ if( this.directionLocked === 'v' ){\r
+ deltaX = 0;\r
+ };\r
+\r
+ deltaX = this.hasHScroll ? deltaX : 0;\r
+ deltaY = this.hasVScroll ? deltaY : 0;\r
+\r
+ if( !this.moved ){\r
+ this.dispatch( X.UI.Event.SCROLL_BEFORE_MOVE );\r
+ this.moved = true;\r
+ this.minusX = deltaX;\r
+ this.minusY = deltaY;\r
+ } else {\r
+ this.dispatch( X.UI.Event.SCROLL_MOVE );\r
+ };\r
+\r
+ newX = this.x + deltaX;// - this.minusX;\r
+ newY = this.y + deltaY;// - this.minusY;\r
+\r
+ // Slow down if outside of the boundaries\r
+ if( 0 < newX || newX < this.maxScrollX ){\r
+ newX = this.bounce ? this.x + ( deltaX ) / 3 : 0 < newX ? 0 : this.maxScrollX;\r
+ };\r
+ if( 0 < newY || newY < this.maxScrollY ){\r
+ newY = this.bounce ? this.y + ( deltaY ) / 3 : 0 < newY ? 0 : this.maxScrollY;\r
+ };\r
+\r
+ this.directionX = 0 < deltaX ? -1 : deltaX < 0 ? 1 : 0;\r
+ this.directionY = 0 < deltaY ? -1 : deltaY < 0 ? 1 : 0;\r
+\r
+ this._translate( newX, newY );\r
+\r
+ if( 300 < timestamp - this.startTime ){\r
+ this.startTime = timestamp;\r
+ this.startX = this.x;\r
+ this.startY = this.y;\r
+ };\r
+ // イベントの拘束\r
+ return ret | X.Callback.PREVENT_DEFAULT | X.Callback.MONOPOLY;\r
+};\r
+\r
+function X_UI_ScrollBox_onEnd( e ){\r
+ var ret = X.Callback.NONE,\r
+ time = 0,\r
+ easing = '',\r
+ newX, newY,\r
+ momentumX, momentumY,\r
+ duration, distanceX, distanceY;\r
+ \r
+ this.unlisten( X.UI.Event.POINTER_MOVE, X_UI_ScrollBox_onMove );\r
+ this.unlisten( X.UI.Event.POINTER_END, X_UI_ScrollBox_onEnd );\r
+ \r
+ if( !this.enabled || e.pointerType !== this.initiated ){\r
+ return ret;\r
+ };\r
+\r
+ delete this.isInTransition;\r
+ delete this.initiated;\r
+ this.endTime = X_Timer_now(); \r
+\r
+ duration = this.endTime - this.startTime;\r
+ newX = Math.round( this.x );\r
+ newY = Math.round( this.y );\r
+ distanceX = Math.abs(newX - this.startX);\r
+ distanceY = Math.abs(newY - this.startY);\r
+\r
+ // reset if we are outside of the boundaries\r
+ if( X_UI_ScrollBox_resetPosition( this, this.options.bounceTime ) ){\r
+ return ret;\r
+ };\r
+\r
+ // we scrolled less than 10 pixels\r
+ if( !this.moved ){\r
+ // this.dispatch( X_Event.CANCELED );\r
+ return ret;\r
+ };\r
+ \r
+ this.scrollTo( newX, newY, 0 ); // ensures that the last position is rounded\r
+\r
+ // start momentum animation if needed\r
+ if( this.options.momentum && duration < 300 ){\r
+ momentumX = this.hasHScroll ?\r
+ X_UI_ScrollBox_momentum( this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options.deceleration ) :{ destination: newX, duration: 0 };\r
+ momentumY = this.hasVScroll ?\r
+ X_UI_ScrollBox_momentum( this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options.deceleration ) : { destination: newY, duration: 0 };\r
+ newX = momentumX.destination;\r
+ newY = momentumY.destination;\r
+ time = Math.max( momentumX.duration, momentumY.duration );\r
+ this.isInTransition = 1;\r
+ };\r
+\r
+ if( newX != this.x || newY != this.y ){\r
+ // change easing function when scroller goes out of the boundaries\r
+ if( 0 < newX || newX < this.maxScrollX || 0 < newY || newY < this.maxScrollY ){\r
+ easing = 'quadratic';\r
+ };\r
+\r
+ this.scrollTo( newX, newY, time, easing );\r
+ return ret;\r
+ };\r
+\r
+ this.dispatch( X.UI.Event.SCROLL_END );\r
+ \r
+ return ret;\r
+};\r
+\r
+function X_UI_ScrollBox_resetPosition( that, time ){\r
+ var x = this.x,\r
+ y = this.y;\r
+\r
+ time = time || 0;\r
+\r
+ if( !this.hasHScroll || 0 < this.x ){\r
+ x = 0;\r
+ } else\r
+ if( this.x < this.maxScrollX ){\r
+ x = this.maxScrollX;\r
+ };\r
+\r
+ if( !this.hasVScroll || 0 < this.y ){\r
+ y = 0;\r
+ } else\r
+ if( this.y < this.maxScrollY ){\r
+ y = this.maxScrollY;\r
+ };\r
+\r
+ if( x === this.x && y === this.y ){\r
+ console.log( 'no バウンド' );\r
+ return false;\r
+ };\r
+\r
+ console.log( 'バウンド!' );\r
+ this.scrollTo( x, y, time, this.options.bounceEasing );\r
+\r
+ return true;\r
+};\r
+\r
+function X_UI_ScrollBox_translate( x, y ){\r
+ this.containerNode.animate(\r
+ {\r
+ x : this.x,\r
+ y : this.y\r
+ },\r
+ {\r
+ x : x,\r
+ y : y\r
+ },\r
+ 0, '', 300\r
+ );\r
+ \r
+ this.x = x;\r
+ this.y = y;\r
+ \r
+ if( this.indicators ){\r
+ for( i = this.indicators.length; i--; ){\r
+ this.indicators[ i ].updatePosition();\r
+ };\r
+ };\r
+};\r
+\r
+function X_UI_ScrollBox_refresh( remove ){\r
+ this.maxScrollX = this.boxWidth - this.containerNode.boxWidth;\r
+ this.maxScrollY = this.boxHeight - this.containerNode.boxHeight;\r
+\r
+ this.hasHScroll = this.User.attr( 'scrollX' ) && this.maxScrollX < 0;\r
+ this.hasVScroll = this.User.attr( 'scrollY' ) && this.maxScrollY < 0;\r
+\r
+ if( !this.hasHScroll ){\r
+ this.maxScrollX = 0;\r
+ this.scrollerWidth = this.wrapperWidth;\r
+ };\r
+\r
+ if( !this.hasVScroll ){\r
+ this.maxScrollY = 0;\r
+ this.scrollerHeight = this.wrapperHeight;\r
+ };\r
+\r
+ delete this.endTime;\r
+ delete this.directionX;\r
+ delete this.directionY;\r
+\r
+ this.wrapperOffset = this.xnodeWrapper.offset();\r
+\r
+ //this.dispatch('refresh');\r
+\r
+ X_UI_ScrollBox_resetPosition( this, 0 );\r
+};\r
\r
X.UI.ScrollBox = X.UI.ChromeBox.inherits(\r
'ScrollBox',\r