     
     /**
      *  @private
+     *  A utility function to add a new keyframe to the motion path and return the frame time.  
+     */
+    private function addKeyframe(motionPath:SimpleMotionPath,time:Number,position:Number,easer:IEaser):Number
+    {
+        var keyframe:Keyframe = new Keyframe(time,position);
+        keyframe.easer = easer;
+        motionPath.keyframes.push(keyframe);
+        return time;
+    }
+    
+    /**
+     *  @private
+     *  This function builds a motion path that reflects the starting conditions (position, velocity)
+     *  and exhibits overshoot/settle/snap effects (aka bounce/pull) according to the min/max boundaries.
+     */
+    private function createThrowMotionPath(propertyName:String, velocity:Number, position:Number, minPosition:Number,
+                                            maxPosition:Number, throwEffectTime:Number):SimpleMotionPath
+    {
+        var motionPath:SimpleMotionPath = new SimpleMotionPath(propertyName);
+        motionPath.keyframes = Vector.<Keyframe>([new Keyframe(0, position)]);
+        var keyframe:Keyframe = null;
+        var nowTime:Number = 0;
+        
+        // First, we handle the case where the velocity is zero (finger wasn't significantly moving when lifted).
+        // Ordinarily, we do nothing in this case, but if the list is currently scrolled past its end (i.e. "pulled"),
+        // we need to have the animation move it back so none of the empty space is visible.
+        if (velocity == 0)
+        {
+            if (position < minPosition || position > maxPosition)
+            {
+                // Velocity is zero and we're past the end of the list.  We want the 
+                // list to "snap" back to its resting position at the end.  We use a 
+                // cubic easer curve so the snap has high initial velocity and 
+                // gradually decelerates toward the resting point.
+                position = position < minPosition ? minPosition : maxPosition;
+                nowTime = addKeyframe(motionPath, nowTime + THROW_SETTLE_TIME, position, new Power(0, THROW_CURVE_EXPONENT));
+            }
+            else
+            {
+                // Velocity zero without being past the end of the list is a no-op.
+                return null;
+            }
+        }
+        
+        // Each iteration of this loop adds one of more keyframes to the motion path and then
+        // updates the velocity and position values.  Once the velocity has decayed to zero,
+        // the motion path is complete.
+        while (velocity != 0.0)
+        {
+            if ((position < minPosition && velocity > 0) || (position > maxPosition && velocity < 0))
+            {
+                // We're past the end of the list and the velocity is directed further beyond
+                // the end.  In this case we want to overshoot the end of the list and then 
+                // settle back to it.
+                var settlePosition:Number = position < minPosition ? minPosition : maxPosition;
+                
+                // OVERSHOOT_CURVE_EXPONENT is the default initial slope of the easer function we use for the overshoot.  
+                // This calculation scales the y axis (distance) of the overshoot so the actual slope matches the velocity.
+                var overshootPosition:Number = Math.round(position - 
+                    ((velocity / OVERSHOOT_CURVE_EXPONENT) * THROW_OVERSHOOT_TIME));
+                
+                nowTime = addKeyframe(motionPath, nowTime + THROW_OVERSHOOT_TIME,
+                    overshootPosition, new Power(0, OVERSHOOT_CURVE_EXPONENT));
+                nowTime = addKeyframe(motionPath, nowTime + THROW_SETTLE_TIME, settlePosition, new Sine(0.25));
+                
+                // Clear the velocity to indicate that the motion path is complete.
+                velocity = 0;
+                position = settlePosition;
+            }
+            else
+            {
+                // Here we're going to do a "normal" throw.
+
+                var effectTime:Number = throwEffectTime;
+                
+                var minVelocity:Number;
+                if (position < minPosition || position > maxPosition)
+                {
+                    // The throw is starting beyond the end of the list.  We need to enforce a minimum velocity
+                    // to make sure the throw makes it all the way back to the end (i.e. doesn't leave any blank area
+                    // exposed) and does so within THROW_SETTLE_TIME.  THROW_SETTLE_TIME needs to be consistently
+                    // adhered to in all cases where the tension of being beyond the end acts on the scroll position.  
+                    
+                    // The minimum velocity is that which gets us back to the end position in exactly THROW_SETTLE_TIME milliseconds. 
+                    minVelocity = ((position - (position < minPosition ? minPosition : maxPosition)) / 
+                        THROW_SETTLE_TIME) * THROW_CURVE_EXPONENT;
+                    if (Math.abs(velocity) < Math.abs(minVelocity))
+                    {   
+                        velocity = minVelocity;
+                        effectTime = THROW_SETTLE_TIME;
+                    }
+                }
+                
+                // The easer function we use is 1-((1-x)^THROW_CURVE_EXPONENT), which has an initial slope of THROW_CURVE_EXPONENT.
+                // The x axis is scaled according to the throw duration we calculated above, so now we need
+                // to determine the correct y-axis scaling (i.e. throw distance) such that the initial 
+                // slope matches the specified throw velocity.
+                var finalPosition:Number = Math.round(position - ((velocity / THROW_CURVE_EXPONENT) * effectTime));
+                
+                if (finalPosition < minPosition || finalPosition > maxPosition)
+                {
+                    // The throw is going to hit the end of the list.  In this case we need to clip the 
+                    // deceleration curve at the appropriate point.  We want the curve to look exactly as
+                    // it would if we were allowing the throw to go beyond the end of the list.  But the 
+                    // keyframe we add here will stop exactly at the end.  The subsequent loop iteration
+                    // will add keyframes that describe the overshoot & settle behavior.
+                    
+                    var endPosition:Number = finalPosition < minPosition ? minPosition : maxPosition;
+
+                    // since easing function is f(t) = start + (final - start) * e(t)
+                    // e(t) = Math.pow(1 - t/throwEffectTime, 3)
+                    // We want to solve for t when e(t) = finalPosition
+                    // t = throwEffectTime*(1-(Math.pow(1-((endPosition-position)/(finalVSP-position)),1/3)));
+                    var partialTime:Number = 
+                            effectTime*(1 - (Math.pow(1 - ((endPosition - position) / (finalPosition - position)), 1 / THROW_CURVE_EXPONENT)));
+                    
+                    // PartialExponentialCurve creates a portion of the throw easer curve, but scaled up to fill the 
+                    // specified duration.
+                    nowTime = addKeyframe(motionPath, nowTime + partialTime, endPosition,
+                            new PartialExponentialCurve(THROW_CURVE_EXPONENT, partialTime / effectTime));
+                    
+                    // Set the position just past the end of the list for the next loop iteration.
+                    if (finalPosition < minPosition)
+                        position = minPosition - 1;
+                    if (finalPosition > maxPosition)
+                        position = maxPosition + 1;
+                    
+                    // Set the velocity for the next loop iteration.  Make sure it matches the actual velocity in effect when the 
+                    // throw reaches the end of the list.
+                    //
+                    // The easer function we use for the throw is 1-((1-x)^3), the derivative of which is 3*x^2-6*x+3.
+                    // (I used http://www.numberempire.com/derivatives.php to differentiate the easer function).
+                    // Since the slope of a curve function at any point x (i.e. f(x)) is the value of the derivative at x (i.e. f'(x)),
+                    // we can use this to determine the velocity of the throw at the point it reached the beginning of the bounce.
+                    var x:Number = partialTime / effectTime;
+                    var y:Number =  3 * Math.pow(x, 2) - 6 * x + 3; // NOTE: This calculation must be matched to the THROW_CURVE_EXPONENT value.
+                    velocity = -y * (finalPosition - position) / effectTime; 
+                }
+                else
+                {
+                    // This is the simplest case.  The throw both begins and ends on the list (i.e. not past the 
+                    // end of the list).  We create a single keyframe and clear the velocity to indicate that the
+                    // motion path is complete.
+                    nowTime = addKeyframe(motionPath, nowTime + effectTime, finalPosition, new Power(0, THROW_CURVE_EXPONENT));
+                    velocity = 0;
+                }
+            }
+        }
+        return motionPath;
+    }
+
+    /**
+     *  @private
      *  Set up the effect to be used for the throw animation
-     *  FIXME (rfrishbe): this could use some work
-     */
-    private function setUpThrowEffect(velocityX:Number, velocityY:Number, distanceRatio:Point):void
+     */
+    private function setUpThrowEffect(velocityX:Number, velocityY:Number):Boolean
     {
         // create throwEffect if we haven't already
         if (!throwEffect)

     */
    private function mouseDownHandler(event:MouseEvent):void
    {
        // TODO (rfrishbe): if currently scrolling and we have spring/pull effects, 
        // we should stop scrolling and move the VSP/HSP in to range (0, maxVSP).
        
        captureNextClick = false;
        

import spark.effects.animation.SimpleMotionPath;
import spark.effects.easing.IEaser;
import spark.effects.easing.Power;
import spark.effects.easing.Sine;
import spark.utils.MouseEventUtil;

use namespace mx_internal;
