 
 package spark.components
 {
+import flash.display.InteractiveObject;
 import flash.events.Event;
 import flash.events.FocusEvent;
+import flash.events.KeyboardEvent;
 import flash.events.MouseEvent;
-import flash.events.KeyboardEvent;
+import flash.geom.Point;
 import flash.system.ApplicationDomain;
 import flash.text.TextField;
 import flash.ui.Keyboard;
 
-import spark.components.Group;
-import spark.components.supportClasses.ScrollerLayout;
-import spark.components.supportClasses.SkinnableComponent;
-import spark.core.IViewport;
-import spark.core.NavigationUnit;
 import mx.core.IInvalidating;
 import mx.core.IVisualElement;
 import mx.core.IVisualElementContainer;
 import mx.core.LayoutDirection;
 import mx.core.ScrollPolicy;
+import mx.events.EffectEvent;
 import mx.events.PropertyChangeEvent;
 import mx.managers.IFocusManagerComponent;
-import flash.display.InteractiveObject;
+
+import spark.components.Group;
+import spark.components.supportClasses.ScrollerLayout;
+import spark.components.supportClasses.SkinnableComponent;
+import spark.components.supportClasses.TouchScrollingEasing;
+import spark.core.IViewport;
+import spark.core.NavigationUnit;
+import spark.effects.Animate;
+import spark.effects.animation.MotionPath;
+import spark.effects.animation.SimpleMotionPath;
+import spark.events.TouchScrollEvent;
 
 include "../styles/metadata/BasicInheritingTextStyles.as"
 include "../styles/metadata/AdvancedInheritingTextStyles.as"

}

}

     */
    override public function styleChanged(styleProp:String):void
    {
        super.styleChanged(styleProp);
        
        var allStyles:Boolean = (styleProp == null || styleProp == "styleName");
        
        if (allStyles || styleProp == "horizontalScrollPolicy" || 
            styleProp == "verticalScrollPolicy")
        {
            invalidateSkin();
        }
        
        if (allStyles || styleProp == "inputMode")
        {
            if (getStyle("inputMode") == "touch")
            {
                installTouchListeners();
                
                touchScrollHelper = new TouchScrollHelper(this);
                touchScrollHelper.horizontalSlop = MIN_HORIZONTAL_SLOP;
                touchScrollHelper.verticalSlop = MIN_VERTICAL_SLOP;
                touchScrollHelper.diagonalSlop = MIN_DIAGONAL_SLOP;
            }
            else
            {
                uninstallTouchListeners();
            }
        }
    }
    
    /**
     *  @private
     */
    private function installTouchListeners():void
    {
        addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
        addEventListener(TouchScrollEvent.TOUCH_SCROLL_STARTING, touchScrollStartingHandler);
        addEventListener(TouchScrollEvent.TOUCH_SCROLL_START, touchScrollStartHandler);
        addEventListener(TouchScrollEvent.TOUCH_SCROLL_DRAG, touchScrollDragHandler);
        addEventListener(TouchScrollEvent.TOUCH_SCROLL_END, touchScrollEndHandler);
        addEventListener(TouchScrollEvent.TOUCH_SCROLL_THROW, touchScrollThrowHandler);
    }
    
    // ScrollerLayout vars
    public var horizontalScrollInProgress:Boolean = false;
    public var verticalScrollInProgress:Boolean = false;
    
    // SLOP CONSTANTS
    private static const MIN_HORIZONTAL_SLOP:int = 10;
    private static const MIN_VERTICAL_SLOP:int = 10;
    private static const MIN_DIAGONAL_SLOP:int = 10;
    
    /**
     *  @private
     */
    private function uninstallTouchListeners():void
    {
        removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
        removeEventListener(TouchScrollEvent.TOUCH_SCROLL_STARTING, touchScrollStartingHandler);
        removeEventListener(TouchScrollEvent.TOUCH_SCROLL_START, touchScrollStartHandler);
        removeEventListener(TouchScrollEvent.TOUCH_SCROLL_END, touchScrollEndHandler);
        removeEventListener(TouchScrollEvent.TOUCH_SCROLL_DRAG, touchScrollDragHandler);
        removeEventListener(TouchScrollEvent.TOUCH_SCROLL_THROW, touchScrollThrowHandler);
    }
    
    private function touchScrollStartingHandler(event:TouchScrollEvent):void
    {
        if (event.scrollingObject != this)
        {
            touchScrollHelper.stopScrollWatch();
        }
    }
    
    private function touchScrollStartHandler(event:TouchScrollEvent):void
    {
        installSwallowingMouseHandlers();
        hspBeforeTouchScroll = viewport.horizontalScrollPosition;
        vspBeforeTouchScroll = viewport.verticalScrollPosition;
        
        if (viewport.scrollAxis == "horizontal" || viewport.scrollAxis == "diagonal")
            horizontalScrollInProgress = true;
        if (viewport.scrollAxis == "vertical" || viewport.scrollAxis == "diagonal")
            verticalScrollInProgress = true;
        
        // need to invaliadte the ScrollerLayout object so it'll update the
        // scrollbars in overlay mode
        skin.invalidateDisplayList();
    }
    
    private function installSwallowingMouseHandlers():void
    {
        // block click events from occuring on me
        addEventListener(MouseEvent.CLICK, touchScrolling_captureMouseHandler, true);
        addEventListener(MouseEvent.MOUSE_DOWN, touchScrolling_captureMouseHandler, true);
//        addEventListener(MouseEvent.MOUSE_OVER, touchScrolling_captureMouseHandler, true);
//        addEventListener(MouseEvent.ROLL_OVER, touchScrolling_captureMouseHandler, true);
    }
    
    private function uninstallSwallowingMouseHandlers():void
    {
        removeEventListener(MouseEvent.CLICK, touchScrolling_captureMouseHandler, true);
        removeEventListener(MouseEvent.MOUSE_DOWN, touchScrolling_captureMouseHandler, true);
//        removeEventListener(MouseEvent.MOUSE_OVER, touchScrolling_captureMouseHandler, true);
//        removeEventListener(MouseEvent.ROLL_OVER, touchScrolling_captureMouseHandler, true);
    }
    
    private var stoppedPreemptively:Boolean = false;
    
    private function touchScrolling_captureMouseHandler(event:MouseEvent):void
    {
        switch(event.type)
        {
            case MouseEvent.MOUSE_DOWN:
                if (throwEffect && throwEffect.isPlaying)
                {
                    // stop the effect.  we don't want to move it to its final value...we want to stop it in place
                    // FIXME (rfrishbe): however if it's below 0 or above maxVSP, we should snap it to these values
                    stoppedPreemptively = true;
                    throwEffect.stop();
                    
                    // get new values in case we start scrolling again
                    hspBeforeTouchScroll = viewport.horizontalScrollPosition;
                    vspBeforeTouchScroll = viewport.verticalScrollPosition;
                }
                
                touchScrollHelper.startScrollWatch(event);
                event.stopImmediatePropagation();
                break;
            case MouseEvent.ROLL_OVER:
            case MouseEvent.MOUSE_OVER:
            case MouseEvent.CLICK:
                event.stopImmediatePropagation();
                break;
        }
    }
    
    private function mouseDownHandler(event:MouseEvent):void
    {
        // FIXME (rfrishbe): if currently scrolling, we should stop scrolling 
        // and move the VSP/HSP in to range (0, maxVSP).
               
        touchScrollHelper.startScrollWatch(event);
    }
    
    private var touchScrollHelper:TouchScrollHelper;
    private var hspBeforeTouchScroll:Number;
    private var vspBeforeTouchScroll:Number;
    
    private function touchScrollDragHandler(event:TouchScrollEvent):void
    {
        var xMove:int = 0;
        var yMove:int = 0;
        
        if (viewport.scrollAxis == "horizontal" || viewport.scrollAxis == "diagonal")
            xMove = event.dragX;
        
        if (viewport.scrollAxis == "vertical" || viewport.scrollAxis == "diagonal")
            yMove = event.dragY

        // FIXME (rfrishbe): figure out how we want negative signs to work
        viewport.horizontalScrollPosition = hspBeforeTouchScroll - xMove;
        viewport.verticalScrollPosition = vspBeforeTouchScroll - yMove;
    }
    
    private function touchScrollEndHandler(event:TouchScrollEvent):void
    {
        horizontalScrollInProgress = false;
        verticalScrollInProgress = false;
        uninstallSwallowingMouseHandlers();
        
        // need to invaliadte the ScrollerLayout object so it'll update the
        // scrollbars in overlay mode
        skin.invalidateDisplayList();
    }
    
    private var throwEaser:TouchScrollingEasing;
    private var EFFECT_TIME:int = 3000;
    private var throwEffect:Animate;
    
    private function setUpThrowEffect(velocity:Point):void
    {
        if (!throwEffect)
        {
            throwEffect = new Animate();
            throwEffect.addEventListener(EffectEvent.EFFECT_END, throwEffect_effectEndHandler);
            
            throwEaser = new TouchScrollingEasing(0);
        }
        var finalHSP:Number = viewport.horizontalScrollPosition;
        var finalVSP:Number = viewport.verticalScrollPosition;
        
        // want velocity to be 0 and to decrease by currentFriction every 
        // SCROLLING_TIMER_DELAY over EFFECT_TIME total
        var decelerationRate:Point = velocity.clone();
        decelerationRate.x = decelerationRate.x/EFFECT_TIME;
        decelerationRate.y = decelerationRate.y/EFFECT_TIME;
        
        // figure out where we're scrolling to
        if (viewport.scrollAxis == "horizontal" || viewport.scrollAxis == "diagonal")
        {
            var hsp:Number = viewport.horizontalScrollPosition;
            var viewportWidth:Number = isNaN(viewport.width) ? 0 : viewport.width;
            var cWidth:Number = viewport.contentWidth;
            var maxWidth:Number = Math.max(0, (cWidth == 0) ? viewport.horizontalScrollPosition : cWidth - viewportWidth);
            
            // initialVelocity.y * ellapsedTime - (0.5 * frictionToUse * ellapsedTime * ellapsedTime);
            finalHSP = viewport.horizontalScrollPosition - (velocity.x * EFFECT_TIME) + (.5 * decelerationRate.x * EFFECT_TIME * EFFECT_TIME);
//            finalHSP = Math.max(0, Math.min(finalHSP, maxWidth));
            
        }
        
        if (viewport.scrollAxis == "vertical" || viewport.scrollAxis == "diagonal")
        {
            var vsp:Number = viewport.verticalScrollPosition;
            var viewportHeight:Number = isNaN(viewport.height) ? 0 : viewport.height;
            var cHeight:Number = viewport.contentHeight;
            var maxHeight:Number = Math.max(0, (cHeight == 0) ? viewport.verticalScrollPosition : cHeight - viewportHeight);
            
            // initialVelocity.y * ellapsedTime - (0.5 * frictionToUse * ellapsedTime * ellapsedTime);
            finalVSP = viewport.verticalScrollPosition - (velocity.y * EFFECT_TIME) + (.5 * decelerationRate.y * EFFECT_TIME * EFFECT_TIME);
//            finalVSP = Math.max(0, Math.min(finalVSP, maxHeight));
        }
        
        // set up easer.  Sometimes these values won't be exactly right, but it shouldn't 
        // matter if maxHeight is wrong as long as we aren't actually moving it around
        // in that axis.
        throwEaser.velocity = velocity;
        throwEaser.maxHeight = maxHeight;
        throwEaser.maxWidth = maxWidth;
        throwEaser.maxExtraY = viewportHeight;
        throwEaser.maxExtraX = viewportWidth;
        
        // FIXME (rfrishbe): could move some of this to a setup function to do only once
        // set up throw effect
        throwEffect.target = viewport;
        throwEffect.duration = EFFECT_TIME;
        
        // maybe use motion paths with more keyframes for the bounce effect??
        var horizontalMP:MotionPath = new SimpleMotionPath("horizontalScrollPosition", hsp, finalHSP);
        var verticalMP:MotionPath = new SimpleMotionPath("verticalScrollPosition", vsp, finalVSP);
        
        if (viewport.scrollAxis == "diagonal")
            throwEffect.motionPaths = Vector.<MotionPath>([horizontalMP, verticalMP]);
        else if (viewport.scrollAxis == "horizontal")
            throwEffect.motionPaths = Vector.<MotionPath>([horizontalMP]);
        else if (viewport.scrollAxis == "vertical")
            throwEffect.motionPaths = Vector.<MotionPath>([verticalMP]);
        
        throwEffect.easer = throwEaser;
    }
    
    private function throwEffect_effectEndHandler(event:EffectEvent):void
    {
        // if we stopped the effect ourself (because someone pressed down), then let's not consider
        // this the end
        if (stoppedPreemptively)
            return;
        
        dispatchEvent(new TouchScrollEvent(TouchScrollEvent.TOUCH_SCROLL_THROW_ANIMATION_END));
    }
    
    private function touchScrollThrowHandler(event:TouchScrollEvent):void
    {
        stoppedPreemptively = false;
        setUpThrowEffect(event.throwVelocity);
        throwEffect.play();
    }

    /**
