OSDN Git Service

Fix the bug of X.NodeAnime.
[pettanr/clientJs.git] / 0.6.x / js / 20_ui / 15_ScrollBox.js
index b108e48..e82a647 100644 (file)
+/*\r
+ * scroll 要素は常にひとつ\r
+ * ScrollManager\r
+ * indicatorX, Y は再利用\r
+ */\r
+var XUI_ScrollBox_useCSSP = !X_UA[ 'IE5' ],\r
+       XUI_ScrollBox_current,\r
+       XUI_ScrollBox_indicatorV,\r
+       XUI_ScrollBox_indicatorH;\r
+\r
+function XUI_ScrollBox_start( scrollBox ){\r
+       // 既存スクロールの停止\r
+       if( XUI_ScrollBox_current && XUI_ScrollBox_current !== scrollBox ){\r
+               XUI_ScrollBox_indicatorV &&\r
+               XUI_ScrollBox_current[ 'unlisten' ]( [ X_EVENT_CANCELED, XUI_Event.SCROLL_END ], XUI_ScrollBox_indicatorV, XUI_ScrollBox_indicatorHandleEvent );\r
+               \r
+               XUI_ScrollBox_indicatorH &&\r
+               XUI_ScrollBox_current[ 'unlisten' ]( [ X_EVENT_CANCELED, XUI_Event.SCROLL_END ], XUI_ScrollBox_indicatorH, XUI_ScrollBox_indicatorHandleEvent );\r
+       };\r
 \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
+       if( scrollBox && scrollBox.hasVScroll ){\r
+               if( !XUI_ScrollBox_indicatorV ){\r
+                       XUI_ScrollBox_indicatorV = X_Doc_create( 'div' )[ 'className' ]( 'ScrollBox-IndicatorV' );\r
+               };\r
+               if( scrollBox.xnode !== XUI_ScrollBox_indicatorV.parent ){\r
+                       console.log( '*** Scroll Indicator add ***' );\r
+                       scrollBox.xnode[ 'append' ]( XUI_ScrollBox_indicatorV );\r
+                       XUI_ScrollBox_indicatorV[ 'animate' ]({\r
+                                       'from'        : { opacity : 0 },\r
+                                       'to'          : { opacity : 0.5 },\r
+                                       'duration'    : 900,\r
+                                       'easing'      : 'circular',\r
+                                       'lazyRelease' : 300\r
+                               });\r
+                       scrollBox\r
+                               [ 'listen' ]( [ X_EVENT_CANCELED, XUI_Event.SCROLL_END ], XUI_ScrollBox_indicatorV, XUI_ScrollBox_indicatorHandleEvent );\r
+               };\r
+       } else\r
+       if( XUI_ScrollBox_indicatorV ){\r
+               console.log( '*** Scroll Indicator remove ***' );\r
+               XUI_ScrollBox_indicatorV[ 'remove' ]();\r
+       };\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
+       if( scrollBox && scrollBox.hasHScroll ){\r
+               if( !XUI_ScrollBox_indicatorH ){\r
+                       XUI_ScrollBox_indicatorH = X_Doc_create( 'div' )[ 'className' ]( 'ScrollBox-IndicatorH' );\r
                };\r
+               if( scrollBox.xnode !== XUI_ScrollBox_indicatorH.parent ){                      \r
+                       scrollBox.xnode[ 'append' ]( XUI_ScrollBox_indicatorH );\r
+                       XUI_ScrollBox_indicatorH[ 'animate' ]({\r
+                                       'from'        : { opacity : 0 },\r
+                                       'to'          : { opacity : 0.5 },\r
+                                       'duration'    : 900,\r
+                                       'easing'      : 'circular',\r
+                                       'lazyRelease' : 300\r
+                               });\r
+                       scrollBox\r
+                               [ 'listen' ]( [ X_EVENT_CANCELED, XUI_Event.SCROLL_END ], XUI_ScrollBox_indicatorH, XUI_ScrollBox_indicatorHandleEvent );                       \r
+               };\r
+       } else\r
+       if( XUI_ScrollBox_indicatorH ){\r
+               XUI_ScrollBox_indicatorH[ 'remove' ]();\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
+       XUI_ScrollBox_current = scrollBox;\r
+};\r
+\r
+function XUI_ScrollBox_indicatorHandleEvent( e ){\r
+       //if( !XUI_ScrollBox_useCSSP ) return;\r
+       switch( e.type ){\r
+               case X_EVENT_CANCELED :\r
+               case XUI_Event.SCROLL_END :\r
+                       console.log( '-fadeout-' );\r
+                       this[ 'animate' ]({\r
+                                       'from'        : { opacity : 0.5 },\r
+                                       'to'          : { opacity : 0 },\r
+                                       'duration'    : 900,\r
+                                       'easing'      : 'circular',\r
+                                       'lazyRelease' : 300\r
+                               });\r
+                       break;\r
+       };\r
+};\r
+\r
+\r
+var X_UI_ScrollBox_SUPPORT_ATTRS = {\r
+               // スクロール開始するために必要な移動距離、縦か横、どちらか制限する場合、より重要\r
+               directionLockThreshold : [     10, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.LENGTH ],\r
+               scrollXEnabled         : [   true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ],\r
+               scrollYEnabled         : [   true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ],\r
+               scrollEnabled          : [   true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ],\r
+               bounceEnabled          : [   true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ],\r
+               bounceTime             : [    300, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.TIME ],\r
+               useWheel               : [   true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ],\r
+               useKey                 : [   true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ],\r
+               hasScrollShadow        : [   true, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.BOOLEAN ],\r
+               scrollShadowColor      : [ '#000', XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.COLOR ]\r
+       };\r
+\r
+var XUI_ScrollBox = XUI_ChromeBox.inherits(\r
+       '_ScrollBox',\r
+       X_Class.NONE,\r
+       {\r
+               layout                 : XUI_Layout_Canvas,\r
+               \r
+               directionLockThreshold : 10,\r
+               scrollXEnabled         : true,\r
+               scrollYEnabled         : true,\r
+               scrollEnabled          : true,\r
+               bounceEnabled          : true,\r
+               momentumEnabled        : true,\r
+               bounceTime             : 600,\r
+               useWheel               : true,\r
+               useKey                 : true,\r
+               hasScrollShadow        : true,\r
+               scrollShadowColor      : '#000',\r
+               \r
+               scrolling       : false,\r
+               \r
+               initiated       : '',\r
+               moved               : false,\r
+               directionLocked : '',\r
+               startTime       : 0,\r
+               endTime         : 0,\r
+               isInTransition  : false,\r
+\r
+               hasHScroll      : false,\r
+               hasVScroll      : false,\r
+\r
+               wrapperOffset   : 0,\r
+               wheelTimeout    : 0,\r
+               requestFrameID  : 0,\r
+               \r
+               fontSize        : 0,\r
+               \r
+               scrollX         : 0, // px\r
+               scrollY         : 0, // px\r
+               scrollXMax      : 0, // px\r
+               scrollYMax      : 0, // px\r
+               scrollXRatio    : 0, // この値は scroll 不要になっても保持される。 scroll 必要時に参照される\r
+               scrollYRatio    : 0,            \r
+               startX          : 0, // px\r
+               startY          : 0, // px\r
+               absStartX       : 0, // px\r
+               absStartY       : 0, // px\r
+               pointX          : 0, // px\r
+               pointY          : 0, // px\r
+               distX               : 0, // px\r
+               distY               : 0, // px\r
+               directionX      : 0, // -1, 0, 1\r
+               directionY      : 0, // -1, 0, 1\r
+               \r
+               lastScrollWidth  : 0,\r
+               lastScrollHeight : 0,\r
+               lastBoxWidth     : 0,\r
+               lastBoxHeight    : 0,\r
+               \r
+               _containerNode   : null,\r
+               xnodeSlider      : null,\r
+               \r
+               Constructor : function( user, layout, args ){\r
+                       this[ 'Super' ]( user, layout, args );\r
+                       this._containerNode = X_Pair_get( this.containerNode );\r
+                       this.xnodeSlider = this._containerNode.xnode[ 'className' ]( 'ScrollSlider' )[ 'listen' ]( X_EVENT_ANIME_END, this, X_UI_ScrollBox_onAnimeEnd );\r
+                       this.xnode[ 'className' ]( 'ScrollBox' );\r
+               },\r
+               \r
+               creationComplete : function(){\r
+                       XUI_Box.prototype.creationComplete.apply( this, arguments );\r
+               },\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
+               calculate : function(){\r
+                       this.lastScrollWidth  = this._containerNode.boxWidth;\r
+                       this.lastScrollHeight = this._containerNode.boxHeight;\r
+                       this.lastBoxWidth     = this.boxWidth;\r
+                       this.lastBoxHeight    = this.boxHeight;\r
+                       \r
+                       XUI_Box.prototype.calculate.apply( this, arguments );\r
+                       \r
+                       // TODO root の layout_complete 後に。\r
+                       // TODO calculate 前に scroll の解放。\r
+                       \r
+                       if(\r
+                                       this.lastScrollWidth  !== this._containerNode.boxWidth ||\r
+                                       this.lastScrollHeight !== this._containerNode.boxHeight ||\r
+                                       this.lastBoxWidth    !== this.boxWidth    || this.lastBoxHeight    !== this.boxHeight\r
+                               ){\r
+                                       console.log( 'scroll - calc' );\r
+                                       XUI_rootData[ 'listenOnce' ]( XUI_Event.LAYOUT_COMPLETE, this, X_UI_ScrollBox_onLayoutComplete );\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
        \r
-                               this.options.fadeScrollbar &&\r
-                                       this.xnodeWrapper.css(\r
-                                               {\r
-                                                       opacity            : 0,\r
-                                                       transitionProperty : 'opacity',\r
-                                                       transitionDuration : '350ms'\r
-                                               }\r
-                                       );\r
+               scrollTo : function( x, y, opt_time, opt_easing, opt_release ){\r
+                       //if( this.scrollX === x && this.scrollY === y ) return;\r
+                       \r
+                       opt_time    = 0 <= opt_time ? opt_time : 0;\r
+                       opt_easing  = opt_easing || 'circular';\r
+                       opt_release = 0 <= opt_release ? opt_release : 300;\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
+                       this.isInTransition = 0 < opt_time;\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
+                       X_UI_ScrollBox_translate( this, x, y, opt_time, opt_easing, opt_release );\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
+               _remove : function(){\r
+                       XUI_AbstractUINode.prototype._remove.apply( this, arguments );\r
+                       \r
+                       if( this.scrolling ){\r
+                               // scroller 削除\r
+                               this[ 'unlisten' ]( XUI_Event._POINTER_DOWN, X_UI_ScrollBox_onStart )\r
+                                       [ 'unlisten' ]( XUI_Event.DRAG, X_UI_ScrollBox_onMove )\r
+                                       [ 'unlisten' ]( XUI_Event.DRAG_END, X_UI_ScrollBox_onEnd );\r
+                               XUI_rootData[ 'unlisten' ]( XUI_Event.LAYOUT_BEFORE, this, X_UI_ScrollBox_onLayoutBefore );\r
+                               \r
+                               XUI_rootData[ 'unlisten' ]( XUI_Event.LAYOUT_COMPLETE, this, X_UI_ScrollBox_onLayoutComplete );\r
+                               this[ 'unlisten' ]( XUI_Event.SCROLL_END, XUI_rootData, XUI_rootData.calculate );\r
+                               \r
+                               XUI_ScrollBox_useCSSP ? this.xnodeSlider[ 'stop' ]() : this.xnode[ 'stop' ]();\r
+                               \r
+                               XUI_ScrollBox_current === this && XUI_ScrollBox_start( null );\r
+                               \r
+                               this.scrolling = false;\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
+);\r
 \r
-               // User defined options\r
-               if( options ) for (i in options) 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
+function X_UI_ScrollBox_onLayoutBefore( e ){\r
+       if( e[ 'cancelable' ] && this.isInTransition && X_NodeAnime_translateZ ){\r
+               this[ 'listenOnce' ]( XUI_Event.SCROLL_END, XUI_rootData, XUI_rootData.calculate );\r
+               return X_CALLBACK_PREVENT_DEFAULT;\r
+       };\r
+       this.scrollXRatio = this.scrollX / this.scrollXMax;\r
+       this.scrollYRatio = this.scrollY / this.scrollYMax;\r
+       XUI_ScrollBox_useCSSP ? this.xnodeSlider[ 'stop' ]() : this.xnode[ 'stop' ]();\r
+       this.isInTransition = false;\r
+       return X_CALLBACK_NONE;\r
+};\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
+function X_UI_ScrollBox_onLayoutComplete( e ){\r
+       // scroll の停止、GPU の解除\r
+       var font = this.fontSize = this.xnodeSlider[ 'call' ]( 'fontSize' );\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
+       this.scrollXMax = ( this.boxWidth  - this._containerNode.boxWidth )  * font | 0;\r
+       this.scrollYMax = ( this.boxHeight - this._containerNode.boxHeight ) * font | 0;\r
 \r
-               this.refresh();\r
+       this.hasHScroll = this.scrollXEnabled && ( this.scrollXMax < -1 ); // < 0 だと \r
+       this.hasVScroll = this.scrollYEnabled && ( this.scrollYMax < -1 );\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
+       if( !this.hasHScroll ){\r
+               this.scrollXMax  = 0;\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
+       if( !this.hasVScroll ){\r
+               this.scrollYMax   = 0;\r
+       };\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
+       delete this.endTime;\r
+       delete this.directionX;\r
+       delete this.directionY;\r
 \r
-               if (!this.enabled) return;\r
+       X_UI_ScrollBox_resetPosition( this, 0 );\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
+       if( this.hasHScroll || this.hasVScroll ){\r
+               // scroll が必要。\r
+               if( this.scrolling ){\r
+                       X_UI_ScrollBox_translate( this, this.scrollXMax * this.scrollXRatio, this.scrollYMax * this.scrollYRatio, 100, '', 300 );\r
+               } else {\r
+                       // scroller 作る\r
+                       this[ 'listen' ]( XUI_Event._POINTER_DOWN, X_UI_ScrollBox_onStart )\r
+                               [ 'listen' ]( XUI_Event.DRAG, X_UI_ScrollBox_onMove )\r
+                               [ 'listen' ]( XUI_Event.DRAG_END, X_UI_ScrollBox_onEnd );\r
+                       XUI_rootData[ 'listen' ]( XUI_Event.LAYOUT_BEFORE, this, X_UI_ScrollBox_onLayoutBefore );\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
+                       X_UI_ScrollBox_translate( this, this.scrollXMax * this.scrollXRatio, this.scrollYMax * this.scrollYRatio, 100, '', 300 );\r
+                       this.scrolling = true;\r
                };\r
+       } else\r
+       // scroll 不要\r
+       if( this.scrolling ){\r
+               // scroller 削除\r
+               this[ 'unlisten' ]( XUI_Event._POINTER_DOWN, X_UI_ScrollBox_onStart )\r
+                       [ 'unlisten' ]( XUI_Event.DRAG, X_UI_ScrollBox_onMove )\r
+                       [ 'unlisten' ]( XUI_Event.DRAG_END, X_UI_ScrollBox_onEnd );\r
+               XUI_rootData[ 'unlisten' ]( XUI_Event.LAYOUT_BEFORE, this, X_UI_ScrollBox_onLayoutBefore );\r
+               \r
+               ( this.scrollX !== 0 || this.scrollY !== 0 ) && X_UI_ScrollBox_translate( this, 0, 0, 100, '', 300 );\r
+               \r
+               delete this.scrolling;\r
+               delete this.scrollXRatio;\r
+               delete this.scrollYRatio;\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
+// TODO use scrollLeft, scrollTop\r
+function X_UI_ScrollBox_translate( that, x, y, opt_time, opt_easing, opt_release ){\r
+       var scrollBoxH = that.fontSize * that.boxHeight,\r
+               scrollBoxW = that.fontSize * that.boxWidth,\r
+               indicatorH, indicatorW;\r
+       \r
+       opt_time    = 0 <= opt_time ? opt_time : 0;\r
+       opt_easing  = opt_easing === '' ? '' : opt_easing || 'circular';\r
+       opt_release = 0 <= opt_release ? opt_release : 300;\r
+       \r
+       console.log( 'scr ' + y );\r
+       \r
+       if( !XUI_ScrollBox_useCSSP ){\r
+               that.xnode[ 'animate' ]({\r
+                                               'from'        : {\r
+                                                               scrollX : -that.scrollX,\r
+                                                               scrollY : -that.scrollY\r
+                                                       },\r
+                                               'to'          : {\r
+                                                               scrollX : -x,\r
+                                                               scrollY : -y\r
+                                                       },\r
+                                               'duration'    : opt_time,\r
+                                               'easing'      : opt_easing\r
+                                       });     \r
+       } else {\r
+               that.xnodeSlider[ 'animate' ]({\r
+                                               'from'        : {\r
+                                                               x : that.scrollX,\r
+                                                               y : that.scrollY\r
+                                                       },\r
+                                               'to'          : {\r
+                                                               x : x,\r
+                                                               y : y\r
+                                                       },\r
+                                               'duration'    : opt_time,\r
+                                               'easing'      : opt_easing,\r
+                                               'lazyRelease' : opt_release\r
+                                       });             \r
+       };\r
 \r
-               this.startTime = e.timeStamp || X.Timer.now();\r
+       if( X_UA[ 'IE' ] < 6 ){\r
+               XUI_ScrollBox_indicatorV && XUI_ScrollBox_indicatorV[ 'css' ]( 'left', ( scrollBoxW - that.fontSize * 0.6 | 0 ) + 'px' );\r
+               XUI_ScrollBox_indicatorH && XUI_ScrollBox_indicatorH[ 'css' ]( 'top' , ( scrollBoxH - that.fontSize * 0.6 | 0 ) + 'px' );\r
+       };\r
 \r
-               this.uinodeRoot.listen( X.UI.Event.DRAG, this );\r
-               this.uinodeRoot.listen( X.UI.Event.DRAG_END, this );\r
+       if( that.hasVScroll && XUI_ScrollBox_indicatorV && XUI_ScrollBox_indicatorV.parent === that.xnode ){\r
+               indicatorH = scrollBoxH * scrollBoxH / ( - that.scrollYMax + scrollBoxH );\r
+               scrollBoxH -= indicatorH;\r
+\r
+               XUI_ScrollBox_indicatorV\r
+                       [ 'css' ]({\r
+                               height : ( indicatorH | 0 ) + 'px'\r
+                       })\r
+                       [ 'animate' ]({\r
+                                       'from'        : {\r
+                                                       y       : scrollBoxH * that.scrollY / that.scrollYMax },\r
+                                       'to'          : {\r
+                                                       y       : scrollBoxH * y / that.scrollYMax,\r
+                                                       opacity : 0.5\r
+                                               },\r
+                                       'duration'    : opt_time,\r
+                                       'easing'      : opt_easing,\r
+                                       'lazyRelease' : opt_release\r
+                               });\r
+       };\r
+       if( that.hasHScroll && XUI_ScrollBox_indicatorH && XUI_ScrollBox_indicatorH.parent === that.xnode ){\r
+               indicatorW = scrollBoxW * scrollBoxW / ( - that.scrollXMax + scrollBoxW );\r
+               scrollBoxW -= indicatorW;\r
+               XUI_ScrollBox_indicatorH\r
+                       [ 'css' ]({\r
+                               width : ( indicatorW | 0 ) + 'px'\r
+                       })\r
+                       [ 'animate' ]({\r
+                                       'from'        : {\r
+                                                       x       : scrollBoxW * that.scrollX / that.scrollXMax },\r
+                                       'to'          : {\r
+                                                       x       : scrollBoxW * x / that.scrollXMax,\r
+                                                       opacity : 0.5\r
+                                               },\r
+                                       'duration'    : opt_time,\r
+                                       'easing'      : opt_easing,\r
+                                       'lazyRelease' : opt_release\r
+                               });\r
+       };\r
+       \r
+       that.scrollX   = x;\r
+       that.scrollXEm = x / that.fontSize;\r
+       that.scrollY   = y;\r
+       that.scrollYEm = y / that.fontSize;\r
+};\r
 \r
-               return this._trigger( X.UI.Event.SCROLL_START, e );\r
-       },\r
+function X_UI_ScrollBox_onStart( e ){\r
+       var ret = X_CALLBACK_NONE;\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
+       if( !this.scrollEnabled || ( this.initiated && e.pointerType !== this.initiated ) ){\r
+               return ret;\r
+       };\r
 \r
-               this.pointX = point.pageX;\r
-               this.pointY = point.pageY;\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.isInTransition ){\r
+               this.isInTransition = false;\r
+               //console.log( '-1-' );\r
+               this[ 'dispatch' ]( XUI_Event.SCROLL_END );\r
+               // TODO current位置\r
+               XUI_ScrollBox_useCSSP ? this.xnodeSlider[ 'stop' ]() : this.xnode[ 'stop' ]();\r
+       };\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
+       this.startX    = this.scrollX;\r
+       this.startY    = this.scrollY;\r
+       this.absStartX = this.scrollX;\r
+       this.absStartY = this.scrollY;\r
+       this.pointX    = e.pageX;\r
+       this.pointY    = e.pageY;\r
+       \r
+       console.log( 'scrollstart ' + e.pageY + e.target.className() );\r
 \r
-               this.distX += deltaX;\r
-               this.distY += deltaY;\r
-               this.absDistX = ABS(this.distX);\r
-               this.absDistY = ABS(this.distY);\r
+       return ret | X_CALLBACK_PREVENT_DEFAULT;\r
+};\r
 \r
-               if (this.absDistX < 6 && this.absDistY < 6) {\r
-                       return;\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
-               // 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
+//console.log( 'scrollmove ' + e.buttons + ' ' + e.button );\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
+       if( !this.scrollEnabled || e.pointerType !== this.initiated ){\r
+               return ret;\r
+       };\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
+       // gpu の用意\r
+       if( !XUI_ScrollBox_useCSSP ? ( !this.xnode[ '_anime' ] || !this.xnode[ '_anime' ].phase ) : ( !this.xnodeSlider[ '_anime' ] || !this.xnodeSlider[ '_anime' ].phase ) ){\r
+               //console.log( 'gpuレイヤーの用意 ' + e.pageY );\r
+               //console.log( 'mov1 x:' + this.scrollX + ' y:' + this.scrollY );\r
+               X_UI_ScrollBox_translate( this, this.scrollX, this.scrollY, 0, '', 300 );\r
+               return ret;\r
+       };\r
 \r
-               if (!this.moved) {\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._resetPos(400);\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 (this.options.onTouchEnd) this.options.onTouchEnd.call(this, e);\r
-                       return;\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 (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
+       if( this.directionLocked === 'h' ){\r
+               deltaY = 0;\r
+       } else\r
+       if( this.directionLocked === 'v' ){\r
+               deltaX = 0;\r
+       };\r
 \r
-                       newPosX = this.x + momentumX.dist;\r
-                       newPosY = this.y + momentumY.dist;\r
+       deltaX = this.hasHScroll ? deltaX : 0;\r
+       deltaY = this.hasVScroll ? deltaY : 0;\r
+\r
+       if( !this.moved ){\r
+               this[ 'dispatch' ]( XUI_Event.SCROLL_BEFORE_MOVE );\r
+               this.moved  = true;\r
+               this.minusX = deltaX;\r
+               this.minusY = deltaY;\r
+               // indicator\r
+               XUI_ScrollBox_start( this );\r
+       } else {\r
+               this[ 'dispatch' ]( XUI_Event.SCROLL_MOVE );\r
+       };\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
+       newX = this.scrollX + deltaX;// - this.minusX;\r
+       newY = this.scrollY + deltaY;// - this.minusY;\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
+       // Slow down if outside of the boundaries\r
+       if( 0 < newX || newX < this.scrollXMax ){\r
+               newX = this.bounceEnabled ? this.scrollX + ( deltaX ) / 3 : 0 < newX ? 0 : this.scrollXMax;\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
+       if( 0 < newY || newY < this.scrollYMax ){\r
+               //console.log( 'slow... ' + newY + ' ' + this.scrollYMax );\r
+               newY = this.bounceEnabled ? this.scrollY + ( deltaY ) / 3 : 0 < newY ? 0 : this.scrollYMax;\r
+       };\r
 \r
+       this.directionX = 0 < deltaX ? -1 : deltaX < 0 ? 1 : 0;\r
+       this.directionY = 0 < deltaY ? -1 : deltaY < 0 ? 1 : 0;\r
 \r
-       /**\r
-       *\r
-       * Utilities\r
-       *\r
-       */\r
-       _startAnime: function () {\r
-               var startX = this.x,\r
-                       startY = this.y,\r
-                       step, animate;\r
+       console.log( 'mov2 x:' + newX + ' y:' + newY );\r
+       X_UI_ScrollBox_translate( this, newX, newY, 0, '', 300 );\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
+       if( 300 < timestamp - this.startTime ){\r
+               this.startTime = timestamp;\r
+               this.startX = this.scrollX;\r
+               this.startY = this.scrollY;\r
+       };\r
+       // イベントの拘束\r
+       return ret | X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_CAPTURE_POINTER;\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
+       //console.log( e.type + ' onend ' + XUI_Event.POINTER_OUT  );\r
+       \r
+       if( !this.scrollEnabled || e.pointerType !== this.initiated ){\r
+               //console.log( e.type + ' onend 1 ' + e.pointerType + ' ' + this.initiated  );\r
+               return ret;\r
+       };\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
+       delete this.isInTransition;\r
+       delete this.initiated;\r
+       this.endTime = X_Timer_now();                   \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
+       duration  = this.endTime - this.startTime;\r
+       newX      = Math.round( this.scrollX );\r
+       newY      = Math.round( this.scrollY );\r
+       distanceX = Math.abs(newX - this.startX);\r
+       distanceY = Math.abs(newY - this.startY);\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
+       // reset if we are outside of the boundaries\r
+       if( X_UI_ScrollBox_resetPosition( this, this.bounceTime ) ){\r
+               //console.log( e.type + ' onend 2 ' + XUI_Event.POINTER_OUT  );\r
+               return ret | X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_RELEASE_POINTER;\r
+       };\r
 \r
-               newDist = newDist * (dist < 0 ? -1 : 1);\r
-               newTime = speed / deceleration;\r
+       // we scrolled less than 10 pixels\r
+       if( !this.moved ){\r
+               this[ 'dispatch' ]( X_EVENT_CANCELED );\r
+               //console.log( 'we scrolled less than 10 pixels ' + e.pageY );\r
+               return ret | X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_RELEASE_POINTER;\r
+       };\r
 \r
-               return { dist: newDist, time: m.round(newTime) };\r
-       },\r
+       // start momentum animation if needed\r
+       if( this.momentumEnabled && duration < 300 ){\r
+               momentumX = this.hasHScroll ?\r
+                                               X_UI_ScrollBox_momentum( this.scrollX, this.startX, duration, this.scrollXMax, this.bounceEnabled ? this.boxWidth  * this.fontSize : 0, this.deceleration ) :\r
+                                               { destination: newX, duration: 0 };\r
+               momentumY = this.hasVScroll   ?\r
+                                               X_UI_ScrollBox_momentum( this.scrollY, this.startY, duration, this.scrollYMax, this.bounceEnabled ? this.boxHeight * this.fontSize : 0, this.deceleration ) :\r
+                                               { destination: newY, duration: 0 };\r
+               newX = momentumX.destination;\r
+               newY = momentumY.destination;\r
+               time = Math.max( momentumX.duration, momentumY.duration ) | 0;\r
+               this.isInTransition = true;\r
+       } else {\r
+               //console.log( '慣性無し' );\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
+       if( newX != this.scrollX || newY != this.scrollY ){\r
+               // change easing function when scroller goes out of the boundaries\r
+               if( 0 < newX || newX < this.scrollXMax || 0 < newY || newY < this.scrollYMax ){\r
+                       easing = 'quadratic';\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
+               //console.log( 'end2 x:' + newX + ' y:' + newY + '<-y:' + this.scrollY + ' t:' + time );\r
+               this.scrollTo( newX, newY, time, easing, 1000 );\r
+       } else {\r
+               //console.log( 'end1 x:' + newX + ' y:' + newY );\r
+               this.scrollTo( newX, newY, 0, '', 1000 );       // ensures that the last position is rounded\r
+               //console.log( '-3-' );\r
+               this[ 'dispatch' ]( XUI_Event.SCROLL_END );             \r
+       };\r
 \r
-               // Prepare the scrollbars\r
-               this._scrollbar('h');\r
-               this._scrollbar('v');\r
+       return ret | X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_RELEASE_POINTER;\r
+};\r
 \r
-               if (!this.zoomed) {\r
-                       this.scroller.style[transitionDuration] = '0';\r
-                       this._resetPos(400);\r
-               }\r
-       },\r
+function X_UI_ScrollBox_resetPosition( that, time ){\r
+       var x = that.scrollX,\r
+               y = that.scrollY;\r
 \r
-       scrollTo: function (x, y, time, relative) {\r
-               var step = x,\r
-                       i, l;\r
+       time = time || 0;\r
 \r
-               this.stop();\r
+       if( !that.hasHScroll || 0 < that.scrollX ){\r
+               x = 0;\r
+       } else\r
+       if( that.scrollX < that.scrollXMax ){\r
+               x = that.scrollXMax;\r
+       };\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
+       if( !that.hasVScroll || 0 < that.scrollY ){\r
+               y = 0;\r
+       } else\r
+       if( that.scrollY < that.scrollYMax ){\r
+               y = that.scrollYMax;\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
+       if( x === that.scrollX && y === that.scrollY ){\r
+               //console.log( 'no バウンド y:' + y + ' max:' + that.scrollYMax );\r
+               return false;\r
+       };\r
 \r
+       //console.log( ' ===> resetPosition - バウンド!' );\r
+       //console.log( '      x:' + x + ' y:' + y );\r
+       that.scrollTo( x, y, time, that.bounceEasing, 1000 );\r
 \r
+       return true;\r
+};\r
 \r
-X.UI._ScrollBox = X.UI._ChromeBox.inherits(\r
-       '_ScrollBox',\r
-       X.Class.PRIVATE_DATA | X.Class.SUPER_ACCESS,\r
-       {\r
-               //elmScroll     : null,\r
-               //elmScroller   : null,\r
-               //elmScrollbar  : null,\r
-               \r
-               scrolling      : false,\r
-               _scrollX       : 0,\r
-               _scrollY       : 0,\r
-               scrollXPercent : 0,\r
-               scrollYPercent : 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
-               },\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
-                       this._check();\r
-               },\r
-               \r
-               calculate : function(){\r
-                       X.UI._AbstractUINode.prototype.calculate.call( this, arguments );\r
-                       this._check();\r
-               },\r
-               \r
-               _check : function(){\r
-                       if( this.w < this._containerNode.w || this.h < this._containerNode.h ){\r
-                               // scroll\r
-                               if( this.scrolling ){\r
-                                       // fix scroll position from scrollXPercent, scrollYPercent\r
-                                       \r
-                               } else {\r
-                                       // create scroller\r
-                                       this.listen( X.UI.Event.POINTER_START, this );\r
-                                       \r
-                                       \r
-                                       this._move( 0, 0 );\r
-                                       \r
-                                       this.scrolling = true;\r
-                               };\r
-                       } else\r
-                       // no scroll\r
-                       if( this.scrolling ){\r
-                               // remove scroller\r
-                               \r
-                               ( this._scrollX !== 0 || this._scrollY !== 0 ) && this._move( 0, 0 );\r
-                               \r
-                               delete this.scrolling;\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
-                       if( this.scrolling ){\r
-                               // remove scroll\r
-                       };\r
-               }\r
-               \r
-       }\r
-);\r
+function X_UI_ScrollBox_onAnimeEnd( e ){\r
+       if( this.isInTransition && !X_UI_ScrollBox_resetPosition( this, this.bounceTime ) ){\r
+               this.isInTransition = false;\r
+               //console.log( '-2-' );\r
+               this[ 'dispatch' ]( XUI_Event.SCROLL_END );\r
+       };\r
+       //console.log(' -2.1- '+this.isInTransition );\r
+       return X_CALLBACK_NONE;\r
+};\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
 \r
-})();\r
+       return {\r
+               destination : Math.round( destination ),\r
+               duration    : duration\r
+       };\r
+};\r
 \r
 X.UI.ScrollBox = X.UI.ChromeBox.inherits(\r
        'ScrollBox',\r
-       X.Class.SUPER_ACCESS,\r
-       X.UI._ScrollBox,\r
+       X_Class.NONE,\r
        {\r
                Constructor : function(){\r
-                       var args = [],\r
-                               i    = arguments.length,\r
-                               arg, layout;\r
+                       var supports, slider;\r
+                       \r
+                       if( XUI_ScrollBox.prototype.usableAttrs === XUI_ChromeBox.prototype.usableAttrs ){\r
+                               XUI_ScrollBox.prototype.usableAttrs = supports = XUI_Attr_createAttrDef( XUI_Attr_Support, X_UI_ScrollBox_SUPPORT_ATTRS );\r
+               \r
+                               XUI_ScrollBox.prototype.attrClass   = XUI_Attr_preset( XUI_Box.prototype.attrClass, supports, { width  : '100%', height : '100%', bgColor : 0x111111 } );\r
+                       };\r
+                       \r
+                       var args = [\r
+                                               XUI_Layout_Vertical,                    \r
+                                               {\r
+                                                       name      : 'ScrollBox-Scroller',\r
+                                                       role      : 'container',\r
+                                                       width     : 'auto',\r
+                                                       minWidth  : '100%',\r
+                                                       height    : 'auto',\r
+                                                       minHeight : '100%'\r
+                                               }\r
+                                       ],\r
+                               l    = arguments.length, i = 0, j = 1,\r
+                               arg, attr;\r
                        \r
-                       for( ; i; ){\r
-                               arg = arguments[ --i ];\r
-                               if( arg.instanceOf && arg.instanceOf( X.UI.Layout.Base ) ){\r
-                                       layout = arg;\r
-                               } else {\r
-                                       args[ args.length ] = arg;\r
+                       for( ; i < l; ++i ){\r
+                               arg = arguments[ i ];\r
+                               if( arg[ 'instanceOf' ] && arg[ 'instanceOf' ]( XUI_LayoutBase ) ){\r
+                                       args[ 0 ] = arg;\r
+                               } else\r
+                               if( arg[ 'instanceOf' ] && arg[ 'instanceOf' ]( X.UI.AbstractUINode ) ){\r
+                                       args[ ++j ] = arg;\r
+                               } else\r
+                               if( X_Type_isObject( arg ) ){\r
+                                       args[ ++j ] = attr = arg;\r
+                                       slider = attr.scrollSlider;\r
                                };\r
                        };\r
-                       /*\r
-                       this.style = DisplayNodeStyle( this,\r
-                               X_Class_newPrivate(\r
+                       \r
+                       X_Pair_create(\r
+                               this,\r
+                               XUI_ScrollBox(\r
                                        this,\r
-                                       X.UI.Layout.Canvas,\r
+                                       null,\r
                                        [\r
-                                               Box(\r
-                                                       layout || VerticalLayoutManager,\r
-                                                       {\r
-                                                               name : 'ScrollBox-Scroller',\r
-                                                               role : 'container'\r
-                                                       },\r
-                                                       args\r
-                                               )\r
+                                               slider || X.UI.VBox.apply( 0, args )\r
                                        ]\r
                                )\r
                        );\r
-                       this.style.addName( 'ScrollBox' ); */\r
+                       \r
+                       //attr && this.attr( attr );\r
                },\r
                scrollX  : function(){\r
                        \r
@@ -1048,4 +733,5 @@ X.UI.ScrollBox = X.UI.ChromeBox.inherits(
                        \r
                }\r
        }\r
-);
\ No newline at end of file
+);\r
+\r