You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by yi...@apache.org on 2020/09/26 16:41:47 UTC

[royale-asjs] branch develop updated: Added some spark animation infra

This is an automated email from the ASF dual-hosted git repository.

yishayw pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git


The following commit(s) were added to refs/heads/develop by this push:
     new aee0907  Added some spark animation infra
aee0907 is described below

commit aee0907d93bbe6b8c7fa6cf2a64fbd27704b9f31
Author: Yishay Weiss <yi...@yell.com>
AuthorDate: Sat Sep 26 17:41:20 2020 +0100

    Added some spark animation infra
---
 .../MXRoyale/src/main/royale/mx/effects/Effect.as  |  383 +-
 .../src/main/royale/mx/effects/EffectInstance.as   |  317 +-
 .../src/main/royale/mx/effects/TweenEffect.as      |    2 +-
 .../src/main/royale/SparkRoyaleClasses.as          |    2 +-
 .../src/main/royale/spark/effects/Animate.as       |  681 +++
 .../royale/spark/effects/animation/Animation.as    | 1415 ++++++
 .../spark/effects/animation/IAnimationTarget.as    |  113 +
 .../royale/spark/effects/animation/Keyframe.as     |  223 +
 .../royale/spark/effects/animation/MotionPath.as   |  288 ++
 .../spark/effects/animation/RepeatBehavior.as      |   58 +
 .../spark/effects/animation/SimpleMotionPath.as    |  192 +
 .../royale/spark/effects/easing/EaseInOutBase.as   |  188 +
 .../royale/spark/effects/easing/EasingFraction.as  |   72 +
 .../src/main/royale/spark/effects/easing/Linear.as |  199 +
 .../src/main/royale/spark/effects/easing/Sine.as   |  113 +
 .../spark/effects/interpolation/IInterpolator.as   |  105 +
 .../interpolation/MultiValueInterpolator.as        |  208 +
 .../effects/interpolation/NumberInterpolator.as    |  145 +
 .../spark/effects/interpolation/RGBInterpolator.as |  193 +
 .../effects/supportClasses/AnimateColorInstance.as |  134 +
 .../effects/supportClasses/AnimateInstance.as      | 1166 +++++
 .../main/royale/spark/filters/ColorMatrixFilter.as |    1 -
 .../main/royale/spark/primitives/supportClasses    | 4673 ++++++++++++++++++++
 23 files changed, 10866 insertions(+), 5 deletions(-)

diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/effects/Effect.as b/frameworks/projects/MXRoyale/src/main/royale/mx/effects/Effect.as
index b7b431a..f016bdf 100644
--- a/frameworks/projects/MXRoyale/src/main/royale/mx/effects/Effect.as
+++ b/frameworks/projects/MXRoyale/src/main/royale/mx/effects/Effect.as
@@ -21,7 +21,8 @@ package mx.effects
 {
 
 import org.apache.royale.effects.Effect;
-
+import mx.core.mx_internal;
+use namespace mx_internal;
 /**
  *  Effect is the base class for effects in Royale.
  * 
@@ -94,9 +95,389 @@ public class Effect extends org.apache.royale.effects.Effect
 	  } 
 
   
+    /**
+     *  @copy mx.effects.IEffect#getAffectedProperties()
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function getAffectedProperties():Array /* of String */
+    {
+        // Every subclass should override this method.
+        return [];
+    }
 
+    /**
+     *  @private
+     *  Storage for the relevantStyles property.
+     */
+    private var _relevantStyles:Array /* of String */ = [];
+        
+    /**
+     *  @copy mx.effects.IEffect#relevantStyles
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function get relevantStyles():Array /* of String */
+    {
+        return _relevantStyles;
+    }
+
+    /**
+     *  @private
+     */
+    public function set relevantStyles(value:Array /* of String */):void
+    {
+        _relevantStyles = value;
+    }
 
+    /**
+     *  Copies properties of the effect to the effect instance. 
+     *
+     *  <p>Flex calls this method from the <code>Effect.createInstance()</code>
+     *  method; you do not have to call it yourself. </p>
+     *
+     *  <p>When you create a custom effect, override this method to 
+     *  copy properties from the Effect class to the effect instance class. 
+     *  In your override, call <code>super.initInstance()</code>. </p>
+     *
+     *  @param EffectInstance The effect instance to initialize.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    protected function initInstance(instance:IEffectInstance):void
+    {
+        //instance.duration = duration;
+        //Object(instance).durationExplicitlySet = durationExplicitlySet;
+        //instance.effect = this;
+        //instance.effectTargetHost = effectTargetHost;
+        //instance.hideFocusRing = hideFocusRing;
+        //instance.repeatCount = repeatCount;
+        //instance.repeatDelay = repeatDelay;
+        //instance.startDelay = startDelay;
+        //instance.suspendBackgroundProcessing = suspendBackgroundProcessing;
+    }
 	
+    /**
+     *  Used internally by the Effect infrastructure.
+     *  If <code>captureStartValues()</code> has been called,
+     *  then when Flex calls the <code>play()</code> method, it uses this function
+     *  to set the targets back to the starting state.
+     *  The default behavior is to take the value captured
+     *  using the <code>getValueFromTarget()</code> method
+     *  and set it directly on the target's property. For example: <pre>
+     *  
+     *  target[property] = value;</pre>
+     *
+     *  <p>Only override this method if you need to apply
+     *  the captured values in a different way.
+     *  Note that style properties of a target are set
+     *  using a different mechanism.
+     *  Use the <code>relevantStyles</code> property to specify
+     *  which style properties to capture and apply. </p>
+     *
+     *  @param target The effect target.
+     *
+     *  @param property The target property.
+     *
+     *  @param value The value of the property. 
+     *
+     *  @param props Array of Objects, where each Array element contains a 
+     *  <code>start</code> and <code>end</code> Object
+     *  for the properties that the effect is monitoring. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    protected function applyValueToTarget(target:Object, property:String, 
+                                          value:*, props:Object):void
+    {
+        //if (property in target)
+        //{
+            //// The "property in target" test only tells if the property exists
+            //// in the target, but does not distinguish between read-only and
+            //// read-write properties. Put a try/catch around the setter and 
+            //// ignore any errors.
+            //try
+            //{
+                //
+                //if (applyActualDimensions &&
+                    //target is IFlexDisplayObject &&
+                    //property == "height")
+                //{
+                    //target.setActualSize(target.width,value);
+                //}
+                //else if (applyActualDimensions &&
+                         //target is IFlexDisplayObject &&
+                         //property == "width")
+                //{
+                    //target.setActualSize(value,target.height);
+                //}
+                //else
+                //{
+                    //target[property] = value;
+                //}
+            //}
+            //catch(e:Error)
+            //{
+                //// Ignore errors
+            //}
+        //}
+    }
+
+    /**
+     *  @private
+     *  Used internally to grab the values of the relevant properties
+     */
+    mx_internal function captureValues(propChanges:Array,
+                                       setStartValues:Boolean,
+                                       targetsToCapture:Array = null):Array
+    {
+        //var n:int;
+        //var i:int;
+        //if (!propChanges)
+        //{
+            //propChanges = [];
+                        //
+            //// Create a new PropertyChanges object for the sum of all targets.
+            //n = targets.length;
+            //for (i = 0; i < n; i++)
+                //propChanges.push(new PropertyChanges(targets[i]));
+        //}
+                    //
+        //// Merge Effect.filterProperties and filterObject.filterProperties
+        //var effectProps:Array = !filterObject ?
+                                //relevantProperties :
+                                //mergeArrays(relevantProperties,
+                                //filterObject.filterProperties);
+        //
+        //var valueMap:Object;
+        //var target:Object;      
+        //var m:int;  
+        //var j:int;
+        //
+        //// For each target, grab the property's value
+        //// and put it into the propChanges Array. 
+        //// Walk the targets.
+        //if (effectProps && effectProps.length > 0)
+        //{
+            //n = propChanges.length;
+            //for (i = 0; i < n; i++)
+            //{
+                //target = propChanges[i].target;
+                //if (targetsToCapture == null || targetsToCapture.length == 0 ||
+                    //targetsToCapture.indexOf(target) >= 0)
+                //{
+                    //valueMap = setStartValues ? propChanges[i].start : propChanges[i].end;
+                                            //
+                    //// Walk the properties in the target
+                    //m = effectProps.length;
+                    //for (j = 0; j < m; j++)
+                    //{
+                        //// Don't clobber values already set
+                        //if (valueMap[effectProps[j]] === undefined)
+                        //{
+                            //valueMap[effectProps[j]] = 
+                                //getValueFromTarget(target,effectProps[j]);
+                        //}
+                    //}
+                //}
+            //}
+        //}
+        //
+        //var styles:Array = !filterObject ?
+                           //relevantStyles :
+                           //mergeArrays(relevantStyles,
+                           //filterObject.filterStyles);
+        //
+        //if (styles && styles.length > 0)
+        //{         
+            //n = propChanges.length;
+            //for (i = 0; i < n; i++)
+            //{
+                //target = propChanges[i].target;
+                //if (targetsToCapture == null || targetsToCapture.length == 0 ||
+                    //targetsToCapture.indexOf(target) >= 0)
+                //{
+                    //if (!(target is IStyleClient))
+                        //continue;
+                        //
+                    //valueMap = setStartValues ? propChanges[i].start : propChanges[i].end;
+                                            //
+                    //// Walk the properties in the target.
+                    //m = styles.length;
+                    //for (j = 0; j < m; j++)
+                    //{
+                        //// Don't clobber values set by relevantProperties
+                        //if (valueMap[styles[j]] === undefined)
+                        //{
+                            //var value:* = target.getStyle(styles[j]);
+                            //valueMap[styles[j]] = value;
+                        //}
+                    //}
+                //}
+            //}
+        //}
+        //
+        //return propChanges;
+	return [];
+    }
+    /**
+     *  @private
+     *  Applies the start values found in the array of PropertyChanges
+     *  to the relevant targets.
+     */
+    mx_internal function applyStartValues(propChanges:Array,
+                                     targets:Array):void
+    {
+        //var effectProps:Array = relevantProperties;
+                    //
+        //var n:int = propChanges.length;
+        //for (var i:int = 0; i < n; i++)
+        //{
+            //var m:int;
+            //var j:int;
+//
+            //var target:Object = propChanges[i].target;
+            //var apply:Boolean = false;
+            //
+            //m = targets.length;
+            //for (j = 0; j < m; j++)
+            //{
+                //if (targets[j] == target)
+                //{   
+                    //apply = filterInstance(propChanges, target);
+                    //break;
+                //}
+            //}
+            //
+            //if (apply)
+            //{
+                //// Walk the properties in the target
+                //m = effectProps.length;
+                //for (j = 0; j < m; j++)
+                //{
+                    //var propName:String = effectProps[j];
+                    //var startVal:* = propChanges[i].start[propName];
+                    //var endVal:* = propChanges[i].end[propName];
+                    //if (propName in propChanges[i].start &&
+                        //endVal != startVal &&
+                        //(!(startVal is Number) ||
+                         //!(isNaN(endVal) && isNaN(startVal))))
+                    //{
+                        //applyValueToTarget(target, effectProps[j],
+                                //propChanges[i].start[effectProps[j]],
+                                //propChanges[i].start);
+                    //}
+                //}
+                //
+                //// Walk the styles in the target
+                //m = relevantStyles.length;
+                //for (j = 0; j < m; j++)
+                //{
+                    //var styleName:String = relevantStyles[j];
+                    //var startStyle:* = propChanges[i].start[styleName];
+                    //var endStyle:* = propChanges[i].end[styleName];
+                    //if (styleName in propChanges[i].start &&
+                        //endStyle != startStyle &&
+                        //(!(startStyle is Number) ||
+                         //!(isNaN(endStyle) && isNaN(startStyle))) &&
+                        //target is IStyleClient)
+                    //{
+                        //if (propChanges[i].end[relevantStyles[j]] !== undefined)
+                            //target.setStyle(relevantStyles[j], propChanges[i].start[relevantStyles[j]]);
+                        //else
+                            //target.clearStyle(relevantStyles[j]);
+                    //}
+                //}
+            //}
+        //}
+    }
+
+    /**
+     *  @private
+     *  Applies the start values found in the array of PropertyChanges
+     *  to the relevant targets.
+     */
+    mx_internal function applyEndValues(propChanges:Array,
+                                    targets:Array):void
+    {
+        //// For now, only new Flex4 effects will apply end values when transitions
+        //// are over, to preserve the previous behavior of Flex3 effects
+        //if (!applyTransitionEndProperties)
+            //return;
+            //
+        //var effectProps:Array = relevantProperties;
+                    //
+        //var n:int = propChanges.length;
+        //for (var i:int = 0; i < n; i++)
+        //{
+            //var m:int;
+            //var j:int;
+//
+            //var target:Object = propChanges[i].target;
+            //var apply:Boolean = false;
+            //
+            //m = targets.length;
+            //for (j = 0; j < m; j++)
+            //{
+                //if (targets[j] == target)
+                //{   
+                    //apply = filterInstance(propChanges, target);
+                    //break;
+                //}
+            //}
+            //
+            //if (apply)
+            //{
+                //// Walk the properties in the target
+                //m = effectProps.length;
+                //for (j = 0; j < m; j++)
+                //{
+                    //var propName:String = effectProps[j];
+                    //var startVal:* = propChanges[i].start[propName];
+                    //var endVal:* = propChanges[i].end[propName];
+                    //if (propName in propChanges[i].end &&
+                        //(!(endVal is Number) ||
+                         //!(isNaN(endVal) && isNaN(startVal))))
+                    //{
+                        //applyValueToTarget(target, propName,
+                                //propChanges[i].end[propName],
+                                //propChanges[i].end);
+                    //}
+                //}
+                //
+                //// Walk the styles in the target
+                //m = relevantStyles.length;
+                //for (j = 0; j < m; j++)
+                //{
+                    //var styleName:String = relevantStyles[j];
+                    //var startStyle:* = propChanges[i].start[styleName];
+                    //var endStyle:* = propChanges[i].end[styleName];
+                    //if (styleName in propChanges[i].end &&
+                        //(!(endStyle is Number) ||
+                         //!(isNaN(endStyle) && isNaN(startStyle))) &&
+                        //target is IStyleClient)
+                    //{
+                        //if (propChanges[i].end[styleName] !== undefined)
+                            //target.setStyle(styleName, propChanges[i].end[styleName]);
+                        //else
+                            //target.clearStyle(styleName);
+                    //}
+                //}
+            //}
+        //}
+    }
 }
 
 }
diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/effects/EffectInstance.as b/frameworks/projects/MXRoyale/src/main/royale/mx/effects/EffectInstance.as
index 3a95608..fade0df 100644
--- a/frameworks/projects/MXRoyale/src/main/royale/mx/effects/EffectInstance.as
+++ b/frameworks/projects/MXRoyale/src/main/royale/mx/effects/EffectInstance.as
@@ -27,10 +27,10 @@ import flash.events.TimerEvent;
 import flash.utils.Timer;
 import flash.utils.getQualifiedClassName;
 import flash.utils.getTimer; 
-import mx.effects.effectClasses.PropertyChanges;
 
 */
 
+import mx.effects.effectClasses.PropertyChanges;
 import mx.core.UIComponent;
 import mx.core.mx_internal;
 import mx.events.EffectEvent;
@@ -168,6 +168,32 @@ public class EffectInstance extends EventDispatcher implements IEffectInstance
     }
 
    
+    /**
+     *  @private
+     *  Storage for the effect property.
+     */
+    private var _effect:IEffect;
+
+    /**
+     *  @copy mx.effects.IEffectInstance#effect
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function get effect():IEffect
+    {
+        return _effect;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set effect(value:IEffect):void
+    {
+        _effect = value;
+    }
     
     //----------------------------------
     //  target
@@ -332,6 +358,295 @@ public class EffectInstance extends EventDispatcher implements IEffectInstance
         }
     }
 
+    /**
+     *  Current time position of the effect.
+     *  This property has a value between 0 and the total duration, 
+     *  which includes the Effect's <code>startDelay</code>, 
+     *  <code>repeatCount</code>, and <code>repeatDelay</code>.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function get playheadTime():Number 
+    {
+        //return Math.max(playCount - 1, 0) * (duration + repeatDelay) +
+               //(playReversed ? 0 : startDelay);
+	return NaN;
+    }
+
+    /**
+     * @private
+     */
+    public function set playheadTime(value:Number):void
+    {
+        //if (delayTimer && delayTimer.running)
+        //{
+            //delayTimer.reset();
+            //if (value < startDelay)
+            //{
+                //delayTimer = new Timer(startDelay - value, 1);
+                //delayStartTime = getTimer();
+                //delayTimer.addEventListener(TimerEvent.TIMER, delayTimerHandler);
+                //delayTimer.start();
+            //}
+            //else
+            //{
+                //playCount = 0;
+                //play();
+            //}
+        //}
+    }
+
+    /**
+     *  @copy mx.effects.IEffectInstance#pause()
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function pause():void
+    {   
+        //if (delayTimer && delayTimer.running && !isNaN(delayStartTime))
+        //{
+            //delayTimer.stop(); // Pause the timer
+            //delayElapsedTime = getTimer() - delayStartTime;
+        //}
+    }
+
+    /**
+     *  @copy mx.effects.IEffectInstance#stop()
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function stop():void
+    {   
+        //if (delayTimer)
+            //delayTimer.reset();
+        //stopRepeat = true;
+        //// Dispatch STOP event in case listeners need to handle this situation
+        //// The Effect class may hinge setting final state values on whether
+        //// the effect was stopped or ended.
+        //dispatchEvent(new EffectEvent(EffectEvent.EFFECT_STOP,
+                                     //false, false, this));        
+        //if (target && (target is IEventDispatcher))
+            //target.dispatchEvent(new EffectEvent(EffectEvent.EFFECT_STOP,
+                                                 //false, false, this));
+        //finishEffect();
+    }
+
+    /**
+     *  @copy mx.effects.IEffectInstance#resume()
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function resume():void
+    {
+        //if (delayTimer && !delayTimer.running && !isNaN(delayElapsedTime))
+        //{
+            //delayTimer.delay = !playReversed ? delayTimer.delay - delayElapsedTime : delayElapsedTime;
+            //delayStartTime = getTimer();
+            //delayTimer.start();
+        //}
+    }
+
+    /**
+     *  @copy mx.effects.IEffectInstance#reverse()
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function reverse():void
+    {
+        //if (repeatCount > 0)
+            //playCount = repeatCount - playCount + 1;
+    }
+
+    /**
+     *  @copy mx.effects.IEffectInstance#startEffect()
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function startEffect():void
+    {   
+        //EffectManager.effectStarted(this);
+//
+        //if (target is UIComponent)
+        //{
+            //UIComponent(target).effectStarted(this);
+        //}
+        //
+        //if (startDelay > 0 && !playReversed)
+        //{
+            //delayTimer = new Timer(startDelay, 1);
+            //delayStartTime = getTimer();
+            //delayTimer.addEventListener(TimerEvent.TIMER, delayTimerHandler);
+            //delayTimer.start();
+        //}
+        //else
+        //{
+            //play();
+        //}
+    }
+
+    //----------------------------------
+    //  playReversed
+    //----------------------------------
+
+    /**
+     *  @private
+     *  Storage for the playReversed property. 
+     */
+    private var _playReversed:Boolean;
+    
+    /**
+     *  @private
+     *  Used internally to specify whether or not this effect
+     *  should be played in reverse.
+     *  Set this value before you play the effect. 
+     */
+    mx_internal function get playReversed():Boolean
+    {
+        return _playReversed;
+    }
+
+    //----------------------------------
+    //  propertyChanges
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the propertyChanges property. 
+     */
+    private var _propertyChanges:PropertyChanges;
+
+    /**
+     *  @copy mx.effects.IEffectInstance#propertyChanges
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function get propertyChanges():PropertyChanges
+    {
+        return _propertyChanges;
+    }
+
+    /**
+     *  @private
+     */
+    public function set propertyChanges(value:PropertyChanges):void
+    {
+        _propertyChanges = value;
+    }
+
+    
+    //----------------------------------
+    //  repeatCount
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the repeatCount property. 
+     */
+    private var _repeatCount:int = 0;
+
+    /**
+     *  @copy mx.effects.IEffectInstance#repeatCount
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function get repeatCount():int
+    {
+        return _repeatCount;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set repeatCount(value:int):void
+    {
+        _repeatCount = value;
+    }
+    
+    //----------------------------------
+    //  repeatDelay
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the repeatDelay property. 
+     */
+    private var _repeatDelay:int = 0;
+
+    /**
+     *  @copy mx.effects.IEffectInstance#repeatDelay
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function get repeatDelay():int
+    {
+        return _repeatDelay;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set repeatDelay(value:int):void
+    {
+        _repeatDelay = value;
+    }
+    
+    //----------------------------------
+    //  startDelay
+    //----------------------------------
+
+    /**
+     *  @private
+     *  Storage for the startDelay property. 
+     */
+    private var _startDelay:int = 0;
+
+    /**
+     *  @copy mx.effects.IEffectInstance#startDelay
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 9
+     *  @playerversion AIR 1.1
+     *  @productversion Flex 3
+     */
+    public function get startDelay():int
+    {
+        return _startDelay;
+    }
+
+    /**
+     *  @private
+     */
+    public function set startDelay(value:int):void
+    {
+        _startDelay = value;
+    }
 }
 
 }
diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/effects/TweenEffect.as b/frameworks/projects/MXRoyale/src/main/royale/mx/effects/TweenEffect.as
index c9ff63f..2f2ab5b 100644
--- a/frameworks/projects/MXRoyale/src/main/royale/mx/effects/TweenEffect.as
+++ b/frameworks/projects/MXRoyale/src/main/royale/mx/effects/TweenEffect.as
@@ -205,7 +205,7 @@ public class TweenEffect extends Effect
     /**
      *  @private
      */
-    /* override */ protected function initInstance(instance:IEffectInstance):void
+    override protected function initInstance(instance:IEffectInstance):void
     {
         //super.initInstance(instance);
         
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/SparkRoyaleClasses.as b/frameworks/projects/SparkRoyale/src/main/royale/SparkRoyaleClasses.as
index 8554576..b309abf 100644
--- a/frameworks/projects/SparkRoyale/src/main/royale/SparkRoyaleClasses.as
+++ b/frameworks/projects/SparkRoyale/src/main/royale/SparkRoyaleClasses.as
@@ -107,7 +107,7 @@ import spark.components.IItemRenderer; IItemRenderer;
 //import spark.components.mediaClasses.ScrubBar; ScrubBar;
 //import spark.components.mediaClasses.VolumeBar; VolumeBar;
 import spark.components.supportClasses.ButtonBarHorizontalLayout; ButtonBarHorizontalLayout;
-//import spark.effects.AnimateColor; AnimateColor; // needed
+import spark.effects.AnimateColor; AnimateColor; // needed
 import spark.effects.Fade; Fade;
 //import spark.effects.Resize; Resize; // needed
 //import spark.effects.SetAction; SetAction; // needed
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/Animate.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/Animate.as
new file mode 100644
index 0000000..c8f2a13
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/Animate.as
@@ -0,0 +1,681 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects
+{
+import mx.core.mx_internal;
+import mx.effects.Effect;
+import mx.effects.IEffectInstance;
+import mx.events.EffectEvent;
+
+import spark.effects.animation.Animation;
+import spark.effects.animation.MotionPath;
+import spark.effects.animation.RepeatBehavior;
+import spark.effects.easing.IEaser;
+import spark.effects.easing.Sine;
+import spark.effects.interpolation.IInterpolator;
+import spark.effects.supportClasses.AnimateInstance;
+
+use namespace mx_internal;
+
+//--------------------------------------
+//  Excluded APIs
+//--------------------------------------
+
+// Exclude suspendBackgroundProcessing for now because the Flex 4
+// effects depend on the layout validation work that the flag suppresses
+[Exclude(name="suspendBackgroundProcessing", kind="property")]
+
+
+[DefaultProperty("motionPaths")]
+
+/**
+ * Dispatched every time the effect updates the target.
+ *
+ * @eventType mx.events.EffectEvent.EFFECT_UPDATE
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+[Event(name="effectUpdate", type="mx.events.EffectEvent")]
+
+/**
+ * Dispatched when the effect begins a new repetition, for
+ * any effect that is repeated more than once.
+ * Flex also dispatches an <code>effectUpdate</code> event 
+ * for the effect at the same time.
+ *
+ * @eventType mx.events.EffectEvent.EFFECT_REPEAT
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+[Event(name="effectRepeat", type="mx.events.EffectEvent")]
+
+
+/**
+ * This Animate effect animates an arbitrary set of properties between values. 
+ * Specify the properties and values to animate by setting the <code>motionPaths</code> property. 
+ *  
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:Animate&gt;</code> tag
+ *  inherits all of the tag attributes of its superclass,
+ *  and adds the following tag attributes:</p>
+ *
+ *  <pre>
+ *  &lt;s:Animate
+ *    <b>Properties</b>
+ *    id="ID"
+ *    disableLayout="false"
+ *    easer="{spark.effects.easing.Sine(.5)}"
+ *    interpolator="NumberInterpolator"
+ *    motionPaths="no default"
+ *    repeatBehavior="loop"
+ *  /&gt;
+ *  </pre>
+ *
+ *  @see spark.effects.supportClasses.AnimateInstance
+ *
+ *  @includeExample examples/AnimateEffectExample.mxml
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class Animate extends Effect
+{
+    //include "../core/Version.as";
+
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Constructor. 
+     *
+     *  @param target The Object to animate with this effect.  
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function Animate(target:Object = null)
+    {
+        super(target);
+          
+        instanceClass = AnimateInstance;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Variables
+    //
+    //--------------------------------------------------------------------------
+    
+    // Cached version of the affected properties. By default, we simply return
+    // the list of properties specified in the motionPaths Vector.
+    // Subclasses should override getAffectedProperties() if they wish to 
+    // specify a different set.
+    private var affectedProperties:Array = null;
+
+    // Cached version of the relevant styles. By default, we simply return
+    // the list of properties specified in the motionPaths Vector.
+    // Subclasses should override relevantStyles() if they wish to 
+    // specify a different set.
+    private var _relevantStyles:Array = null;
+
+    // Cached default easer. We only need one of these, so we cache this static
+    // object to be reused by any Animate instances that do not specify
+    // a custom easer.
+    private static var defaultEaser:IEaser = new Sine(.5); 
+
+    // Used to optimize event dispatching: only send out updated events if
+    // there is someone listening
+    private var numUpdateListeners:int = 0;
+    
+
+    //--------------------------------------------------------------------------
+    //
+    // Properties
+    //
+    //--------------------------------------------------------------------------
+
+    //----------------------------------
+    //  motionPaths
+    //----------------------------------
+    /**
+     * @private
+     * Storage for the motionPaths property. 
+     */
+    private var _motionPaths:Vector.<MotionPath>;
+    /**
+     * A Vector of MotionPath objects, each of which holds the
+     * name of a property being animated and the values that the property
+     * takes during the animation. 
+     * This Vector takes precedence over
+     * any properties declared in subclasses of Animate.
+     * For example, if this Array is set directly on a Move effect, 
+     * then any properties of the Move effect, such as <code>xFrom</code>, are ignored. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get motionPaths():Vector.<MotionPath>
+    {
+        return _motionPaths;
+    }
+    /**
+     * @private
+     */
+    public function set motionPaths(value:Vector.<MotionPath>):void
+    {
+        _motionPaths = value;
+    }
+    
+    //----------------------------------
+    //  easer
+    //----------------------------------
+    /**
+     * @private
+     * Storage for the easer property. 
+     */
+    private var _easer:IEaser = defaultEaser;
+    /**
+     * The easing behavior for this effect. 
+     * This IEaser object is used to convert the elapsed fraction of 
+     * the animation into an eased fraction, which is then used to
+     * calculate the value at that eased elapsed fraction.
+     * 
+     * <p>Note that it is possible to have easing at both the effect
+     * level and the Keyframe level (where Keyframes hold the values/times
+     * used in the MotionPath structures).
+     * These easing behaviors build on each other. 
+     * The <code>easer</code> controls the easing of the overall effect.
+     * The Keyframe controls the easing in any particular interval of the animation.
+     * By default, the easing for Animate is non-linear (Sine(.5)).
+     * The easing for Keyframes is linear. If you desire an effect with easing
+     * at the keyframe level instead, you can set the easing of the
+     * effect to linear, and then set the easing specifically on the Keyframes.</p>
+     * 
+     * @default spark.effects.easing.Sine(.5)
+     *
+     * @see spark.effects.easing.Sine
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get easer():IEaser
+    {
+        return _easer;
+    }
+    /**
+     * @private
+     */
+    public function set easer(value:IEaser):void
+    {
+        _easer = value;
+    }
+    
+    //----------------------------------
+    //  interpolator
+    //----------------------------------
+    /**
+     * @private
+     * Storage for the interpolator property. 
+     */
+    private var _interpolator:IInterpolator = null;
+    /**
+     * The interpolator used by this effect to calculate values between
+     * the start and end values of a property. 
+     * By default, the NumberInterpolator class handles interpolation 
+     * or, in the case of the start
+     * and end values being Arrays or Vectors, by the 
+     * MultiValueInterpolator class.
+     * Interpolation of other types, or of Numbers that should be interpolated
+     * differently, such as <code>uint</code> values that hold color
+     * channel information, can be handled by supplying a different
+     * interpolator.
+     *
+     *  @see spark.effects.interpolation.NumberInterpolator
+     *  @see spark.effects.interpolation.MultiValueInterpolator
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get interpolator():IInterpolator
+    {
+        return _interpolator;
+    }
+    /**
+     * @private
+     */
+    public function set interpolator(value:IInterpolator):void
+    {
+        _interpolator = value;
+    }
+
+    //----------------------------------
+    //  repeatBehavior
+    //----------------------------------
+    /**
+     * @private
+     * Storage for the repeatBehavior property. 
+     */
+    private var _repeatBehavior:String = RepeatBehavior.LOOP;
+    
+    [Inspectable(category="General", enumeration="loop,reverse", defaultValue="loop" )]
+    
+    /**
+     * The behavior of a repeating effect, which means an effect
+     * with <code>repeatCount</code> equal to either 0 or &gt; 1. This
+     * value should be either <code>RepeatBehavior.LOOP</code>, which means the animation
+     * repeats in the same order each time, or <code>RepeatBehavior.REVERSE</code>,
+     * which means the animation reverses direction on each iteration.
+     * 
+     * @default RepeatBehavior.LOOP
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get repeatBehavior():String
+    {
+        return _repeatBehavior;
+    }
+    /**
+     * @private
+     */
+    public function set repeatBehavior(value:String):void
+    {
+        _repeatBehavior = value;
+    }
+    
+    //----------------------------------
+    //  disableLayout
+    //----------------------------------
+    /**
+     * @private
+     * Storage for the disableLayout property. 
+     */
+    private var _disableLayout:Boolean = false;
+    /**
+     * If <code>true</code>, the effect disables layout on its
+     * targets' parent containers, setting the containers <code>autoLayout</code>
+     * property to false, and also disables any layout constraints on the 
+     * target objects. These properties are restored when the effect
+     * finishes.
+     * 
+     * @default false
+     *  
+     * @langversion 3.0
+     * @playerversion Flash 10
+     * @playerversion AIR 1.5
+     * @productversion Flex 4
+     */
+    public function get disableLayout():Boolean
+    {
+        return _disableLayout;
+    }
+    /**
+     * @private
+     */
+    public function set disableLayout(value:Boolean):void
+    {
+        _disableLayout = value;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    // Methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private 
+     */
+    override public function getAffectedProperties():Array /* of String */
+    {
+        if (!affectedProperties)
+        {
+            if (motionPaths)
+            {
+                var horizontalMove:Boolean;
+                var verticalMove:Boolean;
+                affectedProperties = new Array();
+                for (var i:int = 0; i < motionPaths.length; ++i)
+                {
+                    var effectHolder:MotionPath = MotionPath(motionPaths[i]);
+                    affectedProperties.push(effectHolder.property);
+                    // Some properties side-affect others: add them to the list
+                    switch (effectHolder.property)
+                    {
+                        // left/right/top/bottom side-effect x/y/width/height
+                        case "left":
+                        case "right":
+                        case "horizontalCenter":
+                            horizontalMove = true;
+                            break;
+                        case "top":
+                        case "bottom":
+                        case "verticalCenter":
+                            verticalMove = true;
+                            break;
+                        // The next two let us capture the explicit values, so that we
+                        // can restore them if necessary when the effect ends
+                        case "width":
+                            if (affectedProperties.indexOf("explicitWidth") < 0)
+                                affectedProperties.push("explicitWidth");
+                            if (affectedProperties.indexOf("percentWidth") < 0)
+                                affectedProperties.push("percentWidth");
+                            break;
+                        case "height":
+                            if (affectedProperties.indexOf("explicitHeight") < 0)
+                                affectedProperties.push("explicitHeight");
+                            if (affectedProperties.indexOf("percentHeight") < 0)
+                                affectedProperties.push("percentHeight");
+                            break;
+                    }
+                }
+                if (horizontalMove)
+                {
+                    if (affectedProperties.indexOf("x") < 0)
+                        affectedProperties.push("x");
+                    if (affectedProperties.indexOf("width") < 0)
+                        affectedProperties.push("width");
+                    if (affectedProperties.indexOf("explicitWidth") < 0)
+                        affectedProperties.push("explicitWidth");
+                }
+                if (verticalMove)
+                {
+                    if (affectedProperties.indexOf("y") < 0)
+                        affectedProperties.push("y");
+                    if (affectedProperties.indexOf("height") < 0)
+                        affectedProperties.push("height");
+                    if (affectedProperties.indexOf("explicitHeight") < 0)
+                        affectedProperties.push("explicitHeight");
+                }
+            }
+            else
+            {
+                affectedProperties = [];
+            }
+        }
+        return affectedProperties;
+    }
+    
+    /**
+     *  @private 
+     */
+    override public function get relevantStyles():Array /* of String */
+    {
+        if (!_relevantStyles)
+        {
+            if (motionPaths)
+            {
+                _relevantStyles = new Array(motionPaths.length);
+                for (var i:int = 0; i < motionPaths.length; ++i)
+                {
+                    var effectHolder:MotionPath = MotionPath(motionPaths[i]);
+                    _relevantStyles[i] = effectHolder.property;
+                }
+            }
+            else
+            {
+                _relevantStyles = [];
+            }
+        }
+        return _relevantStyles;
+    }
+    
+    /**
+     * @private
+     */
+    override protected function initInstance(instance:IEffectInstance):void
+    {
+        super.initInstance(instance);
+        
+        var animateInstance:AnimateInstance = AnimateInstance(instance);
+
+        //animateInstance.addEventListener(EffectEvent.EFFECT_REPEAT, animationEventHandler);
+        // Optimization: don't bother listening for update events if we don't have
+        // any listeners for that event
+        //if (numUpdateListeners > 0)
+            //animateInstance.addEventListener(EffectEvent.EFFECT_UPDATE, animationEventHandler);
+
+        if (easer)
+            animateInstance.easer = easer;
+            
+        if (interpolator)
+            animateInstance.interpolator = interpolator;
+        
+        //if (isNaN(repeatCount))
+            //animateInstance.repeatCount = repeatCount;
+            
+        animateInstance.repeatBehavior = repeatBehavior;
+        animateInstance.disableLayout = disableLayout;
+        
+        if (motionPaths)
+        {
+            animateInstance.motionPaths = new Vector.<MotionPath>();
+            for (var i:int = 0; i < motionPaths.length; ++i)
+                animateInstance.motionPaths[i] = motionPaths[i].clone();
+        }
+    }
+    
+
+    /**
+     * @private
+     */
+    override protected function applyValueToTarget(target:Object, property:String, 
+                                          value:*, props:Object):void
+    {
+        if (property in target)
+        {
+            // The "property in target" test only tells if the property exists
+            // in the target, but does not distinguish between read-only and
+            // read-write properties. Put a try/catch around the setter and 
+            // ignore any errors.
+            try
+            {
+                target[property] = value;
+            }
+            catch(e:Error)
+            {
+                // Ignore errors
+            }
+        }
+    }
+
+    /**
+     * @private
+     * Track number of listeners to update event for optimization purposes
+     */
+    override public function addEventListener(type:String, handler:Function, opt_capture:Boolean = false, opt_handlerScope:Object = null):void
+    {
+        super.addEventListener(type, handler, opt_capture, opt_handlerScope);
+        //if (type == EffectEvent.EFFECT_UPDATE)
+            //++numUpdateListeners;
+    }
+    
+    /**
+     * @private
+     * Track number of listeners to update event for optimization purposes
+     */
+    override public function removeEventListener(type:String, handler:Function, opt_capture:Boolean = false, opt_handlerScope:Object = null):void
+    {
+        super.removeEventListener(type, handler, opt_capture, opt_handlerScope);
+        //if (type == EffectEvent.EFFECT_UPDATE)
+            //--numUpdateListeners;
+    }
+    
+    /**
+     * @private
+     * Called when the AnimateInstance object dispatches an EffectEvent.
+     *
+     * @param event An event object of type EffectEvent.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private function animationEventHandler(event:EffectEvent):void
+    {
+        dispatchEvent(event);
+    }
+    
+    /**
+     * @private
+     * Tell the propertyChanges array to keep all values, unchanged or not.
+     * This enables us to check later, when the effect is finished, whether
+     * we need to restore explicit height/width values.
+     */
+    override mx_internal function captureValues(propChanges:Array,
+                                                setStartValues:Boolean, 
+                                                targetsToCapture:Array = null):Array
+    {
+        var propertyChanges:Array = 
+            super.captureValues(propChanges, setStartValues, targetsToCapture);
+
+        // If we're capturing explicitWidth/Height values, don't strip unchanging
+        // values from propertyChanges; we want to know whether these values should
+        // be restored when the effect ends
+        var explicitValuesCaptured:Boolean = 
+            getAffectedProperties().indexOf("explicitWidth") >= 0 ||
+            getAffectedProperties().indexOf("explicitHeight") >= 0;
+        if (explicitValuesCaptured && setStartValues)
+        {
+            var n:int = propertyChanges.length;
+            for (var i:int = 0; i < n; i++)
+            {
+                if (targetsToCapture == null || targetsToCapture.length == 0 ||
+                    targetsToCapture.indexOf(propertyChanges[i].target) >= 0)
+                {
+                    propertyChanges[i].stripUnchangedValues = false;
+                }
+            }
+        }
+        return propertyChanges;
+    }
+
+    /**
+     *  @private
+     *  After applying start values, check to see whether the values
+     *  contain percentWidth/percentHeight, which should be applied
+     *  after any width/height/explicitWidth/explicitHeight values.
+     */
+    override mx_internal function applyStartValues(propChanges:Array,
+                                                   targets:Array):void
+    {
+        super.applyStartValues(propChanges, targets);
+        // Special case for percentWidth/Height properties. If we are watching
+        // width/explicitWidth or height/explicitHeight values and also
+        // percentWidth/Height values, we have to make sure to apply the 
+        // percent values last to avoid having them get clobbered by
+        // applying the width/height values. We could either sort the
+        // propChanges array or just make sure the re-apply the percent
+        // values here, after we're done with the rest.
+        if (propChanges)
+        {
+            var n:int = propChanges.length;
+            for (var i:int = 0; i < n; i++)
+            {
+                var target:Object = propChanges[i].target;
+                if (propChanges[i].start["percentWidth"] !== undefined && 
+                    "percentWidth" in target)
+                {
+                    target.percentWidth = propChanges[i].start["percentWidth"];
+                }
+                if (propChanges[i].start["percentHeight"] !== undefined && 
+                    "percentHeight" in target)
+                {
+                    target.percentHeight = propChanges[i].start["percentHeight"];
+                }
+            }
+        }
+    }
+        
+    /**
+     * @private
+     * When we're done, check to see whether explicitWidth/Height values
+     * for the target are NaN in the end state. If so, we should restore
+     * them to that value. This ensures that the target will be sized by
+     * its layout manager instead of by the width/height set during
+     * the effect.
+     */
+    override mx_internal function applyEndValues(propChanges:Array,
+                                                 targets:Array):void
+    {
+        super.applyEndValues(propChanges, targets);
+        // Special case for animating width/height during a transition, because
+        // we may have clobbered the explicitWidth/Height values which otherwise 
+        // would not have been set. We need to restore these values plus any
+        // associated layout constraint values (percentWidth/Height)
+        if (propChanges)
+        {
+            var n:int = propChanges.length;
+            for (var i:int = 0; i < n; i++)
+            {
+                var target:Object = propChanges[i].target;
+                if (propChanges[i].end["explicitWidth"] !== undefined)
+                {
+                    if (isNaN(propChanges[i].end["explicitWidth"]) && 
+                        "explicitWidth" in target)
+                    {
+                        target.explicitWidth = NaN;
+                        if (propChanges[i].end["percentWidth"] !== undefined && 
+                            "percentWidth" in target)
+                        {
+                            target.percentWidth = propChanges[i].end["percentWidth"];
+                        }
+                    }
+                }
+                if (propChanges[i].end["explicitHeight"] !== undefined)
+                {
+                    if (isNaN(propChanges[i].end["explicitHeight"]) && 
+                        "explicitHeight" in target)
+                    {
+                        target.explicitHeight = NaN;
+                        if (propChanges[i].end["percentHeight"] !== undefined && 
+                            "percentHeight" in target)
+                        {
+                            target.percentHeight = propChanges[i].end["percentHeight"];
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/Animation.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/Animation.as
new file mode 100644
index 0000000..917231a
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/Animation.as
@@ -0,0 +1,1415 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects.animation
+{
+import org.apache.royale.events.Event;
+import org.apache.royale.utils.Timer;
+
+import mx.core.mx_internal;
+import mx.events.EffectEvent;
+import mx.resources.IResourceManager;
+import mx.resources.ResourceManager;
+
+import spark.effects.easing.IEaser;
+import spark.effects.easing.Linear;
+import spark.effects.easing.Sine;
+import spark.effects.interpolation.IInterpolator;
+
+use namespace mx_internal;
+
+[DefaultProperty("motionPaths")]
+
+//--------------------------------------
+//  Other metadata
+//--------------------------------------
+
+[ResourceBundle("sparkEffects")]
+
+/**
+ *  The Animation class defines an animation that happens between 
+ *  the start and end values of a property over a specified period of time.
+ *  The animation can be a change in position, such as performed by
+ *  the Move effect; a change in size, as performed by the Resize effect;
+ *  a change in visibility, as performed by the Fade effect; or other 
+ *  types of animations used by effects or run directly with the Animation class.
+ *
+ *  <p>This class defines the timing and value parts of the animation. 
+ *  Other code, either in effects or in application code, associates the animation
+ *  with target objects and properties, such that the animated values produced by
+ *  Animation class can then be applied to target objects and properties to actually
+ *  cause these objects to animate.</p>
+ *
+ *  <p>When defining animation effects, you typically create an
+ *  instance of the Animate class, or of a subclass of Animate. This creates
+ *  an Animation instance in the <code>play()</code> method. The Animation instance
+ *  accepts start and end values, a duration, and optional parameters such as
+ *  easer and interpolator objects.</p>
+ * 
+ *  <p>The Animation object calls event listeners at the start and end of the animation,
+ *  when the animation repeats, and at regular update intervals during
+ *  the animation. These calls pass values which the Animation instance calculated from
+ *  the start and end values and the easer and interpolator objects. These
+ *  values can then be used to set property values on target objects.</p>
+ *
+ *  @see spark.effects.Animate
+ *  @see spark.effects.supportClasses.AnimateInstance
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public final class Animation
+{
+    //include "../../core/Version.as";
+
+    //--------------------------------------------------------------------------
+    //
+    //  Class Constants
+    //
+    //--------------------------------------------------------------------------
+
+    private static const TIMER_RESOLUTION:Number = 1000 / 60;	// 60 fps
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Constructor. 
+     *  The optional <code>property</code>, <code>startValue</code>, and 
+     *  <code>endValue</code> parameters define a simple
+     *  animation with a single MotionPath object with two Keyframes. 
+     *  If either value is non-null,
+     *  <code>startValue</code> becomes the <code>value</code> of the
+     *  first keyframe, at time=0, and 
+     *  <code>endValue</code> becomes the <code>value</code> of 
+     *  the second keyframe, at the end of the animation.
+     * 
+     *  @param duration The length of the animation, in milliseconds.
+     *
+     *  @param property The property to animate.
+     * 
+     *  @param startValue The initial value of the property.
+     *
+     *  @param endValue The final value of the property.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function Animation(duration:Number = 500, property:String = null,
+        startValue:Object = null, endValue:Object = null)
+    {
+        this.duration = duration;
+        if (property != null && (startValue !== null || endValue !== null))
+        {
+            motionPaths = new <MotionPath>[new MotionPath(property)];
+            motionPaths[0].keyframes = new <Keyframe>[new Keyframe(0, startValue), 
+                new Keyframe(duration, endValue)];
+        }
+    }
+    
+
+    //--------------------------------------------------------------------------
+    //
+    //  Class variables
+    //
+    //--------------------------------------------------------------------------
+
+
+    /**
+     * @private
+     * 
+     * The time being used in the current frame calculations. This time is
+     * shared by all active animations.
+     */
+    private static var intervalTime:Number = NaN;
+
+    // A single Timer object runs all animations in the process
+    private static var activeAnimations:Vector.<Animation> = new Vector.<Animation>;
+    private static var timer:Timer = null;
+
+    /**
+     * @private
+     * Default easer used if easer is set to null
+     */
+    private static var linearEaser:IEaser;
+    
+    private var id:int = -1;
+    private var _doSeek:Boolean = false;
+    private var _isPlaying:Boolean = false;
+    private var _doReverse:Boolean = false;
+    private var _invertValues:Boolean = false;
+    // Original start time of animation
+    private var startTime:Number;
+    private var started:Boolean = false;
+    // Time when the current cycle started
+    private var cycleStartTime:Number;
+    // The amount of time that the animation should delay before
+    // starting. This is set to a non-negative number only when
+    // an Animation is paused during its startDelay phase
+    private var delayTime:Number = -1;
+    private static var defaultEaser:IEaser = new Sine(.5); 
+    private static var delayedStartAnims:Vector.<Animation> =
+        new Vector.<Animation>();
+    private static var delayedStartTimes:Object = new Object();
+
+    
+    /**
+     *  @private
+     *  Used for accessing localized Error messages.
+     */
+    private var resourceManager:IResourceManager =
+                                    ResourceManager.getInstance();
+    
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  An Object containing the calculated values as of the current frame 
+     *  of the Animation.
+     *  The values are stored as map values, using property names as the key.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var currentValue:Object;
+
+    /**
+     *  The set of MotionPath objects that define the properties and values
+     *  that the Animation will animate over time.
+     *
+     *  @see spark.effects.animation.MotionPath
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var motionPaths:Vector.<MotionPath>;
+    
+    //----------------------------------
+    //  animationTarget
+    //----------------------------------
+    /**
+     * @private
+     * Storage for the animationTarget property. 
+     */
+    private var _animationTarget:IAnimationTarget = null;
+    /**
+     *  The IAnimationTarget object notified with all
+     *  start, end, repeat, and update events for this animation.
+     *  A value of <code>null</code> indicates that there is no target 
+     *  to notify.
+     * 
+     *  @default null
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get animationTarget():IAnimationTarget
+    {
+        return _animationTarget;
+    }
+    public function set animationTarget(value:IAnimationTarget):void
+    {
+        _animationTarget = value;
+    }
+    
+    //----------------------------------
+    //  playheadTime
+    //----------------------------------
+    /**
+     * @private
+     * Storage for the animationTarget property. 
+     */
+    private var _playheadTime:Number;
+    
+    [Inspectable(minValue="0.0")]
+    
+    
+    /**
+     *  The total elapsed time of the animation, including any start delay
+     *  and repetitions. For an animation playing through its first cycle,
+     *  this value will equal that of <code>cycleTime</code>.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get playheadTime():Number
+    {
+        return _playheadTime + startDelay;
+    }
+    public function set playheadTime(value:Number):void
+    {
+        seek(value, true);
+    }
+    /**
+     *  If <code>true</code>, the animation is currently playing.
+     *  The value is <code>false</code> unless the animation
+     *  has been played and not yet stopped (either programmatically or
+     *  automatically) or paused.
+     *
+     *  @default false
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get isPlaying():Boolean
+    {
+        return _isPlaying;
+    }
+    
+    [Inspectable(minValue="0.0")]
+    
+    /**
+     *  The length of time, in milliseconds, of the animation,
+     *  not counting any repetitions defined by 
+     *  the <code>repeatCount</code> property.
+     * 
+     * @default 500
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var duration:Number = 500;
+
+    //----------------------------------
+    //  repeatBehavior
+    //----------------------------------
+    /**
+     * @private
+     * Storage for the repeatBehavior property. 
+     */
+    private var _repeatBehavior:String = RepeatBehavior.LOOP;
+    /**
+     *  Sets the behavior of a repeating animation.
+     *  A repeating animation has the 
+     *  <code>repeatCount</code> property set to 0 or to a value greater than 1. 
+     *  This value should be either <code>RepeatBehavior.LOOP</code>, 
+     *  meaning the animation repeats in the same order each time, or 
+     *  <code>RepeatBehavior.REVERSE</code>,
+     *  meaning the animation reverses direction for each iteration.
+     * 
+     *  @default RepeatBehavior.LOOP
+     *
+     *  @see spark.effects.animation.RepeatBehavior
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get repeatBehavior():String
+    {
+        return _repeatBehavior;
+    }
+    public function set repeatBehavior(value:String):void
+    {
+        _repeatBehavior = value;
+    }
+
+    //----------------------------------
+    //  repeatCount
+    //----------------------------------
+    
+    /**
+     * @private
+     * Storage for the repeatCount property. 
+     */
+    private var _repeatCount:int = 1;
+    
+    [Inspectable(minValue="0")]
+    
+    /**
+     *  The number of times that this animation repeats. 
+     *  A value of 0 means that it repeats indefinitely.
+     * 
+     *  @default 1
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function set repeatCount(value:int):void
+    {
+        _repeatCount = value;
+    }
+    public function get repeatCount():int
+    {
+        return _repeatCount;
+    }
+    
+    //----------------------------------
+    //  repeatDelay
+    //----------------------------------
+
+    /**
+     * @private
+     * Storage for the repeatDelay property. 
+     */
+    private var _repeatDelay:Number = 0;
+    
+    [Inspectable(minValue="0.0")]
+    
+    /**
+     *  The amount of time, in milliseconds, to delay before each repetition cycle
+     *  begins. Setting this value to a non-zero number 
+     *  ends previous animation cycle exactly at  its end value
+     *  However, non-delayed repetitions may skip over that 
+     *  value completely as the animation transitions smoothly from being
+     *  near the end of one cycle to being past the beginning of the next.
+     *  This property must be a value &gt;= 0.
+     *
+     *  <p>This property is used for the first repetition
+     *  after the first animation cycle. 
+     *  To delay the first cycle of the animation, use
+     *  the <code>startDelay</code> property. </p>
+     *
+     *  @see #startDelay
+     * 
+     *  @default 0
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function set repeatDelay(value:Number):void
+    {
+        _repeatDelay = value;
+    }
+    public function get repeatDelay():Number
+    {
+        return _repeatDelay;
+    }
+    
+    /**
+     * @private
+     * Storage for the startDelay property. 
+     */
+    private var _startDelay:Number = 0;
+    
+    [Inspectable(minValue="0.0")]
+    
+    /**
+     *  The amount of time spent waiting before the animation
+     *  begins.
+     *  This property must be a value &gt;= 0.
+     * 
+     *  @default 0
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function set startDelay(value:Number):void
+    {
+        _startDelay = value;
+    }
+    public function get startDelay():Number
+    {
+        return _startDelay;
+    }
+
+    //----------------------------------
+    //  interpolator
+    //----------------------------------
+
+    /**
+     *  The interpolator used by the Animation instance 
+     *  to calculate values between
+     *  the start and end values of the property. 
+     *  By default, the class uses the NumberInterpolator class or, 
+     *  in the case of the start and end values being arrays or Vectors, 
+     *  by the MultiValueInterpolator class.
+     *  Interpolation of other data types, or 
+     *  of Numbers that should be interpolated
+     *  differently, such as <code>uint</code> values that hold color
+     *  channel information, can be handled by supplying a different
+     *  interpolator.
+     *
+     *  @see spark.effects.interpolation.NumberInterpolator
+     *  @see spark.effects.interpolation.MultiValueInterpolator
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var interpolator:IInterpolator = null;
+    
+    //----------------------------------
+    //  cycleTime
+    //----------------------------------
+
+    private var _cycleTime:Number = 0;
+    /**
+     *  The current millisecond position in the current cycle animation.
+     *  This value is between 0 and <code>duration</code>.
+     *  An animation 'cycle' is defined as a single repetition of the animation,
+     *  where the <code>repeatCount</code> property defines the number of
+     *  cycles that will be played.
+     *  Use the <code>seek()</code> method to change the position of the animation.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get cycleTime():Number
+    {
+        return _cycleTime;
+    }
+
+    
+    //----------------------------------
+    //  cycleFraction
+    //----------------------------------
+
+    private var _cycleFraction:Number;
+    /**
+     *  The current fraction elapsed in the animation, after easing
+     *  has been applied. This value is between 0 and 1.
+     *  An animation 'cycle' is defined as a single repetition of the animation,
+     *  where the <code>repeatCount</code> property defines the number of
+     *  cycles that will be played.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get cycleFraction():Number
+    {
+        return _cycleFraction;
+    }
+
+    //----------------------------------
+    //  easer
+    //----------------------------------
+
+    /**
+     * @private
+     * Storage for the easer property.
+     */
+    private var _easer:IEaser = defaultEaser;
+    /**
+     *  The easing behavior for this effect. 
+     *  This IEaser object is used to convert the elapsed fraction of the animation 
+     *  into an eased fraction, which is then used to calculate 
+     *  the value at that eased elapsed fraction. 
+     * 
+     *  <p>A value of <code>null</code> means no easing is
+     *  used, which is equivalent to using a Linear ease, or
+     *  <code>animation.easer = Linear.getInstance();</code>.</p>
+     * 
+     *  @default Sine(.5)
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get easer():IEaser
+    {
+        return _easer;
+    }
+    /**
+     *  @private
+     */
+    public function set easer(value:IEaser):void
+    {
+        if (!value)
+        {
+            if (!linearEaser)
+                linearEaser = new Linear();
+            value = linearEaser;
+        }
+        _easer = value;
+    }
+    
+    //----------------------------------
+    //  playReversed
+    //----------------------------------
+
+    /**
+     * @private
+     * Storage for the playReversed property
+     */
+    private var _playReversed:Boolean;
+    /**
+     *  If <code>true</code>, play the animation in reverse.
+     *  If the animation is currently playing in the opposite
+     *  direction to the specified value of <code>playReversed</code>,
+     *  the animation will change direction dynamically.
+     *
+     *  @default false
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get playReversed():Boolean
+    {
+        return _playReversed;
+    }
+    /**
+     *  @private
+     */
+    public function set playReversed(value:Boolean):void
+    {
+        if (_isPlaying)
+        {
+            if (_invertValues != value)
+            {
+                _invertValues = value;
+                seek(duration - _cycleTime, true);
+            }
+        }
+        _doReverse = value;
+        _playReversed = value;
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Adds a new animation instance to the system. 
+     *  All animations run off the same Timer instance, 
+     *  so starting any one animation simply adds it onto the
+     *  static list of active animations.
+     *
+     *  @param animation The Animation object.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private static function addAnimation(animation:Animation):void
+    {
+        
+        // Because of how autoCenterTransform works, depending on the
+        // current size of the object for a correct calculation, we
+        // should ensure that any size-changing animations are processed
+        // first in each frame, so each new animation gets inserted at the
+        // front it if deals with width or height. Note that this will
+        // only work for Resize effects, which only animate width/height.
+        // We do not exhaustively search all motionPaths of any given animation.
+        if (animation.motionPaths && animation.motionPaths.length > 0 &&
+            animation.motionPaths[0] &&
+            (animation.motionPaths[0].property == "width" ||
+             animation.motionPaths[0].property == "height"))
+        {
+            activeAnimations.splice(0, 0, animation);
+            animation.id = 0;
+            for (var i:int = 1; i < activeAnimations.length; ++i)
+                Animation(activeAnimations[i]).id = i;
+        }
+        else
+        {
+            animation.id = activeAnimations.length;
+            activeAnimations.push(animation);
+        }
+        
+        if (!timer)
+        {
+            //Timeline.pulse();
+            timer = new Timer(TIMER_RESOLUTION);
+            timer.addEventListener("timer", timerHandler);
+            timer.start();
+        }
+        
+        //intervalTime = Timeline.currentTime;
+
+        animation.cycleStartTime = intervalTime;
+    }
+
+    private static function removeAnimationAt(index:int):void
+    {
+        if (index >= 0 && index < activeAnimations.length)
+        {
+            activeAnimations.splice(index, 1);
+                    
+            var n:int = activeAnimations.length;
+            for (var i:int = index; i < n; i++)
+            {
+                var curAnimation:Animation = Animation(activeAnimations[i]);
+                curAnimation.id--;
+            }
+        }
+        stopTimerIfDone();
+    }
+
+    /**
+     *  @private
+     */
+    private static function removeAnimation(animation:Animation):void
+    {
+        removeAnimationAt(animation.id);
+    }
+    
+    private static function timerHandler(event:Event):void
+    {
+        var oldTime:Number = intervalTime;
+        //intervalTime = Timeline.pulse();
+        
+        var n:int = activeAnimations.length;
+        var i:int = 0;
+        
+        while (i < activeAnimations.length)
+        {
+            // only increment index into array if no animation was stopped
+            // as a result to call to doInterval(). Stopped animations
+            // will be removed from the array and everything after them
+            // shifts down
+            var incrementIndex:Boolean = true;
+            var animation:Animation = Animation(activeAnimations[i]);
+            if (animation)
+                incrementIndex = !animation.doInterval();
+            if (incrementIndex)
+                ++i;
+        }
+        
+        // Check to see whether it's time to start any delayed animations
+        while (delayedStartAnims.length > 0)
+        {
+            // This loop will either start() an animation, which removes it
+            // from delayedStartAnims, or it will break out. In either case,
+            // we only check against the first item in the list each time
+            // through because any previous iteration will have removed the
+            // item that was at index 0
+            var anim:Animation = Animation(delayedStartAnims[0]);
+            var animStartTime:Number = delayedStartTimes[anim];
+            // Keep starting animations unless our sorted lists return
+            // animations that start past the current time
+            //if (animStartTime < Timeline.currentTime)
+                //if (anim.playReversed)
+                    //anim.end();
+                //else
+                    //anim.start();
+            //else
+                //break;
+        }
+        //event.updateAfterEvent();
+    }
+
+    /**
+     * @private
+     * 
+     * Calculates the time and elapsed fraction, then gets the
+     * appropriate interpolated value at that fraction, then sends out
+     * the animation event to all listeners.
+     *  
+     * Returns true if the animation has ended.
+     */
+    private function doInterval():Boolean
+    {
+        var animationEnded:Boolean = false;
+        var repeated:Boolean = false;
+                
+        if (_isPlaying || _doSeek)
+        {
+            
+            var currentTime:Number = intervalTime - cycleStartTime;
+            _playheadTime = intervalTime - startTime;
+            if (currentTime >= duration)
+            { 
+                // numRepeats reflects the current repetition cycle that we're going to
+                // be in, once we repeat.
+                var numRepeats:int = 2;
+                if ((duration + repeatDelay) > 0)
+                    numRepeats += (_playheadTime - duration) / (duration + repeatDelay);
+                if (repeatCount == 0 || numRepeats <= repeatCount)
+                {
+                    if (repeatDelay == 0)
+                    {
+                        _cycleTime = currentTime % duration;
+                        cycleStartTime = intervalTime - _cycleTime;
+                        currentTime = _cycleTime;                        
+                        if (repeatBehavior == RepeatBehavior.REVERSE)
+                            _invertValues = !_invertValues;
+                        repeated = true;
+                    }
+                    else
+                    {
+                        if (_doSeek)
+                        {
+                            _cycleTime = currentTime % (duration + repeatDelay);
+                            if (_cycleTime > duration)
+                                _cycleTime = duration; // must be in repeatDelay phase
+                            calculateValue(_cycleTime);
+                            sendUpdateEvent();
+                            return false;
+                        }
+                        else
+                        {
+                            // repeatDelay: send out a final update for this cycle with the
+                            // end value, then schedule a timer to wake up and
+                            // start the next cycle
+                            _cycleTime = duration;
+                            calculateValue(_cycleTime);
+                            sendUpdateEvent();
+                            removeAnimation(this);
+                            var delayTimer:Timer = new Timer(repeatDelay, 1);
+                            delayTimer.addEventListener("timer", repeat);
+                            delayTimer.start();
+                            return false;
+                        }
+                    }
+                }
+                else if (currentTime > duration)
+                {
+                    currentTime = duration;
+                    _playheadTime = duration;
+                }
+            }
+            _cycleTime = currentTime;
+            
+            calculateValue(currentTime);
+
+            if (currentTime >= duration && !_doSeek)
+            {
+                if (!playReversed || startDelay == 0)
+                {
+                    end();
+                    animationEnded = true;
+                }
+                else
+                {
+                    stopAnimation();
+                    addToDelayedAnimations(startDelay);
+                }
+            }
+            else
+            {
+                //if (repeated)
+                    //sendAnimationEvent(EffectEvent.EFFECT_REPEAT);
+                sendUpdateEvent();
+            }
+        }
+        return animationEnded;
+    }
+    
+    /**
+     * Utility function for dispatching an update event to the 
+     * animationTarget. This is a separate function for performance
+     * reasons; don't want to bother switching on the event type for the
+     * common case of update events.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private function sendUpdateEvent():void
+    {
+        if (_animationTarget)
+            _animationTarget.animationUpdate(this);
+    }
+
+    /**
+     * Utility function for dispatching a specified event to
+     * the animationTarget.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private function sendAnimationEvent(eventType:String):void
+    {
+        if (_animationTarget)
+            switch (eventType) {
+                case EffectEvent.EFFECT_START:
+                    _animationTarget.animationStart(this);
+                    break;
+                case EffectEvent.EFFECT_END:
+                    _animationTarget.animationEnd(this);
+                    break;
+                case EffectEvent.EFFECT_STOP:
+                    _animationTarget.animationStop(this);
+                    break;
+                //case EffectEvent.EFFECT_REPEAT:
+                    //_animationTarget.animationRepeat(this);
+                    //break;
+                //case EffectEvent.EFFECT_UPDATE:
+                    //// here for completeness; usually handled in sendUpdateEvent
+                    //_animationTarget.animationUpdate(this);
+                    //break;
+            }
+    }
+    
+    /**
+     * @private
+     * 
+     * Calculates all values for this animation for the elapsed time
+     */
+    private function calculateValue(currentTime:Number):void
+    {
+        var i:int;
+        
+        currentValue = new Object();
+        if (duration == 0)
+        {
+            for (i = 0; i < motionPaths.length; ++i)
+            {
+                currentValue[motionPaths[i].property] = 
+                    _invertValues ? 
+                    motionPaths[i].keyframes[0].value :
+                    motionPaths[i].keyframes[motionPaths[i].keyframes.length - 1].value;
+            }
+            return;
+        }
+    
+        if (_invertValues)
+            currentTime = duration - currentTime;
+    
+        _cycleFraction = easer.ease(currentTime/duration);
+
+        if (motionPaths)
+            for (i = 0; i < motionPaths.length; ++i)
+                currentValue[motionPaths[i].property] = 
+                    motionPaths[i].getValue(_cycleFraction);
+    }
+
+    /**
+     * Remove this animation from the list of pending animations,
+     * as appropriate
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private function removeFromDelayedAnimations():void
+    {
+        if (delayedStartTimes[this])
+        {
+            var animPendingTime:int = delayedStartTimes[this];
+            for (var i:int = 0; i < delayedStartAnims.length; ++i)
+            {
+                if (delayedStartAnims[i] == this)
+                {
+                    delayedStartAnims.splice(i, 1);
+                    break;
+                }
+            }
+            delete delayedStartTimes[this];
+        }
+    }
+
+    /**
+     *  Interrupts the animation, jumps immediately to the end of the animation, 
+     *  and calls the animationEnd() function on the <code>animationTarget</code>.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function end():void
+    {
+        // TODO (chaase): call removal utility instead of this code
+        // Make sure to remove any references on the delayed lists
+        if (startDelay > 0 && delayedStartAnims.length > 0)
+        {
+            for (var i:int = 0; i < delayedStartAnims.length; ++i)
+            {
+                if (this == delayedStartAnims[i])
+                {
+                    delete delayedStartTimes[this];
+                    delayedStartAnims.splice(i, 1);
+                    break;
+                }
+            }
+        }
+
+        // Note that we are potentially sending out this event to effects
+        // whose Animation is not yet running (for example, if we're autoReversing
+        // a transition and then skipping past the first effect). 
+        // This could mean that the end value is not yet initialized, so
+        // interpolators should be written to just send back the end value
+        // instead of trying to calculate it for the end time.
+        if (!started)
+            sendAnimationEvent(EffectEvent.EFFECT_START);
+        if (repeatCount > 1 && repeatBehavior == "reverse" && (repeatCount % 2 == 0))
+            _invertValues = true;
+
+        // We don't want to update to final values when playing in 
+        // reverse with a start delay.
+        if (!(_doReverse && startDelay > 0))
+        {
+            calculateValue(duration);
+            sendUpdateEvent();
+        }
+
+        sendAnimationEvent(EffectEvent.EFFECT_END);
+
+        // The rest of what we need to do is handled by stopAnimation()
+        if (isPlaying)
+            stopAnimation();
+        else
+            stopTimerIfDone();
+    }
+    
+    /**
+     * @private
+     * If no more animations running or pending, stop the timer
+     */
+    private static function stopTimerIfDone():void
+    {
+        if (timer && activeAnimations.length == 0 && delayedStartAnims.length == 0)
+        {
+            intervalTime = NaN;
+            timer.reset();
+            timer = null;
+        }
+    }
+    
+    private function addToDelayedAnimations(timeToDelay:Number):void
+    {
+        // Run timer if it's not currently running
+        if (!timer)
+        {
+            //Timeline.pulse();
+            timer = new Timer(TIMER_RESOLUTION);
+            timer.addEventListener("timer", timerHandler);
+            timer.start();
+        }
+        //var animStartTime:int = Timeline.currentTime + timeToDelay;
+        //var insertIndex:int = -1;
+        //for (var i:int = 0; i < delayedStartAnims.length; ++i)
+        //{
+            //var timeAtIndex:int = 
+                //delayedStartTimes[delayedStartAnims[i]];
+            //if (animStartTime < timeAtIndex)
+            //{
+                //insertIndex = i;
+                //break;
+            //}
+        //}
+        //if (insertIndex >= 0)
+            //delayedStartAnims.splice(insertIndex, 0, this);
+        //else
+            //delayedStartAnims.push(this);
+        //delayedStartTimes[this] = animStartTime;
+    }
+
+    /**
+     *  Start the animation. 
+     *  If the animation is already playing, it
+     *  is stopped first, then played.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function play():void
+    {
+        // stop an already-playing animation first
+        stopAnimation();
+        
+        // Make sure the time values in our motion paths are reasonable
+        // SimpleMotionPath objects may be set up with no time values, so
+        // we have to fill in those values from the duration when necessary
+        var i:int;
+        var j:int;
+        for (i = 0; i < motionPaths.length; ++i)
+        {
+            var keyframes:Vector.<Keyframe> = motionPaths[i].keyframes;
+            if (isNaN(keyframes[0].time))
+                keyframes[0].time = 0;
+            // Create an initial interval if necessary; this holds the property
+            // at it's current value until the first real keyframe is reached.
+            // This situation can occur when an effect has a startDelay that causes
+            // the first keyframe to have a nonzero time value.
+            else if (keyframes[0].time > 0)
+            {
+                var startTime:Number = keyframes[0].time;
+                keyframes.splice(0, 0, new Keyframe(0, null));
+                keyframes.splice(1, 0, new Keyframe(startTime-1, null));
+                if (playReversed)
+                {
+                    // Want to hold *next* value when playing backwards
+                    keyframes[0].value = keyframes[2].value;
+                    keyframes[1].value = keyframes[2].value;
+                }
+            }
+            for (j = 1; j < keyframes.length; ++j)
+            {
+                if (isNaN(keyframes[j].time))
+                    keyframes[j].time = duration;
+            }
+        }
+        for (i = 0; i < motionPaths.length; ++i)
+            motionPaths[i].scaleKeyframes(duration);
+        
+        if (_doReverse)
+            _invertValues = true;
+
+        if (startDelay > 0 && !playReversed)
+            addToDelayedAnimations(startDelay);
+        else
+            start();
+    }
+    
+    /**
+     *  @private
+     *  
+     *  Advances the animation to the specified position. 
+     *
+     *  @param playheadTime The position, in milliseconds, between 0
+     *  and the value of the <code>duration</code> property.
+     *
+     *  @param includeStartDelay Set to <code>true</code> if there is 
+     *  a start delay on the effect, as defined by the 
+     *  <code>startDelay</code> property.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */ 
+    private function seek(playheadTime:Number, includeStartDelay:Boolean = false):void
+    {
+        // Set value between 0 and duration
+        //playheadTime = Math.min(Math.max(playheadTime, 0), duration);
+        
+        // Reset the start time
+        // TODO (chaase): Redundant for cases that set this again below
+        // Should only do this for playing animation, as the stopped animations
+        // do it for themselves
+        startTime = cycleStartTime = intervalTime - playheadTime;
+        _doSeek = true;
+        
+        if (!_isPlaying || playReversed)
+        {
+            var isPlayingTmp:Boolean = _isPlaying;
+            //intervalTime = Timeline.currentTime;
+            if (includeStartDelay && startDelay > 0)
+            {
+                if (delayedStartTimes[this])
+                {
+                    // TODO (chaase): refactor removal/addition into utility functions
+                    // Still sleeping - reduce the delay time by the seek time
+                    var animPendingTime:int = delayedStartTimes[this];
+                    for (var i:int = 0; i < delayedStartAnims.length; ++i)
+                    {
+                        if (delayedStartAnims[i] == this)
+                        {
+                            delayedStartAnims.splice(i, 1);
+                            break;
+                        }
+                    }
+                    delete delayedStartTimes[this];
+                    var postDelaySeekTime:Number = playheadTime - startDelay;
+                    // also subtract out duration if playing in reverse because
+                    if (playReversed)
+                        postDelaySeekTime -= duration;
+                    if (postDelaySeekTime < 0)
+                    {
+                        animPendingTime = intervalTime + (startDelay - playheadTime);
+                        // add it back into the array in the proper order
+                        var insertIndex:int = -1;
+                        for (i = 0; i < delayedStartAnims.length; ++i)
+                        {
+                            if (animPendingTime < delayedStartTimes[delayedStartAnims[i]])
+                            {
+                                insertIndex = i;
+                                break;
+                            }
+                        }
+                        if (insertIndex >= 0)
+                            delayedStartAnims.splice(insertIndex, 0, this);
+                        else
+                            delayedStartAnims.push(this);
+                        delayedStartTimes[this] = animPendingTime;
+                        return;
+                    }
+                    else
+                    {
+                        // reduce seek time by startTime; we will go ahead and
+                        // seek into the now-playing animation by that much
+                        playheadTime -= startDelay;
+                        if (!isPlaying)
+                            start();
+                        startTime = cycleStartTime = intervalTime - playheadTime;
+                        doInterval();
+                        _doSeek = false;
+                        return;
+                    }
+                }
+            }
+            if (!isPlayingTmp)
+            {
+                // start/end values only valid after animation starts 
+                sendAnimationEvent(EffectEvent.EFFECT_START);
+                setupInterpolation();
+            }
+            startTime = cycleStartTime = intervalTime - playheadTime;
+        }
+        
+        doInterval();
+        _doSeek = false;
+    }
+
+    /**
+     *  Sets up interpolation for the animation. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private function setupInterpolation():void
+    {
+        if (interpolator && motionPaths)
+            for (var i:int = 0; i < motionPaths.length; ++i)
+                motionPaths[i].interpolator = interpolator;
+    }
+ 
+    // TODO (chaase): should eventually remove this function, since it
+    // overlaps with playReverse property. Just leaving it mx_internal
+    // for now to avoid perturbing the code too much
+    /**
+     *  @private
+     * 
+     *  Plays the effect in reverse,if the effect is currently playing,
+     *  starting from the current position of the effect.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    mx_internal function reverse():void
+    {
+        if (_isPlaying)
+        {
+            _doReverse = false;
+            seek(duration - _cycleTime);
+            _invertValues = !_invertValues;
+        }
+        else
+        {
+            _doReverse = !_doReverse;
+        }
+    }
+    
+    /**
+     *  @private
+     *  Method is used to force the animation timeline to update its current
+     *  time.
+     */ 
+    mx_internal static function pulse():void
+    {
+        //if (timer)
+            //Timeline.pulse();
+    }
+    
+    /**
+     *  Pauses the effect until the <code>resume()</code> method is called.
+     *  If <code>stop()</code> is called before <code>resume()</code>, then
+     *  the animation cannot be resumed.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function pause():void
+    {
+        var animPendingTime:Number = delayedStartTimes[this];
+        if (!isNaN(animPendingTime))
+        {
+            //delayTime = animPendingTime - Timeline.currentTime;
+            removeFromDelayedAnimations();
+        }
+        _isPlaying = false;
+    }
+
+    /**
+     * @private
+     * 
+     * Called by stop(), but also other places where we simply
+     * want to stop the animation without sending out the stop()
+     * event.
+     */
+    private function stopAnimation():void
+    {
+        removeFromDelayedAnimations();
+        // If animation has been added, id >= 0
+        // but if duration = 0, this might not be the case.
+        if (id >= 0)
+        {
+            Animation.removeAnimationAt(id);
+            id = -1;
+            _invertValues = false;
+            _isPlaying = false;
+        }
+    }
+    /**
+     *  Stops the animation, ending it without calling the <code>end()</code> 
+     *  method. The animationStop() function on the <code>animationTarget</code>
+     *  will be called.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function stop():void
+    {
+        stopAnimation();
+        sendAnimationEvent(EffectEvent.EFFECT_STOP);
+    }
+    
+    /**
+     *  Resumes the effect after it has been paused 
+     *  by a call to the <code>pause()</code> method. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function resume():void
+    {
+        _isPlaying = true;
+
+        if (delayTime >= 0)
+        {
+            addToDelayedAnimations(delayTime);
+        }
+        else
+        {
+            cycleStartTime = intervalTime - _cycleTime;
+            startTime = intervalTime - _playheadTime;
+            if (_doReverse)
+            {
+                reverse();
+                _doReverse = false;
+            }
+        }
+    }
+    
+
+    //--------------------------------------------------------------------------
+    //
+    //  Event handlers
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     * 
+     * Called by a Timer after repeatDelay has elapsed for a given
+     * repetition cycle. This causes the animation to send out an initial 
+     * value at the starting point, just as if the animation were just starting
+     * out.
+     */
+    private function repeat(event:Event = null):void
+    {
+        if (repeatBehavior == RepeatBehavior.REVERSE)
+            _invertValues = !_invertValues;
+        calculateValue(0);
+        //sendAnimationEvent(EffectEvent.EFFECT_REPEAT);
+        //sendUpdateEvent();
+        Animation.addAnimation(this);
+    }
+    
+    /**
+     * Called by play() or by a Timer, if startDelay is nonzero. This
+     * method initializes any necessary default state and adds the animation
+     * to the list of active animations, which starts it actually running.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private function start(event:Event = null):void
+    {
+        // actualStartTime accounts for overrun in desired startDelay
+        var actualStartTime:int = 0;
+        
+        // TODO (chaase): call removal utility instead of this code
+        // Make sure to remove any references on the delayed lists
+        if (!playReversed)
+            for (var i:int = 0; i < delayedStartAnims.length; ++i)
+            {
+                if (this == delayedStartAnims[i])
+                {
+                    //var animStartTime:int = int(delayedStartTimes[this]);
+                    //var overrun:int = Timeline.currentTime - animStartTime;
+                    //if (overrun > 0)
+                        //actualStartTime = Math.min(overrun, duration);
+                    //delete delayedStartTimes[this];
+                    //delayedStartAnims.splice(i, 1);
+                    //break;
+                }
+            }
+        sendAnimationEvent(EffectEvent.EFFECT_START);
+        
+        // start/end values may be changed by Animate (set dynamically),
+        // so now we set up our interpolator based on the real values
+        setupInterpolation();
+        
+        calculateValue(0);
+
+        // TODO (rfrishbe): if the animation gets stopped() or ended() in 
+        // the first update, then the animation never actually gets removed
+        sendUpdateEvent();
+        Animation.addAnimation(this);
+        startTime = cycleStartTime;
+        _isPlaying = true;
+        if (actualStartTime > 0)
+            seek(actualStartTime);
+
+        started = true;
+    }
+
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/IAnimationTarget.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/IAnimationTarget.as
new file mode 100644
index 0000000..e326ca9
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/IAnimationTarget.as
@@ -0,0 +1,113 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects.animation
+{
+/**
+ *  The IAnimationTarget interface is implemented by classes that support 
+ *  the events for an Animation instance.
+ *
+ *  @see spark.effects.animation.Animation
+ * 
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public interface IAnimationTarget
+{
+    /**
+     *  Called when an Animation instance starts. If there
+     *  is a <code>startDelay</code> on the Animation, this function is called
+     *  after that delay.
+     *
+     *  @param animation The Animation object.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    function animationStart(animation:Animation):void;
+
+    /**
+     *  Called when an Animation instance stops. 
+     *  This is different than <code>animationEnd()</code> method, 
+     *  which is called when the animation ends,
+     *  automatically setting the end values of the targets. 
+     *  The <code>animationStop()</code> method
+     *  is called when an animation is stopped where it's at.
+     *  Handling this event allows necessary cleanup when the animation
+     *  is interrupted.
+     *
+     *  @param animation The Animation object.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    function animationStop(animation:Animation):void;
+
+    /**
+     *  Called when an Animation instance ends.
+     *
+     *  @param animation The Animation object.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    function animationEnd(animation:Animation):void;
+
+    /**
+     *  Called when an Animation instance repeats. 
+     *  The Animation instance must have a <code>repeatCount</code> equal to 0 
+     *  (infinitely repeating) or a value greater than 1.
+     *
+     *  @param animation The Animation object.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    function animationRepeat(animation:Animation):void;
+
+    /**
+     *  Called during every update of an Animation instance.
+     *  If an implementation class is listening to an Animation specifically to
+     *  be able to do something after the Animation values are calculated for
+     *  a given time, this is the function in which those values should be used.
+     *  The other methods in this interface are more informational. 
+     *  They tell the listeners when the Animation starts, stops, or repeats.
+     *  This method is called when values have been calculated and something can be
+     *  done with them. 
+     *
+     *  @param animation The Animation object.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    function animationUpdate(animation:Animation):void;
+    
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/Keyframe.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/Keyframe.as
new file mode 100644
index 0000000..b53c3f9
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/Keyframe.as
@@ -0,0 +1,223 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package spark.effects.animation
+{
+import mx.core.mx_internal;
+
+import spark.effects.easing.IEaser;
+import spark.effects.easing.Linear;
+
+use namespace mx_internal;
+
+/**
+ *  The Keyframe class defines the value of a property at a specific time during an effect. 
+ *  For example, you can create three keyframes that define the value of a property at 
+ *  the beginning of the effect, at the midpoint of the effect, and at the end of the effect. 
+ *  The effect animates the property change on the target from keyframe to keyframe 
+ *  over the effect duration.
+ *
+ *  <p>The collection of keyframes for an effect is called the effect's motion path. 
+ *  A motion path can define any number of keyframes. 
+ *  The effect then calculates the value of the property by interpolating between 
+ *  the values specified by two key frames. </p>
+ * 
+ *  <p>Use the MotionPath class to hold the collection of Keyframe objects that 
+ *  represent the motion path of the effect.
+ *  The MotionPath class specifies the name of the property on the target, 
+ *  and the collection of Keyframes objects specify the values of the property at different
+ *  times during the effect.</p>
+ *  
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:Keyframe&gt;</code> tag
+ *  inherits the tag attributes of its superclass,
+ *  and adds the following tag attributes:</p>
+ *  
+ *  <pre>
+ *  &lt;s:Keyframe 
+ *    id="ID"
+ *    easier="Linear"
+ *    time="val"
+ *    value="val"
+ *    valueBy="val"
+ *  /&gt;
+ *  </pre>
+ * 
+ *  @see MotionPath
+ *
+ *  @includeExample examples/KeyFrameEffectExample.mxml
+ * 
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class Keyframe
+{
+    //include "../../core/Version.as";
+
+    /**
+     *  Constructor.
+     * 
+     *  @param time The time, in milliseconds, at which the effect target
+     *  of this keyframe should have the value
+     *  specified by the <code>value</code> parameter.
+     *
+     *  @param value The value that the effect target should have
+     *  at the given <code>time</code>.
+     *
+     *  @param valueBy Optional parameter which, if provided, 
+     *  causes <code>value</code> to be calculated dynamically by
+     *  adding <code>valueBy</code> to the <code>value</code> of
+     *  the previous keyframe in the set of keyframes in a MotionPath
+     *  object. This value is ignored if this is the first
+     *  Keyframe in a sequence.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function Keyframe(time:Number = NaN, 
+        value:Object = null, valueBy:Object = null)
+    {
+        this.value = value;
+        this.time = time;
+        this.valueBy = valueBy;
+    }
+    
+    /**
+     *  Returns a copy of this Keyframe object.
+     *
+     *  @return A copy of this Keyframe object.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function clone():Keyframe
+    {
+        var kf:Keyframe = new Keyframe(time, value, valueBy);
+        kf.easer = easer;
+        kf.timeFraction = timeFraction;
+        return kf;
+    }
+    
+    /**
+     *  The value that the property of the effect target should have
+     *  at the time specified by the <code>time</code> property.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var value:Object;
+    
+    [Inspectable(minValue="0.0")]
+    
+    /**
+     *  The time, in milliseconds, at which the effect target
+     *  for this keyframe should have the value
+     *  specified by the <code>value</code> property. This time
+     *  is relative to the starting time of the effect defined
+     *  with this keyframe.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var time:Number;
+    
+    /**
+     *  @private
+     * 
+     *  The time, represented as a fraction of an animation's
+     *  total duration. This value should be a value between 0
+     *  and 1. This value is calculated automatically prior to 
+     *  playing the animation. It simplifies the API for users (who
+     *  are able to use absolute time values) and the implementation
+     *  (because we can work on fractional values directly) and is not
+     *  to be used externally.
+     */
+    mx_internal var timeFraction:Number;
+    
+    /**
+     * @private
+     * Default easer for keyframes
+     */
+    private static var linearEaser:IEaser = new Linear();
+    /**
+     *  The easing behavior applied to the motion between the previous
+     *  Keyframe object in motion path and this Keyframe object. 
+     *  By default, the easing is linear, or no easing at all. 
+     *
+     *  <p>Note that the parent effect
+     *  might have easing applied already over the entire
+     *  animation. Therefore, if easing per keyframe interval is desired
+     *  instead, it is necessary to set the overall effect  
+     *  easer to linear easing (spark.effects.easing.Linear) and then
+     *  set the <code>easer</code> on each Keyframe as appropriate.</p>
+     * 
+     *  <p>Because this property acts on the interval between the previous
+     *  Keyframe object in a sequence and this Keyframe object, the <code>easer</code> 
+     *  property is ignored on the first Keyframe object in a sequence.</p>
+     *  
+     *  @default Linear
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var easer:IEaser = linearEaser;
+
+    /**
+     *  Optional parameter which, if specified, is used to
+     *  calculate <code>value</code> in this keyframe or
+     *  the previous one. If <code>value</code> is not set in
+     *  the previous keyframe, but this keyframe defines both
+     *  <code>value</code> and <code>valueBy</code>, then <code>value</code>
+     *  in the previous keyframe is calculated as <code>value</code>
+     *  in this keyframe minus <code>valueBy</code> in this keyframe.
+     *
+     *  <p>Similarly, if <code>value</code> in this keyframe is not
+     *  defined, but <code>valueBy</code> in this keyframe and
+     *  <code>value</code> in the previous keyframe are both set,
+     *  then <code>value</code> in this keyframe is calculated as
+     *  <code>value</code> in the previous keyframe plus
+     *  <code>valueBy</code> in this keyframe.</p>
+     *  
+     *  <p><code>valueBy</code> is ignored for the first
+     *  keyframe in a sequence, since it applies only to the interval
+     *  preceding a keyframe, and there is no preceding interval for the
+     *  first keyframe.</p>
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var valueBy:Object;
+
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/MotionPath.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/MotionPath.as
new file mode 100644
index 0000000..ebcf800
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/MotionPath.as
@@ -0,0 +1,288 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package spark.effects.animation  
+{
+import __AS3__.vec.Vector;
+
+import mx.core.mx_internal;
+
+import spark.effects.easing.IEaser;
+import spark.effects.easing.Sine;
+import spark.effects.interpolation.IInterpolator;
+import spark.effects.interpolation.NumberInterpolator;
+
+use namespace mx_internal;
+
+[DefaultProperty("keyframes")]
+
+/**
+ *  The MotionPath class defines the collection of Keyframes objects for an effect,
+ *  and the name of the property on the target to animate. 
+ *  Each Keyframe object defines the value of the property at a specific time during an effect. 
+ *  The effect then calculates the value of the target property 
+ *  by interpolating between the values specified by two key frames.
+ *  
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:MotionPath&gt;</code> tag
+ *  inherits the tag attributes of its superclass,
+ *  and adds the following tag attributes:</p>
+ *  
+ *  <pre>
+ *  &lt;s:MotionPath 
+ *    id="ID"
+ *    interpolator="NumberInterpolator"
+ *    keyframes="val"
+ *    property="val"
+ *  /&gt;
+ *  </pre>
+ * 
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ *
+ *  @includeExample examples/KeyFrameEffectExample.mxml
+ * 
+ *  @see Keyframe
+ *  @see spark.effects.interpolation.NumberInterpolator
+ */
+public class MotionPath
+{
+    //include "../../core/Version.as";
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Constructor. 
+     *
+     *  @param property The name of the property on the target to animate.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function MotionPath(property:String = null)
+    {
+        this.property = property;
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    // Properties
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  The name of the property on the effect target to be animated.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var property:String;
+     
+    /**
+     *  The interpolator determines how in-between values in an animation
+     *  are calculated. By default, the MotionPath class assumes that the values are
+     *  of type Number and can calculate in-between Number values automatically.
+     *  If the MotionPath class is given keyframes with non-Number values, or if the
+     *  desired behavior should use a different approach to interpolation
+     *  (such as per-channel color interpolation), then an interpolator
+     *  should be supplied.
+     *
+     *  <p>Flex supplies predefined interpolators in the spark.effects.interpolation package.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var interpolator:IInterpolator = NumberInterpolator.getInstance();
+    
+    /**
+     *  A sequence of Keyframe objects that represent the time/value pairs
+     *  that the property takes during the animation. Each successive
+     *  pair of keyframes controls the animation during the time interval
+     *  between them.
+     *  The optional <code>easer</code> and <code>valueBy</code>
+     *  properties of the later keyframe are used to determine the behavior
+     *  during that interval. The sequence of keyframes must be sorted in 
+     *  order of increasing time values.
+     * 
+     *  <p>Animations always start at time=0 and lasts for a duration
+     *  equal to the <code>time</code> value in the final keyframe. 
+     *  If no keyframe is defined at time=0, 
+     *  that keyframe is implicit, using the value of the
+     *  target property at the time the animation begins. </p>
+     *
+     *  <p>Because keyframes explicitly define the times involved in an animation, 
+     *  the duration for an effect using keyframes is set according to the maximum time
+     *  of the final keyframe of all MotionPaths in the effect.
+     *  For example, if an effect has keyframes
+     *  at times 0, 500, 1000, and 2000, then the effective duration of that
+     *  effect is 2000 ms, regardless of any <code>duration</code> property set on the
+     *  effect itself. 
+     *  Because the final keyframe determines the duration, there
+     *  must always be a final keyframe in any MotionPath. That is, 
+     *  it is implicit that the time in the final keyframe is the 
+     *  duration of the MotionPath.</p>
+     * 
+     *  <p>Any keyframe may leave its <code>value</code> undefined (either unset, set to 
+     *  <code>null</code>, or set to <code>NaN</code>).
+     *  In that case, the value is determined dynamically when the animation starts.
+     *  Any undefined value is determined as follows: </p>
+     *  <ol>
+     *    <li>If it is the first keyframe, it is calculated from the next keyframe
+     *      if that keyframe has both a <code>value</code> and <code>valueBy</code> property set,
+     *      as the difference of those values. Otherwise it gets the
+     *      current value of the property from the target.</li>
+     *    <li>If it is the final keyframe and the animation is running in a transition, it 
+     *      uses the value in the destination view state of the transition.</li>
+     *    <li>Otherwise, any keyframe calculates its <code>value</code> by using the previous
+     *      keyframe's <code>value</code> and adding the current keyframe's <code>valueBy</code>
+     *      to it, if <code>valueBy</code> is set.</li>
+     *  </ol>
+     *  
+     *  @see Keyframe
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var keyframes:Vector.<Keyframe>;
+    
+
+    /**
+     *  Returns a copy of this MotionPath object, including copies
+     *  of each keyframe.
+     *
+     *  @return A copy of this MotionPath object, including copies
+     *  of each keyframe.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function clone():MotionPath
+    {
+        var mp:MotionPath = new MotionPath(property);
+        mp.interpolator = interpolator;
+        if (keyframes !== null)
+        {
+            mp.keyframes = new Vector.<Keyframe>();
+            for (var i:int = 0; i < keyframes.length; ++i)
+                mp.keyframes[i] = keyframes[i].clone();
+        }
+        return mp;
+    }
+
+    /**
+     *  @private
+     * 
+     *  Calculates the <code>timeFraction</code> values for
+     *  each Keyframe in a MotionPath Keyframe sequence.
+     *  To calculate these values, the time on each Keyframe
+     *  is divided by the supplied <code>duration</code> parameter.
+     *  
+     *  @param duration the duration of the animation that the
+     *  keyframes should be scaled against.
+     */
+    mx_internal function scaleKeyframes(duration:Number):void
+    {
+        var n:int = keyframes.length;
+        for (var i:int; i < n; ++i)
+        {
+            var kf:Keyframe = keyframes[i];
+            // TODO (chaase): Must be some way to allow callers
+            // to supply timeFraction, but currently we clobber it
+            // with this operation. But if we choose to clobber it
+            // only if it's not set already, then it only works the
+            // first time through, since an Effect will retain its
+            // MotionPath, which retains its Keyframes, etc.
+            kf.timeFraction = kf.time / duration;
+        }
+    }
+    
+    /**
+     *  Calculates and returns an interpolated value, given the elapsed
+     *  time fraction. The function determines the keyframe interval
+     *  that the fraction falls within and then interpolates within
+     *  that interval between the values of the bounding keyframes on that
+     *  interval.
+     *
+     *  @param fraction The fraction of the overall duration of the effect,
+     *  (a value from 0.0 to 1.0).
+     *
+     *  @return The interpolated value.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function getValue(fraction:Number):Object
+    {
+        if (!keyframes)
+            return null;
+        var n:int = keyframes.length;
+        if (n == 2 && keyframes[1].timeFraction == 1)
+        {
+            // The common case where we are just animating from/to, as in the
+            // case of an SimpleMotionPath
+            var easedF:Number = (keyframes[1].easer) ? 
+                keyframes[1].easer.ease(fraction) : 
+                fraction;
+            return interpolator.interpolate(easedF, keyframes[0].value,
+                keyframes[1].value);
+        }
+        // if timeFraction on first keyframe is not set, call scaleKeyframes
+        // should not generally happen, but if getValue() is called before
+        // an owning effect is played, then timeFractions were not set
+        if (isNaN(keyframes[0].timeFraction))
+            scaleKeyframes(keyframes[keyframes.length-1].time);
+        var prevT:Number = 0;
+        var prevValue:Object = keyframes[0].value;
+        for (var i:int = 1; i < n; ++i)
+        {
+            var kf:Keyframe = keyframes[i];
+            if (fraction >= prevT && fraction < kf.timeFraction)
+            {
+                var t:Number = (fraction - prevT) / (kf.timeFraction - prevT);
+                var easedT:Number = (kf.easer) ? kf.easer.ease(t) : t;
+                return interpolator.interpolate(easedT, prevValue, kf.value);
+            }
+            prevT = kf.timeFraction;
+            prevValue = kf.value;
+        }
+        // Must be at the end of the animation
+        return keyframes[n-1].value;
+    }
+
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/RepeatBehavior.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/RepeatBehavior.as
new file mode 100644
index 0000000..c41edeb
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/RepeatBehavior.as
@@ -0,0 +1,58 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects.animation
+{
+/**
+ *  The RepeatBehavior class defines constants for use with <code>repeatBehavior</code>
+ *  property of the Animate and Animation classes.
+ * 
+ *  @see spark.effects.Animate#repeatBehavior
+ *  @see Animation#repeatBehavior
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public final class RepeatBehavior
+{
+    /**
+     * Specifies that a repeating animation should progress in a forward direction on
+     * every iteration.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static const LOOP:String = "loop";
+    
+    /**
+     * Specifies that a repeating animation should reverse direction on
+     * every iteration. For example, a reversing animation would play forward
+     * on the even iterations and in reverse on the odd iterations.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static const REVERSE:String = "reverse";
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/SimpleMotionPath.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/SimpleMotionPath.as
new file mode 100644
index 0000000..b4361a8
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/animation/SimpleMotionPath.as
@@ -0,0 +1,192 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package spark.effects.animation
+{
+import spark.effects.interpolation.MultiValueInterpolator;
+    
+    
+/**
+ *  The SimpleMotionPath class specifies the name of a property, and the values that
+ *  that property takes over time, for instances of the Animate
+ *  effect. 
+ * 
+ *  <p>This class is a simple subclass of MotionPath for defining 
+ *  two keyframes to hold the <code>valueFrom</code>, <code>valueTo</code>, and
+ *  <code>valueBy</code> properties. 
+ *  The MotionPath class itself can define any number of keyframes.</p>
+ *
+ *  @see MotionPath
+ *
+ *  @includeExample examples/SimpleMotionPathEffectExample.mxml
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class SimpleMotionPath extends MotionPath
+{
+ 
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Constructor. You can specify both the 
+     *  <code>valueFrom</code> and <code>valueTo</code> parameters, 
+     *  or specify the <code>valueBy</code> parameter and either the <code>valueFrom</code> 
+     *  or <code>valueTo</code> parameter. 
+     *  If you omit these parameters, Flex calculates them from the effect target.
+     * 
+     *  @param property The name of the property being animated.
+     *
+     *  @param valueFrom The initial value of the property.
+     *  
+     *  @param valueTo The final value of the property.
+     *  
+     *  @param valueBy An optional parameter that specifies the delta with
+     *  which to calculate either the from or to values, if one is omitted. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */    
+    public function SimpleMotionPath(property:String = null, 
+        valueFrom:Object = null, valueTo:Object = null, 
+        valueBy:Object = null)
+    {
+        super();
+        this.property = property;
+        keyframes = new <Keyframe>[new Keyframe(0, valueFrom), 
+            new Keyframe(NaN, valueTo, valueBy)];
+        if (valueFrom !== null && valueTo !== null &&
+            ((valueFrom is Array && valueTo is Array) ||
+             (valueFrom is Vector.<Number> && valueTo is Vector.<Number>)))
+        {
+            if (!multiValueInterpolator)
+                multiValueInterpolator = new MultiValueInterpolator();
+            interpolator = multiValueInterpolator;
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Variables
+    //
+    //--------------------------------------------------------------------------
+    
+    private static var multiValueInterpolator:MultiValueInterpolator = null;
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  The starting value for the property during the animation.
+     * 
+     *  <p>A value of Null or NaN (in the case of Numbers) specifies that a
+     *  value must be determined dynamically at runtime, either by
+     *  getting the value from the target property directly or calculating
+     *  it if the other value is valid and there is also a valid 
+     *  <code>valueBy</code> value supplied.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get valueFrom():Object
+    {
+        return keyframes[0].value;
+    }
+    public function set valueFrom(value:Object):void
+    {
+        keyframes[0].value = value;
+    }
+
+    /**
+     *  The value that the named property will animate to.
+     * 
+     *  <p>A value of Null or NaN (in the case of Numbers) element specifies that a
+     *  value must be determined dynamically at runtime, either by
+     *  getting the value from the target property directly or calculating
+     *  it if the other value is valid and there is also a valid <code>valueBy</code>
+     *  value supplied.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get valueTo():Object
+    {
+        return keyframes[keyframes.length -1].value;
+    }
+    public function set valueTo(value:Object):void
+    {
+        keyframes[keyframes.length - 1].value = value;
+    }
+
+    /**
+     *  Optional property which specifies the delta used to calculate
+     *  either the <code>valueFrom</code> or <code>valueTo</code> value.
+     *  Providing this optional property lets the effect 
+     *  calculate the necessary from/to values if either
+     *  are not provided or are to be determined dynamically when the animation 
+     *  begins.
+     * 
+     *  <p>The way that the <code>valueBy</code> value is used depends on which of the
+     *  other values are set. If neither are set, then the <code>valueFrom</code> 
+     *  value is determined from the current property value in the target, 
+     *  and the <code>valueTo</code> value is <code>valueFrom + valueBy</code>. 
+     *  If one or the other is set, but not both, then
+     *  the unset value is calculated by the other value: 
+     *  <code>valueTo = valueFrom + valueBy</code> or  
+     *  <code>valueFrom = valueTo - valueBy</code>). If both are set, then the
+     *  <code>valueBy</code> property is ignored.</p>
+     * 
+     *  <p>Note that since <code>valueBy</code> is of type
+     *  Object, the effect cannot directly calculate the other values
+     *  from it. It uses the effect's interpolator
+     *  to calculate the values by calling the interpolator's <code>increment()</code> 
+     *  and <code>decrement()</code> methods.
+     *  If no interpolator is set, then it will use NumberInterpolator by default.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get valueBy():Object
+    {
+        return keyframes[keyframes.length - 1].valueBy;
+    }
+    public function set valueBy(value:Object):void
+    {
+        keyframes[keyframes.length - 1].valueBy = value;
+    }
+
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/EaseInOutBase.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/EaseInOutBase.as
new file mode 100644
index 0000000..f0f3284
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/EaseInOutBase.as
@@ -0,0 +1,188 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects.easing
+{
+/**
+ *  The EaseInOutBase class is the base class that provide easing capability.
+ *  The EaseInOutBase class  defines easing as consisting of two phases:
+ *  the acceleration, or ease in phase, followed by the deceleration, or ease out phase.
+ *  The default behavior of this class returns a linear
+ *  interpolation for both easing phases. You can create a subclass
+ *  of EaseInOutBase to get more interesting behavior.
+ *
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:EaseInOutBase&gt;</code> tag
+ *  inherits all of the tag attributes of its of its superclass,
+ *  and adds the following tag attributes:</p>
+ *
+ *  <pre>
+ *  &lt;s:EaseInOutBase
+ *    id="ID"
+ *    easeInFraction="0.5"
+ *   /&gt;
+ *  </pre>
+ *
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class EaseInOutBase implements IEaser
+{
+
+    /**
+     *  Constructor.
+     *
+     *  @param easeInFraction Sets the value of
+     *  the <code>easeInFraction</code> property. The default value is
+     *  <code>EasingFraction.IN_OUT</code>, which eases in for the first half
+     *  of the time and eases out for the remainder.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function EaseInOutBase(easeInFraction:Number = EasingFraction.IN_OUT)
+    {
+        this.easeInFraction = easeInFraction;
+    }
+
+    /**
+     * Storage for the _easeInFraction property
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private var _easeInFraction:Number = .5;
+
+    [Inspectable(minValue="0.0", maxValue="1.0")]
+
+    /**
+     *  The percentage of an animation that should be spent accelerating.
+     *  This factor sets an implicit
+     *  "easeOut" parameter, equal to (1 - <code>easeIn</code>), so that any time not
+     *  spent easing in is spent easing out. For example, to have an easing
+     *  equation that spends half the time easing in and half easing out,
+     *  set <code>easeIn</code> to .5.
+     *
+     *  <p>Valid values are between 0.0 and 1.0.</p>
+     *
+     *  @default .5
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get easeInFraction():Number
+    {
+        return _easeInFraction;
+    }
+    public function set easeInFraction(value:Number):void
+    {
+        _easeInFraction = value;
+    }
+
+    /**
+     *  Takes the fraction representing the elapsed duration of an animation
+     *  (a value between 0.0 to 1.0) and returns a new elapsed value.
+     *  This  value is used to calculate animated property values.
+     *  By changing the value of the elapsed fraction, you effectively change
+     *  the animation of the property.
+     *
+     *  For EaseInOutBase, this method calculates the eased fraction
+     *  value based on the <code>easeInFraction</code> property. If
+     *  <code>fraction</code> is less than <code>easeInFraction</code>,
+     *  this method calls the <code>easeIn()</code> method. Otherwise it
+     *  calls the <code>easeOut()</code> method.
+     *  It is expected
+     *  that these functions are overridden in a subclass.
+     *
+     *  @param fraction The elapsed fraction of the animation.
+     *
+     *  @return The eased fraction of the animation.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function ease(fraction:Number):Number
+    {
+        var easeOutFraction:Number = 1 - easeInFraction;
+
+        if (fraction <= easeInFraction && easeInFraction > 0)
+            return easeInFraction * easeIn(fraction/easeInFraction);
+        else
+            return easeInFraction + easeOutFraction *
+                easeOut((fraction - easeInFraction)/easeOutFraction);
+    }
+
+    /**
+     *  Returns a value that represents the eased fraction during the
+     *  ease in phase of the animation. The value returned by this class
+     *  is simply the fraction itself, which represents a linear
+     *  interpolation of the fraction. More interesting behavior is
+     *  implemented by subclasses of EaseInOutBase.
+     *
+     *  @param fraction The fraction elapsed of the easing in phase
+     *  of the animation, between 0.0 and 1.0.
+     *
+     *  @return A value that represents the eased value for this
+     *  phase of the animation.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function easeIn(fraction:Number):Number
+    {
+        return fraction;
+    }
+
+    /**
+     *  Returns a value that represents the eased fraction during the
+     *  ease out phase of the animation. The value returned by this class
+     *  is simply the fraction itself, which represents a linear
+     *  interpolation of the fraction. More interesting behavior is
+     *  implemented by subclasses of EaseInOutBase.
+     *
+     *  @param fraction The fraction elapsed of the easing out phase
+     *  of the animation, between 0.0 and 1.0.
+     *
+     *  @return A value that represents the eased value for this
+     *  phase of the animation.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function easeOut(fraction:Number):Number
+    {
+        return fraction;
+    }
+
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/EasingFraction.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/EasingFraction.as
new file mode 100644
index 0000000..949fda1
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/EasingFraction.as
@@ -0,0 +1,72 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects.easing
+{
+/**
+ *  The EasingFraction class defines constants for 
+ *  the <code>easeInFraction</code> property of the EaseInOutBase class.
+ * 
+ *  @see EaseInOutBase
+ *  @see EaseInOutBase#easeInFraction
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public final class EasingFraction
+{
+    /**
+     *  Specifies that the easing instance
+     *  spends the entire animation easing in. This is equivalent
+     *  to setting the <code>easeInFraction</code> property to 1.0.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static const IN:Number = 1;
+
+    /**
+     *  Specifies that the easing instance
+     *  spends the entire animation easing out. This is equivalent
+     *  to setting the <code>easeInFraction</code> property to 0.0.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static const OUT:Number = 0;
+
+    /**
+     *  Specifies that an easing instance
+     *  that eases in for the first half and eases out for the
+     *  remainder. This is equivalent
+     *  to setting the <code>easeInFraction</code> property to 0.5.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static const IN_OUT:Number = 0.5;
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/Linear.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/Linear.as
new file mode 100644
index 0000000..fc9afa2
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/Linear.as
@@ -0,0 +1,199 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects.easing
+{
+/**
+ *  The Linear class defines an easing with three phases:
+ *  acceleration, uniform motion, and deceleration.
+ *  As the animation starts it accelerates through the period
+ *  specified by the <code>easeInFraction</code> property, it 
+ *  then uses uniform (linear) motion through the next phase, and
+ *  finally decelerates until the end during the period specified
+ *  by the <code>easeOutFraction</code> property.
+ * 
+ *  <p>The easing values for the three phases are calculated
+ *  such that the behavior of constant acceleration, linear motion,
+ *  and constant deceleration all occur within the specified 
+ *  duration of the animation.</p>
+ *  
+ *  <p>Strict linear motion can be achieved by setting 
+ *  <code>easeInFraction</code> and <code>easeOutFraction</code> to 0.0. 
+ *  Note that if acceleration or
+ *  deceleration are not 0.0, then the motion during the middle
+ *  phase is not at the same speed as that of pure
+ *  linear motion. The middle phase consists of
+ *  uniform motion, but the speed of that motion is determined by
+ *  the size of that phase relative to the overall animation.</p>
+ *  
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:Linear&gt;</code> tag
+ *  inherits all of the tag attributes of its of its superclass,
+ *  and adds the following tag attributes:</p>
+ *  
+ *  <pre>
+ *  &lt;s:Linear
+ *    id="ID"
+ *    easeInFraction="0" 
+ *    easeOutFraction="0"
+ *   /&gt;
+ *  </pre>
+ *
+ *  @includeExample examples/LinearEffectExample.mxml
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class Linear implements IEaser
+{
+    /**
+     *  Constructor.
+     *
+     *  @param easeInFraction The fraction of the overall duration 
+     *  in the acceleration phase, between 0.0 and 1.0.
+     *
+     *  @param easeOutFraction The fraction of the overall duration 
+     *  in the deceleration phase, between 0.0 and 1.0.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function Linear(easeInFraction:Number = 0, easeOutFraction:Number = 0)
+    {
+        this.easeInFraction = easeInFraction;
+        this.easeOutFraction = easeOutFraction;
+    }
+    
+    /**
+     * Storage for the _easeInFraction property
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private var _easeInFraction:Number = 0;
+    
+    /**
+     *  The fraction an animation spent accelerating,
+     *  between 0.0 and 1.0.
+     *  The values of the <code>easeOutFraction</code> property 
+     *  and <code>easeInFraction</code> property must satisfy the
+     *  equation <code>easeOutFraction + easeInFraction &lt;= 1</code>
+     *  where any remaining time is spent in the linear motion phase.
+     * 
+     *  @default 0
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get easeInFraction():Number
+    {
+        return _easeInFraction;
+    }
+    public function set easeInFraction(value:Number):void
+    {
+        _easeInFraction = value;
+    }
+
+    /**
+     * Storage for the _easeInFraction property
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private var _easeOutFraction:Number = 0;
+    
+    /**
+     *  The percentage an animation will spend decelerating, 
+     *  between 0.0 and 1.0.
+     *  The values of the <code>easeOutFraction</code> property 
+     *  and <code>easeInFraction</code> property must satisfy the
+     *  equation <code>easeOutFraction + easeInFraction &lt;= 1</code>
+     *  where any remaining time is spent in the linear motion phase.
+     * 
+     *  @default 0
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get easeOutFraction():Number
+    {
+        return _easeOutFraction;
+    }
+    public function set easeOutFraction(value:Number):void
+    {
+        _easeOutFraction = value;
+    }
+
+
+    /**
+     *  Calculates the eased fraction value based on the
+     *  <code>easeInFraction</code> and <code>easeOutFraction</code> 
+     *  properties. 
+     *  If <code>fraction</code>
+     *  is less than <code>easeInFraction</code>, it calculates a value
+     *  based on accelerating up to the linear motion phase. 
+     *  If <code>fraction</code>
+     *  is greater than <code>easeInFraction</code> and less than 
+     *  <code>(1-easeOutFraction)</code>, it calculates a value based
+     *  on the linear motion phase between the easing in and easing out phases.
+     *  Otherwise, it calculates a value based on constant deceleration
+     *  between the linear motion phase and 0.0.
+     * 
+     *  @param fraction The elapsed fraction of the animation, 
+     *  between 0.0 and 1.0..
+     * 
+     *  @return The eased fraction of the animation.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function ease(fraction:Number):Number
+    {
+        // Handle the trivial case where no easing is requested
+        if (easeInFraction == 0 && easeOutFraction == 0)
+            return fraction;
+            
+        var runRate:Number = 1 / (1 - easeInFraction/2 - easeOutFraction/2);
+        if (fraction < easeInFraction)
+            return fraction * runRate * (fraction / easeInFraction) / 2;
+        if (fraction > (1 - easeOutFraction))
+        {
+            var decTime:Number = fraction - (1 - easeOutFraction);
+            var decProportion:Number = decTime / easeOutFraction;
+            return runRate * (1 - easeInFraction/2 - easeOutFraction +
+                decTime * (2 - decProportion) / 2);
+        }
+        return runRate * (fraction - easeInFraction/2);
+    }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/Sine.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/Sine.as
new file mode 100644
index 0000000..73a1864
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/Sine.as
@@ -0,0 +1,113 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects.easing
+{
+/**
+ *  The Sine class defines easing functionality using a Sine function.
+ *  Easing consists of two phases: the acceleration, or ease in phase,
+ *  followed by the deceleration, or ease out phase.
+ *  Use the <code>easeInFraction</code> property to specify
+ *  the percentage of an animation accelerating.
+ *
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:Sine&gt;</code> tag
+ *  inherits all of the tag attributes of its of its superclass,
+ *  and adds the following tag attributes:</p>
+ *
+ *  <pre>
+ *  &lt;s:Sine
+ *    id="ID"
+ *   /&gt;
+ *  </pre>
+ *
+ *  @includeExample examples/SinePowerEffectExample.mxml
+ *
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class Sine extends EaseInOutBase
+{
+    /**
+     *  Constructor.
+     *
+     *  @param easeInFraction Sets the value of
+     *  the <code>easeInFraction</code> property. The default value is
+     *  <code>EasingFraction.IN_OUT</code>, which eases in for the first half
+     *  of the time and eases out for the remainder.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function Sine(easeInFraction:Number = 0.5)
+    {
+        super(easeInFraction);
+    }
+
+    /**
+     *  @private
+     *  Returns a value that represents the eased fraction during the
+     *  ease in phase of the animation.
+     *  The easing calculation for Sine is equal to
+     *  <code>1 - cos(fraction*PI/2)</code>.
+     *
+     *  @param fraction The fraction elapsed of the easing in phase
+     *  of the animation, between 0.0 and 1.0.
+     *
+     *  @return A value that represents the eased value for this
+     *  phase of the animation.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override protected function easeIn(fraction:Number):Number
+    {
+        return 1 - Math.cos(fraction * Math.PI/2);
+    }
+
+    /**
+     *  @private
+     *  Returns a value that represents the eased fraction during the
+     *  ease out phase of the animation.
+     *  The easing calculation for Sine is equal to
+     *  <code>sin(fraction*PI/2)</code>.
+     *
+     *  @param fraction The fraction elapsed of the easing out phase
+     *  of the animation, between 0.0 and 1.0.
+     *
+     *  @return A value that represents the eased value for this
+     *  phase of the animation.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override protected function easeOut(fraction:Number):Number
+    {
+        return Math.sin(fraction * Math.PI/2);
+    }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/IInterpolator.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/IInterpolator.as
new file mode 100644
index 0000000..3ce1d94
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/IInterpolator.as
@@ -0,0 +1,105 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects.interpolation
+{
+/**
+ *  The IInterpolator interface is implemented by classes that calculate
+ *  values for the Animation class. The Animation class
+ *  can handle parametric interpolation between Number values and
+ *  arrays of Number values, but it cannot handle different types
+ *  of interpolation, or interpolation between different types of
+ *  values. Implementors of this interface can provide arbitrary
+ *  interpolation capabilities so that Animations can be created between
+ *  arbitrary values.
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public interface IInterpolator
+{
+    /**
+     *  Given an elapsed fraction of an animation, between 0.0 and 1.0,
+     *  and start and end values to interpolate, return the interpolated value.
+     *
+     *  @param fraction The fraction elapsed of the 
+     *  animation, between 0.0 and 1.0.
+     *
+     *  @param startValue The start value of the interpolation.
+     *
+     *  @param endValue The end value of the interpolation.
+     *
+     *  @return The interpolated value.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    function interpolate(fraction:Number,
+        startValue:Object, endValue:Object):Object;   
+
+    /**
+     *  Given a base value and a value to add to it, 
+     *  return the result of that operation. 
+     *  For example, if the objects are simple Numbers, the result is a 
+     *  <code>Number(baseValue) + Number(incrementValue)</code>.
+     *  This method is called by the animation system when it
+     *  needs to dynamically calculate a value given some starting
+     *  value and a 'by' value that should be added to it. Both of
+     *  the arguments are of type Object and cannot simply be added together.
+     *
+     *  @param baseValue The start value of the interpolation.
+     *
+     *  @param incrementValue The change to apply to the <code>baseValue</code>.
+     *
+     *  @return The interpolated value.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    function increment(baseValue:Object, incrementValue:Object):Object;
+
+    /**
+     *  Given a base value and a value to subtract from it, 
+     *  return the result of that decrement operation. For example,
+     *  if the objects are simple Numbers, the result would be
+     *  <code>Number(baseValue) - Number(incrementValue)</code>.
+     *  This function is called by the animation system when it
+     *  needs to dynamically calculate a value given some ending
+     *  value and a 'by' value that should be subtracted from it. Both of
+     *  the arguments are of type Object and cannot simply be added together.
+     *  
+     *  @param baseValue The start value of the interpolation.
+     *
+     *  @param decrementValue The change to apply to the <code>baseValue</code>.
+     *
+     *  @return The interpolated value.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    function decrement(baseValue:Object, decrementValue:Object):Object;
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/MultiValueInterpolator.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/MultiValueInterpolator.as
new file mode 100644
index 0000000..a56ec1c
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/MultiValueInterpolator.as
@@ -0,0 +1,208 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects.interpolation
+{
+
+import mx.resources.IResourceManager;
+import mx.resources.ResourceManager;    
+
+//--------------------------------------
+//  Other metadata
+//--------------------------------------
+
+[ResourceBundle("sparkEffects")]
+    
+/**
+ *  The MultiValueInterpolator class interpolates each element of Arrays or
+ *  Vectors of start and end elements separately, using another interpolator 
+ *  to do the interpolation for each element. 
+ *  By default, the 
+ *  interpolation for each element uses the NumberInterpolator class, but you 
+ *  can construct a MultiValueInterpolator instance with a different interpolator.
+ *  
+ *  @see
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class MultiValueInterpolator implements IInterpolator
+{
+    
+    /**
+     *  Constructor. 
+     *
+     *  @param elementInterpolator The interpolator for each element
+     *  of the Array.
+     *  If no interpolator is specified, use the NumberInterpolator class.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function MultiValueInterpolator(elementInterpolator:IInterpolator = null)
+    {
+        if (elementInterpolator != null)
+            this.elementInterpolator = elementInterpolator;
+    }
+
+    /**
+     *  @private
+     *  Used for accessing localized Error messages.
+     */
+    private var resourceManager:IResourceManager =
+                                    ResourceManager.getInstance();
+
+    // The internal per-element interpolator
+    private var _elementInterpolator:IInterpolator = NumberInterpolator.getInstance();
+    /**
+     *  The interpolator for each element of the input Array or Vector. 
+     *  A value of null specifies to use the NumberInterpolator class.
+     *  
+     *  @default NumberInterpolator
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get elementInterpolator():IInterpolator
+    {
+        return _elementInterpolator;
+    }
+    /**
+     *  @private
+     */
+    public function set elementInterpolator(value:IInterpolator):void
+    {
+        _elementInterpolator = value ? 
+            value : (NumberInterpolator.getInstance());
+    }
+
+    /**
+     *  Given an elapsed fraction of an animation, between 0.0 and 1.0,
+     *  and start and end values to interpolate, return the interpolated value.
+     * 
+     *  Interpolation for MultiValueInterpolator consists of running a separate
+     *  interpolation on each element of the startValue and endValue
+     *  arrays or vectors, returning a new Array or Vector that holds those 
+     *  interpolated values. The returned object will be an Array if startValue
+     *  and endValue are of type Array, otherwise the returned object will be
+     *  of type Vector.
+     *
+     *  @param fraction The fraction elapsed of the 
+     *  animation, between 0.0 and 1.0.
+     *
+     *  @param startValue The start value of the interpolation.
+     *
+     *  @param endValue The end value of the interpolation.
+     *
+     *  @return The interpolated value.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function interpolate(fraction:Number, startValue:Object, 
+        endValue:Object):Object
+    {
+        if (startValue.length != endValue.length)
+            throw new Error(resourceManager.getString("sparkEffects", "arraysNotOfEqualLength"));
+        var returnObject:Object;
+        if (startValue is Array)
+            returnObject = [];
+        else 
+            // splice(0,0) seems to be the only way to create a Vector of the
+            // same type
+            returnObject = startValue.splice(0, 0);
+        for (var i:int = 0; i < startValue.length; i++)
+            returnObject[i] = _elementInterpolator.interpolate(fraction, 
+                startValue[i], endValue[i]);
+
+        return returnObject;
+    }
+    
+    /**
+     * @inheritDoc
+     * 
+     * Incrementing for MultiValueInterpolator consists of running a separate
+     * increment operation on each element of the <code>baseValue</code> array,
+     * adding the same <code>incrementValue</code> to each one and
+     * returning a new Array or Vector that holds those incremented values.
+     * The returned object will be an Array if startValue
+     * and endValue are of type Array, otherwise the returned object will be
+     * of type Vector.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function increment(baseValue:Object, incrementValue:Object):Object
+    {
+        var returnObject:Object;
+        if (baseValue is Array)
+            returnObject = [];
+        else 
+            // splice(0,0) seems to be the only way to create a Vector of the
+            // same type
+            returnObject = baseValue.splice(0, 0);
+        for (var i:int = 0; i < baseValue.length; i++)
+            returnObject[i] = _elementInterpolator.increment(
+                baseValue[i], incrementValue);
+
+        return returnObject;
+    }
+    
+    /**
+     * @inheritDoc
+     * 
+     * Decrementing for MultiValueInterpolator consists of running a separate
+     * decrement operation on each element of the <code>baseValue</code> object,
+     * subtracting the same <code>incrementValue</code> from each one and
+     * returning a new Array or Vector that holds those decremented values.
+     * The returned object will be an Array if startValue
+     * and endValue are of type Array, otherwise the returned object will be
+     * of type Vector.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function decrement(baseValue:Object, decrementValue:Object):Object
+    {
+        var returnObject:Object;
+        if (baseValue is Array)
+            returnObject = [];
+        else 
+            // splice(0,0) seems to be the only way to create a Vector of the
+            // same type
+            returnObject = baseValue.splice(0, 0);
+        for (var i:int = 0; i < baseValue.length; i++)
+            returnObject[i] = _elementInterpolator.decrement(
+                baseValue[i], decrementValue);
+
+        return returnObject;
+    }
+        
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/NumberInterpolator.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/NumberInterpolator.as
new file mode 100644
index 0000000..6651f09
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/NumberInterpolator.as
@@ -0,0 +1,145 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects.interpolation
+{
+
+import mx.resources.IResourceManager;
+import mx.resources.ResourceManager;
+
+//--------------------------------------
+//  Other metadata
+//--------------------------------------
+
+[ResourceBundle("sparkEffects")]
+
+/**
+ * The NumberInterpolator class provides interpolation between
+ * start and end values represented as Number instances.
+ *
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class NumberInterpolator implements IInterpolator
+{
+    private static var theInstance:NumberInterpolator;
+
+    /**
+     *  Constructor.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function NumberInterpolator()
+    {
+        super();
+    }
+
+    /**
+     *  @private
+     *  Used for accessing localized Error messages.
+     */
+    private var resourceManager:IResourceManager =
+                                    ResourceManager.getInstance();
+
+    /**
+     *  Returns the singleton of this class.
+     *  Since all NumberInterpolators
+     *  have the same behavior, there is no need for more than one instance.
+     *
+     *  @return The singleton of this class.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static function getInstance():NumberInterpolator
+    {
+        if (!theInstance)
+            theInstance = new NumberInterpolator();
+        return theInstance;
+    }
+
+    /**
+     *  Interpolation for NumberInterpolator consists of a simple
+     *  parametric calculation between <code>startValue</code> and
+     *  <code>endValue</code>, using <code>fraction</code> as the
+     *  fraction of the elapsed time from start to end:
+     *
+     *  <pre>return startValue + fraction * (endValue - startValue);</pre>
+     *
+     *  @param fraction The fraction elapsed of the
+     *  animation, between 0.0 and 1.0.
+     *
+     *  @param startValue The start value of the interpolation.
+     *
+     *  @param endValue The end value of the interpolation.
+     *
+     *  @return The interpolated value.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function interpolate(fraction:Number, startValue:Object,
+        endValue:Object):Object
+    {
+        // Quick test for 0 or 1 to avoid round-off error on either end
+        if (fraction == 0)
+            return startValue;
+        else if (fraction == 1)
+            return endValue;
+        if ((startValue is Number && isNaN(Number(startValue))) ||
+            (endValue is Number && isNaN(Number(endValue))))
+            throw new Error(resourceManager.getString("sparkEffects", "cannotCalculateValue", [startValue, endValue]));
+        return Number(startValue) + (fraction * (Number(endValue) - Number(startValue)));
+    }
+
+    /**
+     *  @inheritDoc
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function increment(baseValue:Object, incrementValue:Object):Object
+    {
+        return Number(baseValue) + Number(incrementValue);
+    }
+
+    /**
+     *  @inheritDoc
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+   public function decrement(baseValue:Object, decrementValue:Object):Object
+   {
+        return Number(baseValue) - Number(decrementValue);
+   }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/RGBInterpolator.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/RGBInterpolator.as
new file mode 100644
index 0000000..50a5d64
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/interpolation/RGBInterpolator.as
@@ -0,0 +1,193 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.effects.interpolation
+{
+/**
+ *  The RGBInterpolator class provides interpolation between 
+ *  <code>uint</code> start and end values that represent RGB colors. 
+ *  Interpolation is done by treating
+ *  the start and end values as integers with color channel information in
+ *  the least-significant 3 bytes, and then interpolating each of the channels
+ *  separately.
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class RGBInterpolator implements IInterpolator
+{   
+    private static var theInstance:RGBInterpolator;
+    
+    /**
+     *  Constructor.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function RGBInterpolator()
+    {
+        super();
+    }
+   
+    /**
+     *  Returns the singleton of this class. Since all RGBInterpolators
+     *  have the same behavior, there is no need for more than one instance.
+     *
+     *  @return The singleton of this class.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static function getInstance():RGBInterpolator
+    {
+        if (!theInstance)
+            theInstance = new RGBInterpolator();
+        return theInstance;
+    }
+
+    /**
+     *  Interpolation for the RGBInterpolator class takes the form of parametric
+     *  calculations on each of the bottom three bytes of 
+     *  <code>startValue</code> and <code>endValue</code>. 
+     * 
+     *  @param fraction The fraction elapsed of the 
+     *  animation, between 0.0 and 1.0.
+     *
+     *  @param startValue The start value of the interpolation.
+     *
+     *  @param endValue The end value of the interpolation.
+     *
+     *  @return The interpolated value.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function interpolate(fraction:Number, startValue:Object, 
+        endValue:Object):Object
+    {
+        // Quick test for start or end
+        if (fraction == 0)
+            return startValue;
+        else if (fraction == 1)
+            return endValue;
+        var startR:int;
+        var startG:int;
+        var startB:int;
+        var endR:int;
+        var endG:int;
+        var endB:int;
+        var deltaR:int;
+        var deltaG:int;
+        var deltaB:int;
+        fraction = Math.min(1, Math.max(0, fraction));
+        startR = (uint(startValue) & 0xff0000) >> 16;
+        startG = (uint(startValue) & 0xff00) >> 8;
+        startB = uint(startValue) & 0xff;
+        endR = (uint(endValue) & 0xff0000) >> 16;
+        endG = (uint(endValue) & 0xff00) >> 8;
+        endB = uint(endValue) & 0xff;
+        deltaR = endR - startR;
+        deltaG = endG - startG;
+        deltaB = endB - startB;
+        var newR:uint = startR + deltaR * fraction;
+        var newG:uint = startG + deltaG * fraction;
+        var newB:uint = startB + deltaB * fraction;
+        return newR << 16 | newG << 8 | newB;
+    }
+
+    /**
+     * @private
+     * 
+     * Utility function called by increment() and decrement()
+     */
+    private function combine(baseValue:uint, deltaValue:uint,
+        increment:Boolean):Object
+    {
+        var baseR:int = (baseValue & 0xff0000) >> 16;
+        var baseG:int = (baseValue & 0xff00) >> 8;
+        var baseB:int = baseValue & 0xff;
+        var deltaR:int = (deltaValue & 0xff0000) >> 16;
+        var deltaG:int = (deltaValue & 0xff00) >> 8;
+        var deltaB:int = deltaValue & 0xff;
+        var newR:uint, newG:uint, newB:uint;
+        if (increment)
+        {
+            newR = Math.min(baseR + deltaR, 255);
+            newG = Math.min(baseG + deltaG, 255);
+            newB = Math.min(baseB + deltaB, 255);
+        }
+        else
+        {
+            newR = Math.max(baseR - deltaR, 0);
+            newG = Math.max(baseG - deltaG, 0);
+            newB = Math.max(baseB - deltaB, 0);
+        }
+        return newR << 16 | newG << 8 | newB;
+    }
+
+    /**
+     *  Returns the result of the two values added
+     *  together on a per-channel basis. Each channel has a maximum 
+     *  value of 255 to avoid overflow problems.
+     *
+     *  @param baseValue The start value of the interpolation.
+     *
+     *  @param incrementValue The change to apply to the <code>baseValue</code>.
+     *
+     *  @return The interpolated value.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function increment(baseValue:Object, incrementValue:Object):Object
+    {
+        return combine(uint(baseValue), uint(incrementValue), true);
+    }
+
+    /**
+     *  Returns the result of the two values subtracted
+     *  on a per-channel basis. Each channel has a minimum
+     *  value of 0 to avoid underflow problems.
+     *
+     *  @param baseValue The start value of the interpolation.
+     *
+     *  @param decrementValue The change to apply to the <code>baseValue</code>.
+     *
+     *  @return The interpolated value.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+   public function decrement(baseValue:Object, decrementValue:Object):Object
+   {
+        return combine(uint(baseValue), uint(decrementValue), false);
+   }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/supportClasses/AnimateColorInstance.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/supportClasses/AnimateColorInstance.as
new file mode 100644
index 0000000..a0591c6
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/supportClasses/AnimateColorInstance.as
@@ -0,0 +1,134 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package spark.effects.supportClasses
+{
+import mx.styles.StyleManager;
+
+import spark.effects.animation.Keyframe;
+import spark.effects.animation.MotionPath;
+import spark.effects.animation.SimpleMotionPath;
+import spark.effects.interpolation.RGBInterpolator;
+
+/**
+ *  The AnimateColorInstance class is the instance class of 
+ *  the AnimateColor effect, which animates a change in
+ *  color by interpolating the from/to values per color channel.
+ *  Flex creates an instance of this class when
+ *  it plays a AnimateFilter effect; you do not create one yourself.
+ *
+ *  @see spark.effects.AnimateColor
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class AnimateColorInstance extends AnimateInstance
+{
+    /**
+     *  @copy spark.effects.AnimateColor#colorFrom
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var colorFrom:uint;
+
+    /**
+     *  @copy spark.effects.AnimateColor#colorTo
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var colorTo:uint;
+
+    /**
+     *  @copy spark.effects.AnimateColor#colorPropertyName
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var colorPropertyName:String;
+
+    /**
+     *  Constructor.
+     *
+     *  @param target The Object to animate with this effect.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function AnimateColorInstance(target:Object)
+    {
+        super(target);
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     */
+    override public function play():void
+    {
+        // The user may have supplied some combination of xFrom, xTo, and xBy.
+        // If either xFrom or xTo is not explicitly defined, calculate its
+        // value based on the other two values.
+        if (colorFrom == StyleManager.NOT_A_COLOR)
+        {
+            if (propertyChanges && propertyChanges.start[colorPropertyName] !== undefined)
+                colorFrom = propertyChanges.start[colorPropertyName];
+            else
+                colorFrom = getCurrentValue(colorPropertyName);
+        }
+        if (colorTo == StyleManager.NOT_A_COLOR)
+        {
+            if (propertyChanges &&
+                propertyChanges.end[colorPropertyName] !== undefined)
+            {
+                colorTo = propertyChanges.end[colorPropertyName];
+            }
+            else
+            {
+                colorTo = getCurrentValue(colorPropertyName);
+            }
+        }
+        
+        motionPaths = new <MotionPath>[new MotionPath(colorPropertyName)];
+        motionPaths[0].keyframes = new <Keyframe>[new Keyframe(0, colorFrom), 
+            new Keyframe(duration, colorTo)];
+                 
+        if (!interpolator)
+            interpolator = RGBInterpolator.getInstance();
+                
+        super.play();        
+    }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/supportClasses/AnimateInstance.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/supportClasses/AnimateInstance.as
new file mode 100644
index 0000000..42dba13
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/supportClasses/AnimateInstance.as
@@ -0,0 +1,1166 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package spark.effects.supportClasses
+{
+import org.apache.royale.utils.Timer;
+
+import mx.core.IVisualElement;
+import mx.core.IVisualElementContainer;
+import mx.core.UIComponent;
+import mx.core.mx_internal;
+import mx.effects.Effect;
+import mx.effects.EffectInstance;
+import mx.events.EffectEvent;
+import mx.managers.SystemManager;
+import mx.resources.IResourceManager;
+import mx.resources.ResourceManager;
+import mx.styles.IStyleClient;
+
+import spark.effects.animation.Animation;
+import spark.effects.animation.IAnimationTarget;
+import spark.effects.animation.Keyframe;
+import spark.effects.animation.MotionPath;
+import spark.effects.animation.SimpleMotionPath;
+import spark.effects.easing.IEaser;
+import spark.effects.interpolation.IInterpolator;
+//import spark.primitives.supportClasses.GraphicElement;
+
+use namespace mx_internal;
+
+//--------------------------------------
+//  Other metadata
+//--------------------------------------
+
+[ResourceBundle("sparkEffects")]
+
+/**
+ *  The AnimateInstance class implements the instance class for the
+ *  Animate effect. Flex creates an instance of this class when
+ *  it plays a Animate effect; you do not create one yourself.
+ *
+ *  @see spark.effects.Animate
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class AnimateInstance extends EffectInstance implements IAnimationTarget
+{
+    /**
+     *  @private
+     */
+    public var animation:Animation;
+    
+    /**
+     *  Constructor.
+     *
+     *  @param target The Object to animate with this effect.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function AnimateInstance(target:Object)
+    {
+        super(target);
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Variables
+    //
+    //--------------------------------------------------------------------------
+    /**
+     *  Tracks whether each property of the target is an actual 
+     *  property or a style. We determine this dynamically by
+     *  simply checking whether the property is 'in' the target.
+     *  If not, we check whether it is a valid style, and otherwise
+     *  throw an error.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private var isStyleMap:Object = new Object();
+    
+    /**
+     *  @private.
+     *  Used internally to hold the value of the new playhead position
+     *  if the animation doesn't currently exist.
+     */
+    private var _seekTime:Number = 0;
+
+    private var reverseAnimation:Boolean;
+    
+    private var needsRemoval:Boolean;
+
+    /**
+     * @private
+     * Track number of update listeners for optimization purposes
+     */
+    private var numUpdateListeners:int = 0;
+    
+    
+    /**
+     *  @private
+     *  Used for accessing localized Error messages.
+     */
+    private var resourceManager:IResourceManager =
+                                    ResourceManager.getInstance();
+    
+    /**
+     * @private
+     * Cache these values when disabling constraints, to correctly reset
+     * width/height values when we finish
+     */
+    private var oldWidth:Number;
+    private var oldHeight:Number;
+
+    /**
+     * @private
+     * Used to selectively disable constraints that are being animated by
+     * their underlying values because the constraint values are not
+     * valid in both states of a transition. For example, if left is set
+     * in only one state, then we disable left and animate x instead,
+     * re-enabling left when the effect is finished.
+     */
+    private var disabledConstraintsMap:Object;
+
+    //--------------------------------------------------------------------------
+    //
+    // Properties
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     */
+    //override public function set suspendBackgroundProcessing(value:Boolean):void
+    //{
+        //// Noop: this flag causes Flex 4 effects to break because they
+        //// depend on the layout validation process that the flag suppresses
+    //}
+
+    private var _motionPaths:Vector.<MotionPath>;
+    /**
+     *  @copy spark.effects.Animate#motionPaths
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get motionPaths():Vector.<MotionPath>
+    {
+        return _motionPaths;
+    }
+    public function set motionPaths(value:Vector.<MotionPath>):void
+    {
+        // Only set the list to the given value if we have a 
+        // null list to begin with. Otherwise, we've already
+        // set up the list once and don't need to do it again
+        // (for example, in a repeating effect).
+        if (!_motionPaths)
+            _motionPaths = value;
+    }
+    
+    /**
+     *  If <code>true</code>, the effect retains its target 
+     *  during a transition and removes it when finished. 
+     *  This capability applies specifically to effects like
+     *  Fade which act on targets that go away at the end of the
+     *  transition and removes the need to supply a RemoveAction or similar
+     *  effect to manually keep the item around and remove it when the
+     *  transition completes. 
+     * 
+     *  <p>To use this capability, subclasses
+     *  should set this variable to <code>true</code> and also expose the <code>parent</code>
+     *  property in their <code>affectedProperties</code> Array so 
+     *  that the effect instance has enough information about the target
+     *  and container to do the job.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected var autoRemoveTarget:Boolean = false;
+        
+    /**
+     * @copy spark.effects.Animate#disableLayout
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var disableLayout:Boolean;
+    
+    private var _easer:IEaser;    
+    /**
+     * @copy spark.effects.Animate#easer
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function set easer(value:IEaser):void
+    {
+        _easer = value;
+    }
+    /**
+     *  @private
+     */
+    public function get easer():IEaser
+    {
+        return _easer;
+    }
+    
+    private var _interpolator:IInterpolator;
+    /**
+     * @copy spark.effects.Animate#interpolator
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function set interpolator(value:IInterpolator):void
+    {
+        _interpolator = value;
+        
+    }
+    /**
+     *  @private
+     */
+    public function get interpolator():IInterpolator
+    {
+        return _interpolator;
+    }
+    
+    private var _repeatBehavior:String;
+    /**
+     * @copy spark.effects.Animate#repeatBehavior
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function set repeatBehavior(value:String):void
+    {
+        _repeatBehavior = value;
+    }
+    /**
+     *  @private
+     */
+    public function get repeatBehavior():String
+    {
+        return _repeatBehavior;
+    }
+            
+    //----------------------------------
+    //  playReversed
+    //----------------------------------
+
+    /**
+     *  @private
+     */
+    //override mx_internal function set playReversed(value:Boolean):void
+    //{
+        //super.playReversed = value;
+        //
+        //if (value && animation)
+            //animation.reverse();
+        //
+        //reverseAnimation = value;
+    //}
+
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden methods
+    //
+    //--------------------------------------------------------------------------
+
+    //----------------------------------
+    //  playheadTime
+    //----------------------------------
+    
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override public function get playheadTime():Number 
+    {
+        return animation ? animation.playheadTime : _seekTime;
+    }
+    /**
+     * @private
+     */
+    override public function set playheadTime(value:Number):void
+    {
+        if (animation)
+            animation.playheadTime = value;
+        _seekTime = value;
+    } 
+    
+
+    /**
+     *  @private
+     */
+    override public function pause():void
+    {
+        super.pause();
+        
+        if (animation)
+            animation.pause();
+    }
+
+    /**
+     *  @private
+     */
+    override public function stop():void
+    {
+        super.stop();
+        
+        if (animation)
+            animation.stop();
+    }   
+    
+    /**
+     *  @private
+     */
+    override public function resume():void
+    {
+        super.resume();
+    
+        if (animation)
+            animation.resume();
+    }
+        
+    /**
+     *  @private
+     */
+    override public function reverse():void
+    {
+        super.reverse();
+    
+        if (animation)
+            animation.reverse();
+        
+        reverseAnimation = !reverseAnimation;
+    }
+    
+    /**
+     *  @private
+     *  Interrupts an effect that is currently playing,
+     *  and immediately jumps to the end of the effect.
+     *  Calls the <code>Tween.endTween()</code> method
+     *  on the <code>tween</code> property. 
+     *  This method implements the method of the superclass. 
+     *
+     *  <p>If you create a subclass of TweenEffectInstance,
+     *  you can optionally override this method.</p>
+     *
+     *  <p>The effect dispatches the <code>effectEnd</code> event.</p>
+     *
+     *  @see mx.effects.EffectInstance#end()
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override public function end():void
+    {
+        // Jump to the end of the animation.
+        if (animation)
+        {
+            animation.end();
+            animation = null;
+        }
+
+        super.end();
+    }
+        
+    //--------------------------------------------------------------------------
+    //
+    // Methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override public function startEffect():void
+    {  
+        // This override removes EffectManager.effectStarted() to avoid use of
+        // mx_internal. New effects are not currently triggerable, so
+        // this should not matter
+                 
+        if (target is UIComponent)
+        {
+            //UIComponent(target).effectStarted(this);
+        }
+
+        if (autoRemoveTarget)
+            addDisappearingTarget();
+
+        play();
+    }
+    
+    /**
+     * Starts this effect. Performs any final setup for each property
+     * from/to values and starts an Animation that will update that property.
+     * 
+     * @private
+     */
+    override public function play():void
+    {
+        super.play();
+
+        if (!motionPaths || motionPaths.length == 0)
+        {
+            // nothing to do; at least schedule the effect to end after
+            // the specified duration
+            var timer:Timer = new Timer(duration, 1);
+            timer.addEventListener("timer", noopAnimationHandler);
+            timer.start();
+            return;
+        }
+            
+        isStyleMap = new Array(motionPaths.length);        
+    
+        // TODO (chaase): avoid setting up animations on properties whose
+        // from/to values are the same. Not worth the cycles, but also want
+        // to avoid triggering any side effects when we're not actually changing
+        // values
+        var addWidthMP:Boolean;
+        var addHeightMP:Boolean;
+        var i:int;
+        var j:int;
+        for (i = 0; i < motionPaths.length; ++i)
+        {
+            var mp:MotionPath = MotionPath(motionPaths[i]);
+            var keyframes:Vector.<Keyframe> = mp.keyframes;
+            if (!keyframes)
+                continue;
+            // Account for animating constraints where one or both state values
+            // are invalid; use underlying properties of x, y, width, height
+            // instead, or in addition
+            if (propertyChanges != null &&
+                (mp.property == "left" || mp.property == "right" ||
+                 mp.property == "top" || mp.property == "bottom" ||
+                 mp.property == "percentWidth" || mp.property == "percentHeight" ||
+                 mp.property == "horizontalCenter" || mp.property == "verticalCenter"))
+            {
+                // We need to substitute an underlying property if the animation is
+                // trying to automatically use and animate between the constraint
+                // values in the states, but one or both values are invalid
+                if (!isValidValue(propertyChanges.start[mp.property]) ||
+                    !isValidValue(propertyChanges.end[mp.property]) &&
+                    keyframes.length == 2 && !isValidValue(keyframes[0].value) &&
+                    !isValidValue(keyframes[1].value))
+                {
+                    if (mp.property == "percentWidth")
+                        mp.property = "width";
+                    else if (mp.property == "percentHeight")
+                        mp.property = "height";
+                    else if (mp.property == "left" || mp.property == "right" || 
+                        mp.property == "horizontalCenter")
+                    {
+                        if (!disabledConstraintsMap)
+                            disabledConstraintsMap = new Object();
+                        disabledConstraintsMap[mp.property] = true;
+                        mp.property = "x";
+                        if (isValidValue(propertyChanges.start["width"]) &&
+                            isValidValue(propertyChanges.end["width"]) &&
+                            propertyChanges.start["width"] != propertyChanges.end["width"])
+                        {
+                            // add motionPath to account for width changing between states
+                            addWidthMP = true;
+                        }
+                    }
+                    else
+                    {
+                        if (!disabledConstraintsMap)
+                            disabledConstraintsMap = new Object();
+                        disabledConstraintsMap[mp.property] = true;
+                        mp.property = "y";
+                        if (isValidValue(propertyChanges.start["height"]) &&
+                            isValidValue(propertyChanges.end["height"]) &&
+                            propertyChanges.start["height"] != propertyChanges.end["height"])
+                        {
+                            // add motionPath to account for height changing between states
+                            addHeightMP = true;
+                        }
+                    }
+                }
+            }
+            if (interpolator)
+                mp.interpolator = interpolator;
+            // adjust effect duration to be the max of all MotionPath keyframe times
+            // duration==0 is special-case, because the user (or an internal request)
+            // specifically asked the effect to be of zero duration
+            // TODO (chaase): Currently we do not adjust *down* for smaller duration
+            // MotionPaths. This is because we do not distinguish between
+            // SimpleMotionPath objects (which are created with fake durations of 1,
+            // knowing that they will derive their duration from their effects) and
+            // actual keyframe-based MotionPaths.
+            if (duration > 0)
+                for (j = 0; j < keyframes.length; ++j)
+                    if (!isNaN(keyframes[j].time))
+                        duration = Math.max(duration, keyframes[j].time);
+
+        }
+        if (addWidthMP)
+            motionPaths.push(new SimpleMotionPath("width"));
+        if (addHeightMP)
+            motionPaths.push(new SimpleMotionPath("height"));
+
+        animation = new Animation(duration);
+        animation.animationTarget = this;
+        animation.motionPaths = motionPaths;
+        
+        if (reverseAnimation)
+            animation.playReversed = true;
+        animation.interpolator = interpolator;
+        animation.repeatCount = repeatCount;
+        animation.repeatDelay = repeatDelay;
+        animation.repeatBehavior = repeatBehavior;
+        animation.easer = easer;
+        animation.startDelay = startDelay;
+        
+        animation.play();
+        if (_seekTime > 0)
+            animation.playheadTime = _seekTime;
+    }
+
+    /**
+     *  @private
+     *  Set the values in the given animation on the properties held in
+     *  motionPaths. This is called by the update and end 
+     *  functions, which are called by the Animation during the animation.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function applyValues(anim:Animation):void
+    {
+        for (var i:int = 0; i < motionPaths.length; ++i)
+        {
+            var prop:String = motionPaths[i].property;
+            setValue(prop, anim.currentValue[prop]);
+        }
+    }
+    
+    // TODO (chaase): This function appears in multiple places. Maybe
+    // put it in some util class instead?
+    /**
+     * @private
+     * 
+     * Utility function to determine whether a given value is 'valid',
+     * which means it's either a Number and it's not NaN, or it's not
+     * a Number and it's not null
+     */
+    private function isValidValue(value:Object):Boolean
+    {
+        return ((value is Number && !isNaN(Number(value))) ||
+            (!(value is Number) && value !== null));
+    }
+    
+    /**
+     * Walk the motionPaths looking for null values. A null indicates
+     * that the value should be replaced by the current value or one that
+     * is calculated from the other value and a supplied delta value.
+     * 
+     * @return Boolean whether this call changed any values in the list
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private function finalizeValues():Boolean
+    {
+        var changedValues:Boolean = false;
+        var j:int;
+        var prevValue:Object;
+        for (var i:int = 0; i < motionPaths.length; ++i)
+        {
+            var motionPath:MotionPath = 
+                MotionPath(motionPaths[i]);
+            // set the first value (if invalid) to the current value
+            // in the target
+            var keyframes:Vector.<Keyframe> = motionPath.keyframes;
+            if (!keyframes || keyframes.length == 0)
+                continue;
+            if (!isValidValue(keyframes[0].value))
+            {
+                if (keyframes.length > 0 &&
+                    isValidValue(keyframes[1].valueBy) &&
+                    isValidValue(keyframes[1].value))
+                {
+                    keyframes[0].value = motionPath.interpolator.decrement(
+                        keyframes[1].value, keyframes[1].valueBy);
+                }
+                else
+                {
+                    // An interrupting transition effect should grab its start
+                    // values from the propertyChanges array, which has been populated
+                    // in UIComponent.commitCurrentState() with the values of target
+                    // objects when the previous transition was interrupted. The
+                    // current values of the objects, from getCurrentValue(), may be
+                    // set to the end values of that previous transition, so we do not
+                    // want those values for the animation
+                    //if ((playReversed || Effect(effect).transitionInterruption) && 
+                    if ((playReversed) && 
+                        propertyChanges &&
+                        propertyChanges.start[motionPath.property] !== undefined)
+                    {
+                        keyframes[0].value = propertyChanges.start[motionPath.property];
+                    }
+                    else
+                    {
+                        keyframes[0].value = getCurrentValue(motionPath.property);
+                    }
+                }
+            }
+            // set any other invalid values based on information in surrounding
+            // keyframes
+            prevValue = keyframes[0].value;
+            for (j = 1; j < keyframes.length; ++j)
+            {
+                var kf:Keyframe = Keyframe(keyframes[j]);
+                if (!isValidValue(kf.value))
+                {
+                    if (isValidValue(kf.valueBy))
+                        kf.value = motionPath.interpolator.increment(prevValue, kf.valueBy);
+                    else
+                    {
+                        // if next keyframe has value and valueBy, use them
+                        if (j <= (keyframes.length - 2) &&
+                            isValidValue(keyframes[j+1].value) &&
+                            isValidValue(keyframes[j+1].valueBy))
+                        {
+                            kf.value = motionPath.interpolator.decrement(
+                                keyframes[j+1].value, keyframes[j+1].valueBy);
+                        }
+                        else if (j == (keyframes.length - 1) &&
+                            propertyChanges &&
+                            propertyChanges.end[motionPath.property] !== undefined)
+                        {
+                            // special case for final keyframe - use state value if it exists
+                            kf.value = propertyChanges.end[motionPath.property];
+                        }
+                        else
+                        {
+                            // otherwise, just use previous keyframe value
+                            kf.value = prevValue;
+                        }
+                    }
+                }
+                prevValue = kf.value;
+            }
+        }
+        return changedValues;
+        
+    }
+
+    /**
+     *  @private
+     *  This function is called by subclasses during the play() function
+     *  to add an animation to the current set of <code>motionPaths</code>.
+     *  The animation will be set up on the named constraint if the constraint
+     *  is in the <code>propertyChanges</code> array (which is only true during
+     *  transitions for properties/styles exposed by the effect) and the
+     *  value of that constraint is different between the start and end states.
+     */ 
+    protected function setupConstraintAnimation(constraintName:String):void
+    {
+        var startVal:* = propertyChanges.start[constraintName];
+        var endVal:* = propertyChanges.end[constraintName];
+        if (startVal !== undefined && endVal !== undefined &&
+            startVal !== null && endVal !== null &&
+            !isNaN(startVal) && !isNaN(endVal) &&
+            startVal != endVal)
+        {
+            motionPaths.push(new SimpleMotionPath(constraintName, startVal, endVal));
+        }
+    }
+
+    /**
+     *  @private
+     *  Called internally to handle start events for the animation.
+     *  If you override this method, ensure that you call the super method.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function animationStart(animation:Animation):void
+    {
+        // Wait until the underlying Animation actually starts (after
+        // any startDelay) to cache constraints and disable layout. This
+        // avoids problems with doing this too early and affecting other
+        // effects that are running before this one.
+        if (disableLayout)
+        {
+            setupParentLayout(false);
+            cacheConstraints();
+        }
+        else if (disabledConstraintsMap)
+        {
+            for (var constraint:String in disabledConstraintsMap)
+                cacheConstraint(constraint);
+            disabledConstraintsMap = null;
+        }
+
+        finalizeValues();
+    }
+    
+    /**
+     *  @private
+     *  Called internally to handle update events for the animation.
+     *  If you override this method, ensure that you call the super method.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function animationUpdate(animation:Animation):void
+    {
+        applyValues(animation);
+        if (numUpdateListeners > 0)
+        {
+            // Only bother dispatching this event if there are listeners. This avoids
+            // unnecessary overhead for the common case of no listeners on this frequent
+            // event
+            //var event:EffectEvent = new EffectEvent(EffectEvent.EFFECT_UPDATE);
+            //event.effectInstance = this;
+            //dispatchEvent(event);
+        }
+    }
+    
+    /**
+     *  @private
+     *  Called internally to handle repeat events for the animation.
+     *  If you override this method, ensure that you call the super method.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function animationRepeat(animation:Animation):void
+    {
+        //var event:EffectEvent = new EffectEvent(EffectEvent.EFFECT_REPEAT);
+        //event.effectInstance = this;
+        //dispatchEvent(event);
+    }    
+
+    private function animationCleanup():void
+    {
+        if (disableLayout)
+        {
+            reenableConstraints();
+            if (disableLayout)
+                setupParentLayout(true);
+        }
+    }
+    
+    /**
+     *  @private
+     *  Called internally to handle end events for the animation. 
+     *  If you override this method, ensure that you call the super method.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function animationEnd(animation:Animation):void
+    {
+        animationCleanup();
+        finishEffect();
+    }
+
+    /**
+     *  @private
+     *  Called internally to handle stop events for the animation. 
+     *  If you override this method, ensure that you call the super method.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function animationStop(animation:Animation):void
+    {
+        animationCleanup();
+    }
+
+    private function noopAnimationHandler(event:Event):void
+    {
+        finishEffect();
+    }
+
+    /**
+     * @private
+     * Track number of listeners to update event for optimization purposes
+     */
+    //override public function addEventListener(type:String, listener:Function, 
+        //useCapture:Boolean=false, priority:int=0, 
+        //useWeakReference:Boolean=false):void
+    //{
+        //super.addEventListener(type, listener, useCapture, priority, 
+            //useWeakReference);
+        //if (type == EffectEvent.EFFECT_UPDATE)
+            //++numUpdateListeners;
+    //}
+    //
+    ///**
+     //* @private
+     //* Track number of listeners to update event for optimization purposes
+     //*/
+    //override public function removeEventListener(type:String, listener:Function, 
+        //useCapture:Boolean=false):void
+    //{
+        //super.removeEventListener(type, listener, useCapture);
+        //if (type == EffectEvent.EFFECT_UPDATE)
+            //--numUpdateListeners;
+    //}
+    
+    /**
+     *  @private
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override public function finishEffect():void
+    {
+        if (autoRemoveTarget)
+            removeDisappearingTarget();
+        super.finishEffect();
+    }
+
+    /**
+     * Adds a target which is not in the state we are transitioning
+     * to. This is the partner of removeDisappearingTarget(), which removes
+     * the target when this effect is finished if necessary.
+     * Note that if a RemoveAction effect is playing in a CompositeEffect,
+     * then the adding/removing is already happening and this function
+     * will noop the add.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private function addDisappearingTarget():void
+    {
+        needsRemoval = false;
+        if (propertyChanges)
+        {
+            // Check for non-null parent ensures that we won't double-remove
+            // items, such as if there is a RemoveAction effect working on
+            // the same target
+            if ("parent" in target && !target.parent)
+            {
+                var parentStart:* = propertyChanges.start["parent"];
+                var parentEnd:* = propertyChanges.end["parent"];
+                if (playReversed)
+                {
+                    var tmp:* = parentStart;
+                    parentStart = parentEnd;
+                    parentEnd = tmp;
+                }
+                if (parentStart && !parentEnd && 
+                    (parentStart is IVisualElementContainer || parentStart is SystemManager))
+                {
+                    var startIndex:* = !playReversed ? 
+                        propertyChanges.start["index"] :
+                        propertyChanges.end["index"];
+                    if (parentStart is IVisualElementContainer)
+                    {
+                        var startContainer:IVisualElementContainer = 
+                            IVisualElementContainer(parentStart);
+                        if (startIndex !== undefined && startIndex <= (startContainer as UIComponent).numElements)
+                            (startContainer as UIComponent).addElementAt(
+                                target as UIComponent, int(startIndex));
+                        else
+                            (startContainer as UIComponent).addElement(target as UIComponent);
+                    }
+                    else
+                    {
+                        var startMgr:SystemManager = parentStart as SystemManager;
+                        if (startIndex !== undefined && startIndex <= startMgr.numChildren)
+                            startMgr.addChildAt(target as UIComponent, int(startIndex));
+                        else
+                            startMgr.addChild(target as UIComponent);
+                    }
+                    // GraphicElements may delay parenting their underlying displayObject until
+                    // a layout pass, so let's force it to make sure we're ready to go
+                    // TODO (chaase): this should probably happen as a part of applyStartValues()
+                    // instead, then we already force the layout to happen. Also, this might
+                    // enable this auto-add functionality to work for Sequence instead of just
+                    // Parallel effects, since applyStartValues() is called at the outer effect
+                    // start time, not just at the inner instance start time.
+                    //if (target is GraphicElement)
+                        //GraphicElement(target).validateNow();
+                    needsRemoval = true;
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes a target which is not in the state we are transitioning
+     * to. This is the partner of addDisappearingTarget(), which re-adds
+     * the target when this effect is played if necessary.
+     * Note that if a RemoveAction effect is playing in a CompositeEffect,
+     * then the adding/removing is already happening and this function
+     * will noop the removal.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private function removeDisappearingTarget():void
+    {
+        if (needsRemoval && propertyChanges)
+        {
+            // Check for non-null parent ensures that we won't double-remove
+            // items, such as if there is a RemoveAction effect working on
+            // the same target
+            if ("parent" in target && target.parent)
+            {
+                var parentStart:* = propertyChanges.start["parent"];
+                var parentEnd:* = propertyChanges.end["parent"];
+                if (playReversed)
+                {
+                    var tmp:* = parentStart;
+                    parentStart = parentEnd;
+                    parentEnd = tmp;
+                }
+                if (parentStart && !parentEnd)
+                {
+                    if (parentStart is UIComponent)
+                        UIComponent(parentStart).removeElement(target as UIComponent);
+                    else
+                        parentStart.removeChild(target);
+                }
+            }
+        }
+    }
+
+    private var constraintsHolder:Object;
+    
+    // TODO (chaase): Use IConstraintClient for this
+    private function reenableConstraint(name:String):void
+    {
+        var value:* = constraintsHolder[name];
+        if (value !== undefined)
+        {
+            if (name in target)
+                target[name] = value;
+            else
+                target.setStyle(name, value);
+            delete constraintsHolder[name];
+        }
+    }
+    
+    private function reenableConstraints():void
+    {
+        // Only bother if constraintsHolder is non-null; otherwise
+        // there must have been no constraints to worry about
+        if (constraintsHolder)
+        {
+            var left:* = reenableConstraint("left");
+            var right:* = reenableConstraint("right");
+            var top:* = reenableConstraint("top");
+            var bottom:* = reenableConstraint("bottom");
+            reenableConstraint("horizontalCenter");
+            reenableConstraint("verticalCenter");
+            reenableConstraint("baseline");
+            constraintsHolder = null;
+            if (left != undefined && right != undefined && "explicitWidth" in target)
+                target.explicitWidth = oldWidth;            
+            if (top != undefined && bottom != undefined && "explicitHeight" in target)
+                target.explicitHeight = oldHeight;
+        }
+    }
+    
+    // TODO (chaase): Use IConstraintClient for this
+    private function cacheConstraint(name:String):*
+    {
+        var isProperty:Boolean = (name in target);
+        var value:*;
+        if (isProperty)
+            value = target[name];
+        else
+            value = target.getStyle(name);
+        if (!isNaN(value) && value != null)
+        {
+            if (!constraintsHolder)
+                constraintsHolder = new Object();
+            constraintsHolder[name] = value;
+            // Now disable it - it will be re-enabled when effect finishes
+            if (isProperty)
+                target[name] = NaN;
+            else if (target is IStyleClient)
+                target.setStyle(name, undefined);
+        }
+        return value;
+    }
+    
+    private function cacheConstraints():void
+    {
+        var left:* = cacheConstraint("left");
+        var right:* = cacheConstraint("right");
+        var top:* = cacheConstraint("top");
+        var bottom:* = cacheConstraint("bottom");
+        cacheConstraint("verticalCenter");
+        cacheConstraint("horizontalCenter");
+        cacheConstraint("baseline");
+        if (left != undefined && right != undefined && "explicitWidth" in target)
+        {
+            var w:Number = target.width;    
+            oldWidth = target.explicitWidth;
+            target.width = w;
+        }
+        if (top != undefined && bottom != undefined && "explicitHeight" in target)
+        {
+            var h:Number = target.height;
+            oldHeight = target.explicitHeight;
+            target.height = h;
+        }
+    }
+
+    /**
+     *  @private
+     *  Utility function to handle situation where values may be queried or
+     *  set on the target prior to completely setting up the effect's
+     *  motionPaths data values (from which the styleMap is created)
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function setupStyleMapEntry(property:String):void
+    {
+        // TODO (chaase): Find a better way to set this up just once
+        if (isStyleMap[property] == undefined)
+        {
+            if (property in target)
+            {
+                isStyleMap[property] = false;
+            }
+            else
+            {
+                try {
+                    target.getStyle(property);
+                    isStyleMap[property] = true;
+                }
+                catch (err:Error)
+                {
+                    throw new Error(resourceManager.getString("sparkEffects", 
+                        "propNotPropOrStyle", [property, target, err])); 
+                }
+            }            
+        }
+    }
+    
+    /**
+     *  Utility function that sets the named property on the target to
+     *  the given value. Handles setting the property as either a true
+     *  property or a style.
+     *  @private
+     */
+    protected function setValue(property:String, value:Object):void
+    {
+        // TODO (chaase): Find a better way to set this up just once
+        setupStyleMapEntry(property);
+        if (!isStyleMap[property])
+            target[property] = value;
+        else
+            target.setStyle(property, value);
+    }
+
+    /**
+     *  Utility function that gets the value of the named property on 
+     *  the target. Handles getting the value of the property as either a true
+     *  property or a style.
+     *  @private
+     */
+    protected function getCurrentValue(property:String):*
+    {
+        // TODO (chaase): Find a better way to set this up just once
+        setupStyleMapEntry(property);
+        if (!isStyleMap[property])
+            return target[property];
+        else
+            return target.getStyle(property);
+    }
+
+    /**
+     * Enables or disables autoLayout in the target's container.
+     * This is used to disable layout during the course of an animation,
+     * and to re-enable it when the animation finishes.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private function setupParentLayout(enable:Boolean):void
+    {
+        var parent:* = null;
+        if ("parent" in target && target.parent)
+        {
+            parent = target.parent;
+        }
+        
+        if (parent && ("autoLayout" in parent))
+            parent.autoLayout = enable;
+    }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/ColorMatrixFilter.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/ColorMatrixFilter.as
index f7d6781..2deab36 100644
--- a/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/ColorMatrixFilter.as
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/ColorMatrixFilter.as
@@ -19,7 +19,6 @@
 
 package spark.filters
 {
-import flash.filters.BitmapFilter;
 //import flash.filters.ColorMatrixFilter;
 import mx.filters.BaseFilter;
 import mx.filters.IBitmapFilter;
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/primitives/supportClasses b/frameworks/projects/SparkRoyale/src/main/royale/spark/primitives/supportClasses
new file mode 100644
index 0000000..0a9eb0e
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/primitives/supportClasses
@@ -0,0 +1,4673 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  ADOBE SYSTEMS INCORPORATED
+//  Copyright 2008 Adobe Systems Incorporated
+//  All Rights Reserved.
+//
+//  NOTICE: Adobe permits you to use, modify, and distribute this file
+//  in accordance with the terms of the license agreement accompanying it.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package spark.primitives.supportClasses
+{
+
+import flash.display.BitmapData;
+import flash.display.BlendMode;
+import flash.display.DisplayObject;
+import flash.display.DisplayObjectContainer;
+import flash.display.Shape;
+import flash.display.Sprite;
+import flash.events.Event;
+import flash.events.EventDispatcher;
+import flash.events.IEventDispatcher;
+import flash.geom.ColorTransform;
+import flash.geom.Matrix;
+import flash.geom.Matrix3D;
+import flash.geom.Point;
+import flash.geom.Rectangle;
+import flash.geom.Transform;
+import flash.geom.Vector3D;
+
+import mx.core.AdvancedLayoutFeatures;
+import mx.core.DesignLayer;
+import mx.core.IInvalidating;
+import mx.core.ILayoutDirectionElement;
+import mx.core.ILayoutElement;
+import mx.core.IMXMLObject;
+import mx.core.IUIComponent;
+import mx.core.IVisualElement;
+import mx.core.LayoutDirection;
+import mx.core.UIComponent;
+import mx.core.UIComponentGlobals;
+import mx.core.mx_internal;
+import mx.events.FlexEvent;
+import mx.events.PropertyChangeEvent;
+import mx.filters.BaseFilter;
+import mx.filters.IBitmapFilter;
+import mx.geom.Transform;
+import mx.geom.TransformOffsets;
+import mx.graphics.shaderClasses.ColorBurnShader;
+import mx.graphics.shaderClasses.ColorDodgeShader;
+import mx.graphics.shaderClasses.ColorShader;
+import mx.graphics.shaderClasses.ExclusionShader;
+import mx.graphics.shaderClasses.HueShader;
+import mx.graphics.shaderClasses.LuminosityShader;
+import mx.graphics.shaderClasses.SaturationShader;
+import mx.graphics.shaderClasses.SoftLightShader;
+import mx.managers.ILayoutManagerClient;
+import mx.utils.MatrixUtil;
+
+import spark.components.supportClasses.InvalidatingSprite;
+import spark.core.DisplayObjectSharingMode;
+import spark.core.IGraphicElement;
+import spark.core.IGraphicElementContainer;
+import spark.core.MaskType;
+import spark.utils.FTETextUtil;
+import spark.utils.MaskUtil;
+
+use namespace mx_internal;
+
+/**
+ *  A base class for defining individual graphic elements. Types of graphic elements include:
+ *  <ul>
+ *   <li>Shapes</li>
+ *   <li>Text</li>
+ *   <li>Raster images</li>
+ *  </ul>
+ *  
+ *  <p>When defining a graphic element, you specify an explicit size for the element; 
+ *  that is, you cannot use percentage sizing as you can when specifying the size of a control.</p>
+ *  
+ *  <p>The TBounds are the boundaries of an
+ *  object in the object's parent coordinate space. The UBounds are the boundaries
+ *  of an object in its own coordinate space.</p>
+ *  
+ *  <p>If you set the transform.matrix declaratively in MXML, then it will override
+ *  the values of any transform properties (rotation, scaleX, scaleY, x, and y).
+ *  If you set the transform.matrix or the transform properties in ActionScript, then
+ *  the last value set will be used.</p>  
+ * 
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class GraphicElement extends EventDispatcher
+    implements IGraphicElement, IInvalidating, ILayoutElement, IVisualElement, IMXMLObject
+{
+    include "../../core/Version.as";
+
+    //--------------------------------------------------------------------------
+    //
+    //  Class constants
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     *  The default value for the <code>maxWidth</code> property.
+     */
+    private static const DEFAULT_MAX_WIDTH:Number = 10000;
+
+    /**
+     *  @private
+     *  The default value for the <code>maxHeight</code> property.
+     */
+    private static const DEFAULT_MAX_HEIGHT:Number = 10000;
+
+     /**
+     *  @private 
+     *  The default value for the <code>minWidth</code> property.
+     */
+    private static const DEFAULT_MIN_WIDTH:Number = 0;
+
+    /**
+     *  @private
+     *  The default value for the <code>minHeight</code> property.
+     */
+    private static const DEFAULT_MIN_HEIGHT:Number = 0;
+
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     */
+    public function GraphicElement()
+    {
+        super();
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Variables
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @private
+     */
+    private var displayObjectChanged:Boolean;
+    
+    /**
+     *  @private
+     */
+    private var _colorTransform:ColorTransform;
+
+    private var colorTransformChanged:Boolean;
+    
+    /**
+     *  @private The Sprite to draw into. 
+     *  If null, then we just use displayObject or sharedDisplayObject
+     */
+    private var _drawnDisplayObject:InvalidatingSprite;
+
+    /**
+     *  @private 
+     *  Whether this element needs to have its
+     *  commitProperties() method called.
+     */
+    mx_internal var invalidatePropertiesFlag:Boolean = false;
+
+    /**
+     *  @private
+     *  Whether this element needs to have its
+     *  measure() method called.
+     */
+    mx_internal var invalidateSizeFlag:Boolean = false;
+
+    /**
+     *  @private
+     *  Whether this element needs to be have its
+     *  updateDisplayList() method called.
+     */
+    mx_internal var invalidateDisplayListFlag:Boolean = false;
+
+
+    /**
+     *  Contain all of the implementation details of how the GraphicElement implements
+     *  transform and layering support. In most cases, you should not have to modify this 
+     *  property.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected var layoutFeatures:AdvancedLayoutFeatures;
+
+
+    /**
+     *  @private
+     *  storage for the x property. This property is used when a GraphicElement has a simple transform.
+     */
+    private var _x:Number = 0;
+
+    /**
+     *  @private
+     *  storage for the y property. This property is used when a GraphicElement has a simple transform.
+     */
+    private var _y:Number = 0;
+
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @copy mx.core.IVisualElement#postLayoutTransformOffsets
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get postLayoutTransformOffsets():TransformOffsets
+    {
+        return (layoutFeatures == null)? null:layoutFeatures.postLayoutTransformOffsets;
+    }
+    
+    /**
+     * @private
+     */
+    public function set postLayoutTransformOffsets(value:TransformOffsets):void
+    {
+        if (value != null)
+            allocateLayoutFeatures();
+        
+        if (layoutFeatures.postLayoutTransformOffsets != null)
+            layoutFeatures.postLayoutTransformOffsets.removeEventListener(Event.CHANGE,transformOffsetsChangedHandler);
+        layoutFeatures.postLayoutTransformOffsets = value;
+        if (layoutFeatures.postLayoutTransformOffsets != null)
+            layoutFeatures.postLayoutTransformOffsets.addEventListener(Event.CHANGE,transformOffsetsChangedHandler);
+    }
+
+    /**
+     *  @private
+     */
+    mx_internal function allocateLayoutFeatures():void
+    {
+        if (layoutFeatures != null)
+            return;
+        layoutFeatures = new AdvancedLayoutFeatures();
+        layoutFeatures.layoutX = _x;
+        layoutFeatures.layoutY = _y;
+        layoutFeatures.layoutWidth = _width;  // for the mirror transform       
+    }
+    
+    /**
+     *  @private
+     */
+    private function invalidateTransform(changeInvalidatesLayering:Boolean = true,
+                                           invalidateLayout:Boolean = true):void
+    {
+        if (changeInvalidatesLayering)
+            invalidateDisplayObjectSharing();
+
+        // Make sure we apply the transform    
+        if (layoutFeatures != null)
+            layoutFeatures.updatePending = true;
+
+        // If we are sharing a display object we need to redraw
+        if (displayObjectSharingMode != DisplayObjectSharingMode.OWNS_UNSHARED_OBJECT)
+            invalidateDisplayList();
+        else
+            invalidateProperties(); // We apply the transform in commitProperties
+
+        // Trigger a layout pass
+        if (invalidateLayout)
+            invalidateParentSizeAndDisplayList();
+        }
+
+    /**
+     * @private
+     */
+    private function transformOffsetsChangedHandler(e:Event):void
+    {
+        invalidateTransform();
+    }
+    
+    //----------------------------------
+    //  alpha
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the alpha property.
+     */
+    private var _alpha:Number = 1.0;
+    private var _effectiveAlpha:Number = 1.0;
+    
+    /**
+     *  @private
+     */
+    private var alphaChanged:Boolean = false;
+    
+    [Inspectable(category="General", minValue="0.0", maxValue="1.0")]
+
+    /**
+     *  The level of transparency of the graphic element. Valid values are decimal values between
+     *  0 (fully transparent) and 1 (fully opaque). For example, a value of .25 means that the 
+     *  element has 25% opacity.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get alpha():Number
+    {
+        return _alpha;
+    }
+
+    /**
+     *  @private
+     */
+    public function set alpha(value:Number):void
+    {    
+        if (_alpha == value)
+            return;
+        
+        var previous:Boolean = needsDisplayObject;
+        _alpha = value;
+        
+    // The product of _alpha and the designLayer's 
+    // alpha is the effectiveAlpha which is 
+    // committed in commitProperties() 
+        if (designLayer)
+            value = value * designLayer.effectiveAlpha; 
+
+        if (_blendMode == "auto")
+        {
+            // If alpha changes from an opaque/transparent (1/0) and translucent
+            // (0 < value < 1), then trigger a blendMode change
+            if ((value > 0 && value < 1 && (_effectiveAlpha == 0 || _effectiveAlpha == 1)) ||
+                ((value == 0 || value == 1) && (_effectiveAlpha > 0 && _effectiveAlpha < 1)))
+            {
+                blendModeChanged = true;
+            }
+        }
+        
+        _effectiveAlpha = value;
+        
+        // Clear the colorTransform flag since alpha was explicitly set
+        var mxTransform:mx.geom.Transform = _transform as mx.geom.Transform;
+        if (mxTransform)
+            mxTransform.applyColorTransformAlpha = false;        
+        
+        if (previous != needsDisplayObject)
+            invalidateDisplayObjectSharing();    
+
+        alphaChanged = true;
+        invalidateProperties();
+    }
+
+    //----------------------------------
+    //  alwaysCreateDisplayObject
+    //----------------------------------
+    
+    private var _alwaysCreateDisplayObject:Boolean;
+    
+    /**
+     *  Specifies that this GraphicElement is to be associated with and be rendered 
+     *  to its own DisplayObject.
+     * 
+     *  @default false
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4.5
+     */
+    public function get alwaysCreateDisplayObject():Boolean
+    {
+        return _alwaysCreateDisplayObject;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set alwaysCreateDisplayObject(value:Boolean):void
+    {
+        if (value != _alwaysCreateDisplayObject)
+        {
+            var previous:Boolean = needsDisplayObject;
+            _alwaysCreateDisplayObject = value;
+            if (previous != needsDisplayObject)
+                invalidateDisplayObjectSharing();
+        }
+    }
+    
+    //----------------------------------
+    //  baseline
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the baseline property.
+     */
+    private var _baseline:Object;
+
+    [Inspectable(category="General")]
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get baseline():Object
+    {
+        return _baseline;
+    }
+
+    /**
+     *  @private
+     */
+    public function set baseline(value:Object):void
+    {
+        if (_baseline == value)
+            return;
+
+        _baseline = value;
+        invalidateParentSizeAndDisplayList();
+    }
+    
+    //----------------------------------
+    //  baselinePosition
+    //----------------------------------
+
+    [Inspectable(category="General")]
+
+    /**
+     *  The y-coordinate of the baseline
+     *  of the first line of text of the component.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get baselinePosition():Number
+    {    
+        // Subclasses of GraphicElement should return something 
+        // here as appropriate (e.g. text centric GraphicElements).
+        var parentUIC:UIComponent = parent as UIComponent;
+        
+        if (parentUIC)
+        {
+            if (!parentUIC.validateBaselinePosition())
+                return NaN;
+            
+            return FTETextUtil.calculateFontBaseline(parentUIC, height, parentUIC.moduleFactory);
+        }
+        
+        return 0;
+    }
+    
+    //----------------------------------
+    //  blendMode
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the blendMode property.
+     */
+    private var _blendMode:String = "auto"; 
+    
+    /**
+     *  @private
+     */
+    private var blendModeChanged:Boolean;
+    private var blendShaderChanged:Boolean; 
+    private var blendModeExplicitlySet:Boolean = false;
+
+    [Inspectable(category="General", enumeration="auto,add,alpha,darken,difference,erase,hardlight,invert,layer,lighten,multiply,normal,subtract,screen,overlay,colordodge,colorburn,exclusion,softlight,hue,saturation,color,luminosity", defaultValue="auto")]
+
+    /**
+     *  A value from the BlendMode class that specifies which blend mode to use. 
+     * 
+     *  @default auto
+     * 
+     *  @see flash.display.DisplayObject#blendMode
+     *  @see flash.display.BlendMode
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get blendMode():String
+    {
+        return _blendMode;
+    }
+
+    /**
+     *  @private
+     */
+    public function set blendMode(value:String):void
+    {
+        if (value == _blendMode)
+            return;
+        
+        var oldValue:String = _blendMode;
+        
+        _blendMode = value;
+        blendModeChanged = true; 
+        
+        // If one of the non-native Flash blendModes is set, 
+        // record the new value and set the appropriate 
+        // blendShader on the display object. 
+        if (isAIMBlendMode(value))
+        {
+            blendShaderChanged = true;
+        }
+        
+        // Only need to re-do display object assignment if blendmode was normal
+        // and is changing to something else, or the blend mode was something else 
+        // and is going back to normal.  This is because display object sharing
+        // only happens when blendMode is normal.
+        if ((oldValue == BlendMode.NORMAL || value == BlendMode.NORMAL) && 
+            !(oldValue == BlendMode.NORMAL && value == BlendMode.NORMAL))
+        {
+            invalidateDisplayObjectSharing();
+        }
+        
+        invalidateProperties(); 
+    }
+
+    //----------------------------------
+    //  bottom
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the bottom property.
+     */
+    private var _bottom:Object;
+
+    [Inspectable(category="General")]
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get bottom():Object
+    {
+        return _bottom;
+    }
+
+    /**
+     *  @private
+     */
+    public function set bottom(value:Object):void
+    {
+        if (_bottom == value)
+            return;
+
+        _bottom = value;
+        invalidateParentSizeAndDisplayList();
+    }
+    
+    //----------------------------------
+    //  owner
+    //----------------------------------
+
+    /**
+     *  @private
+     */
+    private var _owner:DisplayObjectContainer;
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get owner():DisplayObjectContainer
+    {
+        return _owner ? _owner : parent;
+    }
+
+    public function set owner(value:DisplayObjectContainer):void
+    {
+        _owner = value;
+    }
+
+    //----------------------------------
+    //  layer
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the layer property.
+     */
+    private var _designLayer:DesignLayer;
+    
+    [Inspectable (environment='none')]
+    
+    /**
+     *  @copy mx.core.IVisualElement#designLayer
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get designLayer():DesignLayer
+    {
+        return _designLayer;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set designLayer(value:DesignLayer):void
+    {
+        if (_designLayer)
+            _designLayer.removeEventListener("layerPropertyChange", layer_PropertyChange, false);
+        
+        _designLayer = value;
+        
+        if (_designLayer)
+            _designLayer.addEventListener("layerPropertyChange", layer_PropertyChange, false, 0, true);
+        
+        _effectiveAlpha = _designLayer ? _alpha * _designLayer.effectiveAlpha : _alpha;
+        _effectiveVisibility = _designLayer ? _visible && _designLayer.effectiveVisibility : _visible;
+        alphaChanged = true;
+        visibleChanged = true;
+        invalidateProperties();
+    }
+        
+    //----------------------------------
+    //  parent
+    //----------------------------------
+
+    /**
+     *  @private
+     *  Storage for the parent property.
+     */
+    private var _parent:IGraphicElementContainer;
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get parent():DisplayObjectContainer
+    {
+        return _parent as DisplayObjectContainer;
+    }
+    
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function parentChanged(value:IGraphicElementContainer):void
+    {
+        _parent = value;
+        invalidateLayoutDirection();
+        
+        // if we now have a parent and we need to do some invalidation, let our parent know
+        if (parent)
+        {
+            if (invalidatePropertiesFlag)
+                IGraphicElementContainer(parent).invalidateGraphicElementProperties(this);
+            if (invalidateSizeFlag)
+                IGraphicElementContainer(parent).invalidateGraphicElementSize(this);
+            if (invalidateDisplayListFlag)
+                IGraphicElementContainer(parent).invalidateGraphicElementDisplayList(this);
+        }
+    }
+
+    //----------------------------------
+    //  explicitHeight
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the explicitHeight property.
+     */
+    private var _explicitHeight:Number;
+
+    [Inspectable(category="General")]
+
+    /**
+     *  Number that specifies the explicit height of the component,
+     *  in pixels, in the component's coordinates.
+     * 
+     *  @see mx.core.UIComponent#explicitHeight
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get explicitHeight():Number
+    {
+        return _explicitHeight;
+    }
+
+    /**
+     *  @private
+     */
+    public function set explicitHeight(value:Number):void
+    {
+        if (_explicitHeight == value)
+            return;
+
+        // height can be pixel or percent, not both
+        if (!isNaN(value))
+            percentHeight = NaN;
+
+        _explicitHeight = value;
+
+        invalidateSize();
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  explicitMaxHeight
+    //----------------------------------
+
+    /**
+     *  The maximum recommended height of the component to be considered
+     *  by the parent during layout. This value is in the
+     *  component's coordinates, in pixels.
+     * 
+     *  @see mx.core.UIComponent#explicitMaxHeight
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get explicitMaxHeight():Number { return maxHeight; }
+    public function set explicitMaxHeight(value:Number):void { maxHeight = value; }
+
+    //----------------------------------
+    //  explicitMaxWidth
+    //----------------------------------
+
+    /**
+     *  The maximum recommended width of the component to be considered
+     *  by the parent during layout. This value is in the
+     *  component's coordinates, in pixels.
+     * 
+     *  @see mx.core.UIComponent#explicitMaxWidth
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get explicitMaxWidth():Number { return maxWidth; }
+    public function set explicitMaxWidth(value:Number):void { maxWidth = value; }
+
+    //----------------------------------
+    //  explicitMinHeight
+    //----------------------------------
+
+    /**
+     *  The minimum recommended height of the component to be considered
+     *  by the parent during layout. This value is in the
+     *  component's coordinates, in pixels. 
+     * 
+     *  @see mx.core.UIComponent#explicitMinHeight
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get explicitMinHeight():Number { return minHeight; }
+    public function set explicitMinHeight(value:Number):void { minHeight = value; }
+
+    //----------------------------------
+    //  explicitMinWidth
+    //----------------------------------
+
+    /** 
+     *  The minimum recommended width of the component to be considered
+     *  by the parent during layout. This value is in the
+     *  component's coordinates, in pixels. 
+     * 
+     *  @see mx.core.UIComponent#explicitMinWidth
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get explicitMinWidth():Number { return minWidth; }
+    public function set explicitMinWidth(value:Number):void { minWidth = value; }
+
+    //----------------------------------
+    //  explicitWidth
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the explicitHeight property.
+     */
+    private var _explicitWidth:Number;
+
+    [Inspectable(category="General")]
+
+    /**
+     *  Number that specifies the explicit width of the component,
+     *  in pixels, in the component's coordinates.
+     * 
+     *  @see mx.core.UIComponent#explicitWidth
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get explicitWidth():Number
+    {
+        return _explicitWidth;
+    }
+
+    /**
+     *  @private
+     */
+    public function set explicitWidth(value:Number):void
+    {
+        if (_explicitWidth == value)
+            return;
+
+        // height can be pixel or percent, not both
+        if (!isNaN(value))
+            percentWidth = NaN;
+
+        _explicitWidth = value;
+
+        invalidateSize();
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  filters
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the filters property.
+     */
+    private var _filters:Array = [];
+    
+    /**
+     *  @private
+     */
+    private var filtersChanged:Boolean;
+
+    /**
+     *  @private
+     */
+    private var _clonedFilters:Array;  
+    
+    [Inspectable(category="General")]
+
+    /**
+     *  An indexed array that contains each filter object currently associated with the graphic element. 
+     *  The mx.filters package contains classes that define specific filters you can use.
+     *  
+     *  <p>The getter returns a copy of the filters array. The filters property value can only be changed 
+     *  via the setter.</p>
+     * 
+     *  @see spark.filters.BevelFilter
+     *  @see spark.filters.BlurFilter
+     *  @see spark.filters.ColorMatrixFilter
+     *  @see spark.filters.ConvolutionFilter
+     *  @see spark.filters.DisplacementMapFilter
+     *  @see spark.filters.DropShadowFilter
+     *  @see spark.filters.GlowFilter
+     *  @see spark.filters.GradientBevelFilter
+     *  @see spark.filters.GradientFilter
+     *  @see spark.filters.GradientGlowFilter
+     *  @see spark.filters.ShaderFilter
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get filters():Array
+    {
+        // Return a copy of the filters to prevent it from being mutated. 
+        // The only way to change the filters is through the setter. 
+        return _filters.slice();
+    }
+
+    /**
+     *  @private
+     */
+    public function set filters(value:Array):void
+    {
+        var i:int = 0;
+        var len:int = _filters ? _filters.length : 0;
+        var newLen:int = value ? value.length : 0; 
+        var edFilter:IEventDispatcher;
+
+        if (len == 0 && newLen == 0)
+            return;
+
+        // Remove the event listeners on the previous filters
+        for (i = 0; i < len; i++)
+        {
+            edFilter = _filters[i] as IEventDispatcher;
+            if (edFilter)
+                edFilter.removeEventListener(BaseFilter.CHANGE, filterChangedHandler);
+        }
+
+        var previous:Boolean = needsDisplayObject;
+        _filters = value;
+        if (previous != needsDisplayObject)
+            invalidateDisplayObjectSharing();
+        
+        _clonedFilters = [];
+        
+        for (i = 0; i < newLen; i++)
+        {
+            if (value[i] is IBitmapFilter)
+            {
+                edFilter = value[i] as IEventDispatcher;
+                if (edFilter)
+                    edFilter.addEventListener(BaseFilter.CHANGE, filterChangedHandler);
+                _clonedFilters.push(IBitmapFilter(value[i]).clone());
+            }
+            else
+            {
+                _clonedFilters.push(value[i]);
+            }
+        }
+
+        filtersChanged = true;
+        invalidateProperties();
+    }
+
+    //----------------------------------
+    //  height
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the height property.
+     */
+    mx_internal var _height:Number = 0;
+
+    [Bindable("propertyChange")]
+    [Inspectable(category="General")]
+    [PercentProxy("percentHeight")]
+
+    /**
+     *  The height of the graphic element.
+     *
+     *  @default 0
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get height():Number
+    {
+        return _height;
+    }
+
+    /**
+     *  @private
+     */
+    
+    public function set height(value:Number):void
+    {
+        explicitHeight = value;
+
+        if (_height == value)
+            return;
+
+        var oldValue:Number = _height;
+        _height = value;
+        dispatchPropertyChangeEvent("height", oldValue, value);
+
+        // Invalidate the display list, since we're changing the actual height
+        // and we're not going to correctly detect whether the layout sets
+        // new actual height different from our previous value.
+        invalidateDisplayList();
+    }
+
+    //----------------------------------
+    //  horizontalCenter
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the horizontalCenter property.
+     */
+    private var _horizontalCenter:Object;
+
+    [Inspectable(category="General")]
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get horizontalCenter():Object
+    {
+        return _horizontalCenter;
+    }
+
+    /**
+     *  @private
+     */
+    public function set horizontalCenter(value:Object):void
+    {
+        if (_horizontalCenter == value)
+            return;
+
+        _horizontalCenter = value;
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  id
+    //----------------------------------
+
+    /**
+     *  @private
+     *  Storage for the id property.
+     */
+    private var _id:String;
+
+    /**
+     *  The identity of the component.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */ 
+    public function get id():String
+    {
+        return _id;
+    }
+
+    /**
+     *  @private
+     */ 
+    public function set id(value:String):void
+    {
+        _id = value;
+    }
+
+    //----------------------------------
+    //  left
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the left property.
+     */
+    private var _left:Object;
+
+    [Inspectable(category="General")]
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get left():Object
+    {
+        return _left;
+    }
+
+    /**
+     *  @private
+     */
+    public function set left(value:Object):void
+    {
+        if (_left == value)
+            return;
+
+        _left = value;
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  mask
+    //----------------------------------
+
+    /**
+     *  @private
+     *  Storage for the mask property.
+     */
+    private var _mask:DisplayObject;
+    
+    /**
+     *  @private
+     */
+    private var maskChanged:Boolean;
+    
+    [Inspectable(category="General")]
+
+    /**
+     *  The calling display object is masked by the specified mask object. 
+     *  If, the mask display object is not on the display list, it will be added to the display list
+     *  as a child of the displayObject. The mask object itself is not drawn. 
+     *  Set mask to null to remove the mask.
+     * 
+     *  To use another GraphicElement as a mask, wrap the GraphicElement in a Group or other container. 
+     *  
+     *  @see flash.display.DisplayObject#mask
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get mask():DisplayObject
+    {
+        return _mask;
+    }
+
+    /**
+     *  @private
+     */
+    public function set mask(value:DisplayObject):void
+    {
+        if (_mask == value)
+            return;
+
+        var oldMask:UIComponent = _mask as UIComponent;
+
+        var previous:Boolean = needsDisplayObject;
+        _mask = value;      
+
+        // If the old mask was attached by us, then we need to 
+        // undo the attachment logic        
+        if (oldMask && oldMask.$parent === displayObject)
+        {       
+            if (oldMask.parent is UIComponent)
+                UIComponent(oldMask.parent).childRemoved(oldMask);
+            oldMask.$parent.removeChild(oldMask);
+        }     
+        
+        // Cleanup the drawnDisplayObject mask and _drawnDisplayObject here
+        // because displayObject (the parent of _drawnDisplayObject)
+        // might be null in commitProperties
+        if (!_mask || _mask.parent)
+        {
+            if (drawnDisplayObject)
+                drawnDisplayObject.mask = null; 
+            
+            if (_drawnDisplayObject)
+            {
+                if (_drawnDisplayObject.parent)
+                    _drawnDisplayObject.parent.removeChild(_drawnDisplayObject);
+                _drawnDisplayObject = null;
+            }
+        }
+        
+        maskChanged = true;
+        maskTypeChanged = true;
+        if (previous != needsDisplayObject)
+            invalidateDisplayObjectSharing();
+
+        invalidateProperties();
+        invalidateDisplayList();
+    }
+
+    //----------------------------------
+    //  maskType
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the maskType property.
+     */
+    private var _maskType:String = MaskType.CLIP;
+    
+    /**
+     *  @private
+     */
+    private var maskTypeChanged:Boolean;
+
+    [Inspectable(category="General", enumeration="clip,alpha,luminosity", defaultValue="clip")]
+    
+    /**
+     *  <p>Defines how the mask is applied to the GraphicElement.</p> 
+     * 
+     *  <p>The possible values are <code>MaskType.CLIP</code>, <code>MaskType.ALPHA</code>, and 
+     *  <code>MaskType.LUMINOSITY</code>.</p>  
+     * 
+     *  <p><strong>Clip Masking</strong></p>
+     * 
+     *  <p>When masking in clip mode, a clipping masks is reduced to 1-bit.  This means that a mask will 
+     *  not affect the opacity of a pixel in the source content; it either leaves the value unmodified, 
+     *  if the corresponding pixel in the mask is has a non-zero alpha value, or makes it fully 
+     *  transparent, if the mask pixel value has an alpha value of zero.</p>
+     * 
+     *  <p>When clip masking is used, only the actual path and shape vectors and fills defined by the
+     *  mask are used to determine the effect on the source content.  strokes and bitmap filters 
+     *  defined on the mask are ignored.  Any filled region in the mask is considered filled, and renders 
+     *  the source content.  The type and parameters of the fill is irrelevant;  a solid color fill, 
+     *  gradient fill, or bitmap fill in a mask will all render the underlying source content, regardless 
+     *  of the alpha values of the mask fill.</p>
+     *  
+     *  <p>BitmapGraphics are treated as bitmap filled rectangles when used in a clipping mask.  As a 
+     *  result, the alpha channel of the source bitmap is irrelevant when part of a mask -- the bitmap 
+     *  affects the mask in the same manner as solid filled rectangle of equivalent dimensions.</p>
+     * 
+     *  <p><strong>Alpha Masking</strong></p>
+     * 
+     *  <p>In alpha mode, the opacity of each pixel in the source content is multiplied by the opacity 
+     *  of the corresponding region of the mask.  i.e., a pixel in the source content with an opacity of 
+     *  1 that is masked by a region of opacity of .5 will have a resulting opacity of .5.  A source pixel 
+     *  with an opacity of .8 masked by a region with opacity of .5 will have a resulting opacity of .4.</p>
+     * 
+     *  <p>Conceptually, alpha masking is equivalent to rendering the transformed mask and source content 
+     *  into separate RGBA surfaces, and multiplying the alpha channel of the mask content into the alpha 
+     *  channel of the source content.  All of the mask content is rendered into its surface before 
+     *  compositing into the source content's surface. As a result, all FXG features, such as strokes, 
+     *  bitmap filters, and fill opacity will affect the final composited content.</p>
+     * 
+     *  <p>When in alpha mode, the alpha channel of any bitmap data is composited normally into the mask 
+     *  alpha channel, and will affect the final rendered content. This holds true for both BitmapGraphics 
+     *  and bitmap filled shapes and paths.</p>
+     * 
+     *  <p><strong>Luminosity Masking</strong></p>
+     * 
+     *  <p>A luminosity mask, sometimes called a 'soft mask', works very similarly to an alpha mask
+     *  except that both the opacity and RGB color value of a pixel in the source content is multiplied
+     *  by the opacity and RGB color value of the corresponding region in the mask.</p>
+     * 
+     *  <p>Conceptually, luminosity masking is equivalent to rendering the transformed mask and source content 
+     *  into separate RGBA surfaces, and multiplying the alpha channel and the RGB color value of the mask 
+     *  content into the alpha channel and RGB color value of the source content.  All of the mask content is 
+     *  rendered into its surface before compositing into the source content's surface. As a result, all FXG 
+     *  features, such as strokes, bitmap filters, and fill opacity will affect the final composited 
+     *  content.</p>
+     * 
+     *  <p>Luminosity masking is not native to Flash but is common in Adobe Creative Suite tools like Adobe 
+     *  Illustrator and Adobe Photoshop. In order to accomplish the visual effect of a luminosity mask in 
+     *  Flash-rendered content, a graphic element specifying a luminosity mask actually instantiates a shader
+     *  filter that mimics the visual look of a luminosity mask as rendered in Adobe Creative Suite tools.</p>
+     * 
+     *  <p>Objects being masked by luminosity masks can set properties to control the RGB color value and 
+     *  clipping of the mask. See the luminosityInvert and luminosityClip attributes.</p>
+     * 
+     *  @default MaskType.CLIP
+     *  
+     *  @see spark.core.MarkType
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get maskType():String
+    {
+        return _maskType;
+    }
+
+    /**
+     *  @private
+     */
+    public function set maskType(value:String):void
+    {
+        if (_maskType == value)
+            return;
+
+        _maskType = value;
+        maskTypeChanged = true;
+        invalidateProperties();
+    }
+
+    //----------------------------------
+    //  luminosityInvert
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the luminosityInvert property.
+     */
+    private var _luminosityInvert:Boolean = false; 
+    
+    /**
+     *  @private
+     */
+    private var luminositySettingsChanged:Boolean;
+
+    [Inspectable(category="General", enumeration="true,false", defaultValue="false")]
+    
+    /**
+     *  A property that controls the calculation of the RGB 
+     *  color value of a graphic element being masked by 
+     *  a luminosity mask. If true, the RGB color value of a  
+     *  pixel in the source content is inverted and multipled  
+     *  by the corresponding region in the mask. If false, 
+     *  the source content's pixel's RGB color value is used 
+     *  directly. 
+     * 
+     *  @default false 
+     *  @see #maskType 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get luminosityInvert():Boolean
+    {
+        return _luminosityInvert;
+    }
+
+    /**
+     *  @private
+     */
+    public function set luminosityInvert(value:Boolean):void
+    {
+        if (_luminosityInvert == value)
+            return;
+
+        _luminosityInvert = value;
+        luminositySettingsChanged = true; 
+    }
+
+    //----------------------------------
+    //  luminosityClip
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the luminosityClip property.
+     */
+    private var _luminosityClip:Boolean = false; 
+    
+    [Inspectable(category="General", enumeration="true,false", defaultValue="false")]
+    
+    /**
+     *  A property that controls whether the luminosity 
+     *  mask clips the masked content. This property can 
+     *  only have an effect if the graphic element has a 
+     *  mask applied to it that is of type 
+     *  MaskType.LUMINOSITY.  
+     * 
+     *  @default false 
+     *  @see #maskType 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get luminosityClip():Boolean
+    {
+        return _luminosityClip;
+    }
+
+    /**
+     *  @private
+     */
+    public function set luminosityClip(value:Boolean):void
+    {
+        if (_luminosityClip == value)
+            return;
+
+        _luminosityClip = value;
+        luminositySettingsChanged = true; 
+    }
+    
+    //----------------------------------
+    //  maxHeight
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the maxHeight property.
+     */
+    private var _maxHeight:Number;
+
+    [Inspectable(category="General")]
+    
+    /**
+     *  @copy mx.core.UIComponent#maxHeight
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get maxHeight():Number
+    {
+        return !isNaN(_maxHeight) ? _maxHeight : DEFAULT_MAX_HEIGHT;
+    }
+
+    /**
+     *  @private
+     */
+    public function set maxHeight(value:Number):void
+    {
+        if (_maxHeight == value)
+            return;
+
+        _maxHeight = value;
+
+        invalidateSize();
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  maxWidth
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the maxHeight property.
+     */
+    mx_internal var _maxWidth:Number;
+
+    [Inspectable(category="General")]
+    
+    /**
+     *  @copy mx.core.UIComponent#maxWidth
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get maxWidth():Number
+    {
+        return !isNaN(_maxWidth) ? _maxWidth : DEFAULT_MAX_WIDTH;
+    }
+
+    /**
+     *  @private
+     */
+    public function set maxWidth(value:Number):void
+    {
+        if (_maxWidth == value)
+            return;
+
+        _maxWidth = value;
+
+        invalidateSize();
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  measuredHeight
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the measuredHeight property.
+     */
+    private var _measuredHeight:Number = 0;
+    
+    /**
+     *  @copy mx.core.UIComponent#measuredHeight
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get measuredHeight():Number
+    {
+        return _measuredHeight;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set measuredHeight(value:Number):void
+    {
+        _measuredHeight = value;
+    }
+
+    //----------------------------------
+    //  measuredWidth
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the measuredWidth property.
+     */
+    private var _measuredWidth:Number = 0;
+    
+    /**
+     *  @copy mx.core.UIComponent#measuredWidth
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get measuredWidth():Number
+    {
+        return _measuredWidth;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set measuredWidth(value:Number):void
+    {
+        _measuredWidth = value;
+    }
+
+    //----------------------------------
+    //  measuredX
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the measuredX property.
+     */
+    private var _measuredX:Number = 0;
+    
+    /**
+     *  The default measured bounds top-left corner relative to the origin of the element.     
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get measuredX():Number
+    {
+        return _measuredX;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set measuredX(value:Number):void
+    {
+        _measuredX = value;
+    }
+
+    //----------------------------------
+    //  measuredY
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the measuredY property.
+     */
+    private var _measuredY:Number = 0;
+    
+    /**
+     *  The default measured bounds top-left corner relative to the origin of the element.     
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get measuredY():Number
+    {
+        return _measuredY;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set measuredY(value:Number):void
+    {
+        _measuredY = value;
+    }
+
+    //----------------------------------
+    //  minHeight
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the minHeight property.
+     */
+    private var _minHeight:Number;
+
+    [Inspectable(category="General")]
+    
+    /**
+     *  @copy mx.core.UIComponent#minHeight
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get minHeight():Number
+    {
+        return !isNaN(_minHeight) ? _minHeight : DEFAULT_MIN_HEIGHT;
+    }
+
+    /**
+     *  @private
+     */
+    public function set minHeight(value:Number):void
+    {
+        if (_minHeight == value)
+            return;
+
+        _minHeight = value;
+
+        invalidateSize();
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  minWidth
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the minWidth property.
+     */
+    private var _minWidth:Number;
+
+    [Inspectable(category="General")]
+    
+    /**
+     *  @copy mx.core.UIComponent#minWidth
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get minWidth():Number
+    {
+        return !isNaN(_minWidth) ? _minWidth : DEFAULT_MIN_WIDTH;
+    }
+
+    /**
+     *  @private
+     */
+    public function set minWidth(value:Number):void
+    {
+        if (_minWidth == value)
+            return;
+
+        _minWidth = value;
+
+        invalidateSize();
+        invalidateParentSizeAndDisplayList();
+    }
+    
+    //----------------------------------
+    //  percentHeight
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the percentHeight property.
+     */
+    private var _percentHeight:Number;
+
+    [Inspectable(category="General")]
+    
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get percentHeight():Number
+    {
+        return _percentHeight;
+    }
+
+    /**
+     *  @private
+     */
+    public function set percentHeight(value:Number):void
+    {
+        if (_percentHeight == value)
+            return;
+
+        if (!isNaN(value))
+            explicitHeight = NaN;
+
+        _percentHeight = value;
+
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  percentWidth
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the percentWidth property.
+     */
+    private var _percentWidth:Number;
+
+    [Inspectable(category="General")]
+    
+    /**
+     *  @copy mx.core.UIComponent#percentWidth
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get percentWidth():Number
+    {
+        return _percentWidth;
+    }
+
+    /**
+     *  @private
+     */
+    public function set percentWidth(value:Number):void
+    {
+        if (_percentWidth == value)
+            return;
+
+        if (!isNaN(value))
+            explicitWidth = NaN;
+
+        _percentWidth = value;
+
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  right
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the right property.
+     */
+    private var _right:Object;
+
+    [Inspectable(category="General")]
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get right():Object
+    {
+        return _right;
+    }
+
+    /**
+     *  @private
+     */
+    public function set right(value:Object):void
+    {
+        if (_right == value)
+            return;
+
+        _right = value;
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  rotation
+    //----------------------------------
+
+    [Inspectable(category="General")]
+    
+    /**
+     * Indicates the x-axis rotation of the element instance, in degrees, from its original orientation 
+     * relative to the 3D parent container. Values from 0 to 180 represent clockwise rotation; values 
+     * from 0 to -180 represent counterclockwise rotation. Values outside this range are added to or subtracted from 
+     * 360 to obtain a value within the range.
+     * 
+     * This property is ignored during calculation by any of Flex's 2D layouts. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get rotationX():Number
+    {
+        return (layoutFeatures == null)? 0:layoutFeatures.layoutRotationX;
+    }
+
+    /**
+     *  @private
+     */
+    public function set rotationX(value:Number):void
+    {
+        if (rotationX == value)
+            return;
+
+        allocateLayoutFeatures();
+        var previous:Boolean = needsDisplayObject;
+        layoutFeatures.layoutRotationX = value;
+        invalidateTransform(previous != needsDisplayObject); 
+    }
+
+    [Inspectable(category="General")]
+    
+    /**
+     * Indicates the y-axis rotation of the DisplayObject instance, in degrees, from its original orientation 
+     * relative to the 3D parent container. Values from 0 to 180 represent clockwise rotation; values 
+     * from 0 to -180 represent counterclockwise rotation. Values outside this range are added to or subtracted from 
+     * 360 to obtain a value within the range.
+     * 
+     * This property is ignored during calculation by any of Flex's 2D layouts. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get rotationY():Number
+    {
+        return (layoutFeatures == null)? 0:layoutFeatures.layoutRotationY;
+    }
+    /**
+     *  @private
+     */
+    public function set rotationY(value:Number):void
+    {
+        if (rotationY == value)
+            return;
+        
+        allocateLayoutFeatures();
+        var previous:Boolean = needsDisplayObject;
+        layoutFeatures.layoutRotationY = value;
+        invalidateTransform(previous != needsDisplayObject);
+    }
+    
+    [Inspectable(category="General")]
+    
+    /**
+     *  Indicates the rotation of the element, in degrees,
+     *  from the transform point.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get rotationZ():Number
+    {
+        return (layoutFeatures == null)? 0:layoutFeatures.layoutRotationZ;
+    }
+
+    /**
+     *  @private
+     */
+    public function set rotationZ(value:Number):void
+    {
+        if (rotationZ == value)
+            return;
+        
+        allocateLayoutFeatures();
+        var previous:Boolean = needsDisplayObject;
+        layoutFeatures.layoutRotationZ = value;
+        invalidateTransform(previous != needsDisplayObject);
+    }
+
+    [Inspectable(category="General")]
+    
+    /**
+     *  Indicates the rotation of the element, in degrees,
+     *  from the transform point.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get rotation():Number
+    {
+        return (layoutFeatures == null)? 0:layoutFeatures.layoutRotationZ;
+    }
+
+    /**
+     *  @private
+     */
+    public function set rotation(value:Number):void
+    {
+        rotationZ = value;
+    }
+
+    //----------------------------------
+    //  scaleX
+    //----------------------------------
+
+    [Inspectable(category="General")]
+
+    /**
+     *  The horizontal scale (percentage) of the element
+     *  as applied from the transform point.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get scaleX():Number
+    {
+        return (layoutFeatures == null)? 1:layoutFeatures.layoutScaleX;
+    }
+
+    /**
+     *  @private
+     */
+    public function set scaleX(value:Number):void
+    {
+        if (scaleX == value)
+            return;
+        
+        allocateLayoutFeatures();
+        var previous:Boolean = needsDisplayObject;
+        layoutFeatures.layoutScaleX = value;
+        invalidateTransform(previous != needsDisplayObject);
+    }
+
+    //----------------------------------
+    //  scaleY
+    //----------------------------------
+
+    [Inspectable(category="General")]
+
+    /**
+     *  The vertical scale (percentage) of the element
+     *  as applied from the transform point.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get scaleY():Number
+    {
+        return (layoutFeatures == null)? 1:layoutFeatures.layoutScaleY;
+    }
+
+    /**
+     *  @private
+     */
+    public function set scaleY(value:Number):void
+    {
+        if (scaleY == value)
+            return;
+            
+        allocateLayoutFeatures();
+        var previous:Boolean = needsDisplayObject;
+        layoutFeatures.layoutScaleY = value;
+        invalidateTransform(previous != needsDisplayObject);
+    }
+
+    //----------------------------------
+    //  scaleZ
+    //----------------------------------
+
+    [Inspectable(category="General")]
+
+    /**
+     *  The z scale (percentage) of the element
+     *  as applied from the transform point.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get scaleZ():Number
+    {
+        return (layoutFeatures == null)? 1:layoutFeatures.layoutScaleZ;
+    }
+
+    /**
+     *  @private
+     */
+    public function set scaleZ(value:Number):void
+    {
+        if (scaleZ == value)
+            return;
+        
+        allocateLayoutFeatures();
+        var previous:Boolean = needsDisplayObject;
+        layoutFeatures.layoutScaleZ = value;
+        invalidateTransform(previous != needsDisplayObject);    
+    }
+
+    //----------------------------------
+    //  top
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the top property.
+     */
+    private var _top:Object;
+
+    [Inspectable(category="General")]
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get top():Object
+    {
+        return _top;
+    }
+
+    /**
+     *  @private
+     */
+    public function set top(value:Object):void
+    {
+        if (_top == value)
+            return;
+
+        _top = value;
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  transform
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the transform property.
+     */
+    private var _transform:flash.geom.Transform;
+
+    /**
+     *  @copy mx.core.IFlexDisplayObject#transform
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get transform():flash.geom.Transform
+    {
+        if (!_transform) 
+            setTransform(new mx.geom.Transform());
+            
+        return _transform;
+    }
+
+    /**
+     *  @private
+     */
+    public function set transform(value:flash.geom.Transform):void
+    {
+        // TODO (jszeto): Add perspectiveProjection support
+        var matrix:Matrix = value && value.matrix ? value.matrix.clone() : null;
+        var matrix3D:Matrix3D = value && value.matrix3D ? value.matrix3D.clone() : null;
+        var colorTransform:ColorTransform = value ? value.colorTransform : null;
+        
+        var mxTransform:mx.geom.Transform = value as mx.geom.Transform; 
+        if (mxTransform)
+        {
+            if (!mxTransform.applyMatrix)
+                matrix = null;
+            
+            if (!mxTransform.applyMatrix3D)
+                matrix3D = null;
+        }
+        
+        setTransform(value);
+
+        var previous:Boolean = needsDisplayObject;
+
+        if (_transform)
+        {
+            allocateLayoutFeatures();
+
+            if (matrix != null)
+            {
+                layoutFeatures.layoutMatrix = matrix;
+            }
+            else if (matrix3D != null)
+            {    
+                layoutFeatures.layoutMatrix3D = matrix3D;
+            }          
+        }
+        
+             
+        applyColorTransform(colorTransform, mxTransform && mxTransform.applyColorTransformAlpha);
+
+        invalidateTransform(previous != needsDisplayObject);
+    }
+
+    /**
+     * @private
+     */ 
+    private function setTransform(value:flash.geom.Transform):void
+    {
+        // Clean up the old transform
+        var oldTransform:mx.geom.Transform = _transform as mx.geom.Transform;
+        if (oldTransform)
+            oldTransform.target = null;
+
+        var newTransform:mx.geom.Transform = value as mx.geom.Transform;
+
+        if (newTransform)
+            newTransform.target = this;
+
+        _transform = value;
+    }
+    
+    /**
+     * @private
+     * 
+     * Sets the colorTransform property of the transform. Called by mx.geom.Transform
+     * when its colorTransform property has been changed.   
+     */
+    public function setColorTransform(value:ColorTransform):void
+    {
+        applyColorTransform(value, true);
+    }
+    
+    /**
+     * @private
+     */
+    private function applyColorTransform(value:ColorTransform, updateAlpha:Boolean):void
+    {
+        if (_colorTransform != value)
+        {
+            var previous:Boolean = needsDisplayObject;
+            // Make a copy of the colorTransform
+            _colorTransform = new ColorTransform(value.redMultiplier, value.greenMultiplier, value.blueMultiplier, value.alphaMultiplier,
+                value.redOffset, value.greenOffset, value.blueOffset, value.alphaOffset);
+            
+            if (updateAlpha)
+            {
+                _alpha = value.alphaMultiplier;
+                _effectiveAlpha = _alpha;
+            }
+            
+            if (displayObject && displayObjectSharingMode == DisplayObjectSharingMode.OWNS_UNSHARED_OBJECT)
+            {
+                displayObject.transform.colorTransform = _colorTransform;
+            }
+            else
+            {
+                colorTransformChanged = true;
+                invalidateProperties();
+                if (previous != needsDisplayObject)
+                    invalidateDisplayObjectSharing();       
+            }
+        }
+    }
+    
+    /**
+     * @private
+     */
+    private function isAIMBlendMode(value:String):Boolean
+    {
+        if (value == "colordodge" || 
+            value =="colorburn" || value =="exclusion" || 
+            value =="softlight" || value =="hue" || 
+            value =="saturation" || value =="color" ||
+            value =="luminosity")
+            return true; 
+        else return false; 
+    }
+    
+    /**
+     *  @copy mx.core.ILayoutElement#transformAround()
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function transformAround(transformCenter:Vector3D,
+                                    scale:Vector3D = null,
+                                    rotation:Vector3D = null,
+                                    translation:Vector3D = null,
+                                    postLayoutScale:Vector3D = null,
+                                    postLayoutRotation:Vector3D = null,
+                                    postLayoutTranslation:Vector3D = null,
+                                    invalidateLayout:Boolean = true):void
+    {
+        // TODO (egreenfi): optimize for simple translations
+        allocateLayoutFeatures();
+        var previous:Boolean = needsDisplayObject;
+        var prevX:Number = layoutFeatures.layoutX;
+        var prevY:Number = layoutFeatures.layoutY;
+        var prevZ:Number = layoutFeatures.layoutZ;
+        layoutFeatures.transformAround(transformCenter,scale,rotation,translation,postLayoutScale,postLayoutRotation,postLayoutTranslation);
+        invalidateTransform(previous != needsDisplayObject, invalidateLayout);
+        if (prevX != layoutFeatures.layoutX)
+            dispatchPropertyChangeEvent("x", prevX, layoutFeatures.layoutX);
+        if (prevY != layoutFeatures.layoutY)
+            dispatchPropertyChangeEvent("y", prevY, layoutFeatures.layoutY);
+        if (prevZ != layoutFeatures.layoutZ)
+            dispatchPropertyChangeEvent("z", prevZ, layoutFeatures.layoutZ);
+    }
+    
+    /**
+     * A utility method to transform a point specified in the local
+     * coordinates of this object to its location in the object's parent's 
+     * coordinates. The pre-layout and post-layout result will be set on 
+     * the <code>position</code> and <code>postLayoutPosition</code>
+     * parameters, if they are non-null.
+     * 
+     * @param localPosition The point to be transformed, specified in the
+     * local coordinates of the object.
+     * @param position A Vector3D point that will hold the pre-layout
+     * result. If null, the parameter is ignored.
+     * @param postLayoutPosition A Vector3D point that will hold the post-layout
+     * result. If null, the parameter is ignored.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function transformPointToParent(localPosition:Vector3D,
+                                           position:Vector3D, 
+                                           postLayoutPosition:Vector3D):void
+    {
+        if (layoutFeatures != null)
+        {
+            layoutFeatures.transformPointToParent(true, localPosition, position,
+                postLayoutPosition);
+        }
+        else
+        {
+            var xformPt:Point = new Point();
+            if (localPosition)
+            {
+                xformPt.x = localPosition.x;
+                xformPt.y = localPosition.y;
+            }
+            if (position != null)
+            {            
+                position.x = xformPt.x + _x;
+                position.y = xformPt.y + _y;
+                position.z = 0;
+            }
+            if (postLayoutPosition != null)
+            {
+                postLayoutPosition.x = xformPt.x + _x;
+                postLayoutPosition.y = xformPt.y + _y;
+                postLayoutPosition.z = 0;
+            }
+        }
+    }
+
+    //----------------------------------
+    //  transformX
+    //----------------------------------
+    
+    [Inspectable(category="General")]
+
+    /**
+     *  The x position transform point of the element.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get transformX():Number
+    {
+        return (layoutFeatures == null)? 0:layoutFeatures.transformX;
+    }
+
+    /**
+     *  @private
+     */
+    public function set transformX(value:Number):void
+    {
+        if (transformX  == value)
+            return;
+            
+        allocateLayoutFeatures();
+        layoutFeatures.transformX = value;
+        invalidateTransform(false);
+    }
+
+    //----------------------------------
+    //  transformY
+    //----------------------------------
+    
+    [Inspectable(category="General")]
+
+    /**
+     *  The y position transform point of the element.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get transformY():Number
+    {
+        return (layoutFeatures == null)? 0:layoutFeatures.transformY;
+    }
+
+    /**
+     *  @private
+     */
+    public function set transformY(value:Number):void
+    {
+        if (transformY == value)
+            return;
+
+        allocateLayoutFeatures();
+        layoutFeatures.transformY = value;
+        invalidateTransform(false);
+    }
+
+    //----------------------------------
+    //  transformZ
+    //----------------------------------
+    
+    [Inspectable(category="General")]
+
+    /**
+     *  The z position transform point of the element.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get transformZ():Number
+    {
+        return (layoutFeatures == null)? 0:layoutFeatures.transformZ;
+    }
+
+    /**
+     *  @private
+     */
+    public function set transformZ(value:Number):void
+    {
+        if (transformZ == value)
+            return;
+
+        allocateLayoutFeatures();
+        var previous:Boolean = needsDisplayObject;
+        layoutFeatures.transformZ = value;
+        invalidateTransform(previous != needsDisplayObject);
+    }
+
+    //----------------------------------
+    //  verticalCenter
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the verticalCenter property.
+     */
+    private var _verticalCenter:Object;
+
+    [Inspectable(category="General")]
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get verticalCenter():Object
+    {
+        return _verticalCenter;
+    }
+
+    /**
+     *  @private
+     */
+    public function set verticalCenter(value:Object):void
+    {
+        if (_verticalCenter == value)
+            return;
+
+        _verticalCenter = value;
+        invalidateParentSizeAndDisplayList();
+    }
+
+    //----------------------------------
+    //  width
+    //----------------------------------
+
+    /**
+     *  @private
+     *  Storage for the width property.
+     */
+    mx_internal var _width:Number = 0;
+
+    [Bindable("propertyChange")]
+    [Inspectable(category="General")]
+    [PercentProxy("percentWidth")]
+
+    /**
+     *  The width of the graphic element.
+     *
+     *  @default 0
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get width():Number
+    {
+        return _width;
+    }
+
+    /**
+     *  @private
+     */
+    public function set width(value:Number):void
+    {
+        explicitWidth = value;
+
+        if (_width == value)
+            return;
+
+        var oldValue:Number = _width;
+        _width = value;
+
+        // The width is needed for the mirroring transform.
+        if (layoutFeatures)
+        {
+            layoutFeatures.layoutWidth = value;
+            invalidateTransform();
+        }        
+
+        dispatchPropertyChangeEvent("width", oldValue, value);
+
+        // Invalidate the display list, since we're changing the actual width
+        // and we're not going to correctly detect whether the layout sets
+        // new actual width different from our previous value.
+        invalidateDisplayList();
+    }
+
+    //----------------------------------
+    //  depth
+    //----------------------------------  
+
+    /**
+     *  @inheritDoc 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get depth():Number
+    {
+        return (layoutFeatures == null) ? 0 : layoutFeatures.depth;
+    }
+
+    /**
+     *  @private
+     */
+    public function set depth(value:Number):void
+    {
+        if (value == depth)
+            return;
+
+        allocateLayoutFeatures();
+        layoutFeatures.depth = value;  
+        if (_parent is UIComponent)
+            UIComponent(_parent).invalidateLayering();
+        invalidateProperties();
+    }
+
+    //----------------------------------
+    //  x
+    //----------------------------------  
+
+    [Bindable("propertyChange")]
+    [Inspectable(category="General")]
+    
+    /**
+     *  The x position of the graphic element.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get x():Number
+    {
+        return (layoutFeatures == null)? _x:layoutFeatures.layoutX;
+    }
+
+    /**
+     *  @private
+     */
+    public function set x(value:Number):void
+    {
+        var oldValue:Number = x;
+        if (oldValue == value)
+            return;
+
+        if (layoutFeatures != null)
+            layoutFeatures.layoutX = value;
+        else
+            _x = value;
+            
+        dispatchPropertyChangeEvent("x", oldValue, value);
+        invalidateTransform(false);
+    }
+
+    //----------------------------------
+    //  y
+    //----------------------------------   
+
+    [Bindable("propertyChange")]
+    [Inspectable(category="General")]
+    
+    /**
+     *  The y position of the graphic element.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get y():Number
+    {
+        return (layoutFeatures == null)? _y:layoutFeatures.layoutY;
+    }
+
+    /**
+     *  @private
+     */
+    public function set y(value:Number):void
+    {
+        var oldValue:Number = y;
+        if (oldValue == value)
+            return;
+
+        if (layoutFeatures != null)
+            layoutFeatures.layoutY = value;
+        else
+            _y = value;
+        dispatchPropertyChangeEvent("y", oldValue, value);
+        invalidateTransform(false);
+    }
+
+    //----------------------------------
+    //  z
+    //----------------------------------   
+
+    [Bindable("propertyChange")]
+    [Inspectable(category="General")]
+    
+    /**
+     *  The z position of the graphic element.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get z():Number
+    {
+        return (layoutFeatures == null)? 0:layoutFeatures.layoutZ;
+    }
+
+    /**
+     *  @private
+     */
+    public function set z(value:Number):void
+    {
+        if (z == value)
+            return;
+        var oldValue:Number = z;
+        
+        allocateLayoutFeatures();
+        var previous:Boolean = needsDisplayObject;
+        layoutFeatures.layoutZ = value;
+        invalidateTransform(previous != needsDisplayObject);
+        dispatchPropertyChangeEvent("z", oldValue, value);
+    }
+
+    //----------------------------------
+    //  visible
+    //----------------------------------
+
+    /**
+     *  @private
+     *  Storage for the visible property.
+     */
+    private var _visible:Boolean = true;
+    
+    
+    /**
+     *  @private
+     *  The actual 'effective' visibility of this
+     *  element, one that considers the visibility of
+     *  the owning design layer parent (if any).
+     */
+    protected var _effectiveVisibility:Boolean = true;
+    
+    /**
+     *  @private
+     */
+    private var visibleChanged:Boolean;
+
+    [Inspectable(category="General")]
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get visible():Boolean
+    {
+        return _visible;
+    }
+
+    /**
+     *  @private
+     */
+    public function set visible(value:Boolean):void
+    {
+        _visible = value;
+        
+        if (designLayer && !designLayer.effectiveVisibility)
+            value = false; 
+        
+        if (_effectiveVisibility == value)
+            return;
+        
+        _effectiveVisibility = value;
+        visibleChanged = true;
+        invalidateProperties();
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Properties: IDisplayObjectElement
+    //
+    //--------------------------------------------------------------------------
+
+    //----------------------------------
+    //  displayObject
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage for the displayObject property.
+     */
+    private var _displayObject:DisplayObject;
+
+    [Bindable("propertyChange")]
+    [Inspectable(category="General")]
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get displayObject():DisplayObject
+    {
+        return _displayObject;
+    }
+
+    /**
+     *  @private
+     */
+    protected function setDisplayObject(value:DisplayObject):void
+    {
+        if (_displayObject == value)
+            return;
+
+        var oldValue:DisplayObject = _displayObject;
+
+        // If we owned the old display object and we have assigned a 3D matrix,
+        // clear it from the display object so that we can set it in the new
+        // display object. A Matrix3D object can't be used simultaneously with
+        // more than one display object.
+        if (oldValue && displayObjectSharingMode == DisplayObjectSharingMode.OWNS_UNSHARED_OBJECT)
+            oldValue.transform.matrix3D = null;
+
+        _displayObject = value;
+        dispatchPropertyChangeEvent("displayObject", oldValue, value);
+
+        // We need to apply the display object related properties.
+        displayObjectChanged = true;
+        invalidateProperties();
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+
+    //----------------------------------
+    //  drawX
+    //----------------------------------
+
+    /**
+     *  The x position where the element should be drawn.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function get drawX():Number
+    {
+        // If we don't share the display object, we will draw at 0,0
+        // since the display object will be positioned at x,y
+        if (displayObjectSharingMode == DisplayObjectSharingMode.OWNS_UNSHARED_OBJECT)
+            return 0;
+            
+        // Otherwise we draw at x,y since the display object will be
+        // positioned at 0,0
+        if (layoutFeatures != null && layoutFeatures.postLayoutTransformOffsets != null)
+            return x + layoutFeatures.postLayoutTransformOffsets.x;
+            
+        return x;
+    } 
+        
+    //----------------------------------
+    //  drawY
+    //----------------------------------
+
+    /**
+     *  The y position where the element should be drawn.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function get drawY():Number
+    {
+        // If we don't share the display object, we will draw at 0,0
+        // since the display object will be positioned at x,y
+        if (displayObjectSharingMode == DisplayObjectSharingMode.OWNS_UNSHARED_OBJECT)
+            return 0;
+            
+        // Otherwise we draw at x,y since the display object will be
+        // positioned at 0,0
+        if (layoutFeatures != null && layoutFeatures.postLayoutTransformOffsets != null)
+            return y + layoutFeatures.postLayoutTransformOffsets.y;
+            
+        return y;
+    }
+    
+    //----------------------------------
+    //  hasComplexLayoutMatrix
+    //----------------------------------
+    
+    /**
+     *  Returns true if the GraphicElement has any non-translation (x,y) transform properties
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function get hasComplexLayoutMatrix():Boolean
+    {
+        return (layoutFeatures == null ? false : !MatrixUtil.isDeltaIdentity(layoutFeatures.layoutMatrix));
+    }
+    
+    //----------------------------------
+    //  includeInLayout
+    //----------------------------------
+
+    /**
+     *  @private
+     *  Storage for the includeInLayout property.
+     */
+    private var _includeInLayout:Boolean = true;
+
+    [Inspectable(category="General", defaultValue="true")]
+
+    /**
+     *  Specifies whether this element is included in the layout of the parent.
+     *
+     *  @default true
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get includeInLayout():Boolean
+    {
+        return _includeInLayout;
+    }
+
+    /**
+     *  @private
+     */
+    public function set includeInLayout(value:Boolean):void
+    {
+        if (_includeInLayout == value)
+            return;
+
+        // Temporarily set includeInLayout to true so that
+        // invalidating the parent doesn't return early.
+        _includeInLayout = true;
+        invalidateParentSizeAndDisplayList();
+
+        _includeInLayout = value;
+    }
+
+    //----------------------------------
+    //  displayObjectSharingMode
+    //----------------------------------
+
+    private var _displayObjectSharingMode:String;
+    
+    [Inspectable(category="General", enumeration="ownsUnsharedObject,ownsSharedObject,usesSharedObject")]
+    
+    /**
+     *  @private
+     */
+    public function set displayObjectSharingMode(value:String):void
+    {
+        if (value == _displayObjectSharingMode)
+            return;
+        
+        if (value != DisplayObjectSharingMode.USES_SHARED_OBJECT ||
+            _displayObjectSharingMode != DisplayObjectSharingMode.USES_SHARED_OBJECT) 
+        {
+            // If the element was previously at the head of the shared sequence or
+            // it is assigned to be at the head, make sure to reapply the 
+            // displayObject specific properties.
+            displayObjectChanged = true;
+            invalidateProperties();            
+        }
+        _displayObjectSharingMode = value;
+    }
+    
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get displayObjectSharingMode():String
+    {
+        return _displayObjectSharingMode;    
+    }
+    
+    //----------------------------------
+    //  layoutDirection
+    //----------------------------------
+    
+    private var _layoutDirection:String = null;
+    
+    [Inspectable(category="General", enumeration="ltr,rtl")]
+    
+    /**
+     *  @inheritDoc
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get layoutDirection():String
+    {
+        if (_layoutDirection != null)
+            return _layoutDirection;
+        
+        const parentElt:ILayoutDirectionElement = parent as ILayoutDirectionElement;
+        return (parentElt) ? parentElt.layoutDirection : LayoutDirection.LTR; 
+    }
+    
+    /**
+     *  @private
+     */
+    public function set layoutDirection(value:String):void
+    {
+        if (_layoutDirection == value)
+            return;
+        
+        _layoutDirection = value;
+        invalidateLayoutDirection();
+    }
+    
+    /**
+     * @inheritDoc
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function invalidateLayoutDirection():void
+    {
+        const parentElt:ILayoutDirectionElement = parent as ILayoutDirectionElement;
+        if (!parentElt)
+            return;
+        
+        // If this element's layoutDirection doesn't match its parent's, then
+        // set the layoutFeatures.mirror flag.  Similarly, if mirroring isn't 
+        // required, then clear the layoutFeatures.mirror flag.
+        
+        const mirror:Boolean = (parentElt.layoutDirection != null && _layoutDirection != null) 
+            && (_layoutDirection != parentElt.layoutDirection);
+        
+        if ((layoutFeatures) ? (mirror != layoutFeatures.mirror) : mirror)
+        {
+            if (layoutFeatures == null)
+                allocateLayoutFeatures();
+            var previous:Boolean = needsDisplayObject;
+            layoutFeatures.mirror = mirror;
+            invalidateTransform(previous != needsDisplayObject);         
+        }
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Called automatically by the MXML compiler when the GraphicElement
+     *  is created using an MXML tag.
+     *  If you create the GraphicElement through ActionScript you must set the
+     * <code>id</code> property manually.
+     *
+     *  @param document The MXML document containing this GraphicElement (not used).
+     *  @param id The MXML id for this GraphicElement.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function initialized(document:Object, id:String):void
+    {
+        this.id = id;
+    }
+
+    /**
+     * Converts the point object from the object's (local) coordinates 
+     * to the Stage (global) coordinates.
+     * 
+     * <p>This method allows you to convert any given x and y coordinates from 
+     * values that are relative to the origin (0,0) of a specific object 
+     * (local coordinates) to values that are relative to the origin 
+     * of the Stage (global coordinates).</p>
+     * 
+     * <p>To use this method, first create an instance of the Point class. 
+     * The x and y values that you assign represent local coordinates 
+     * because they relate to the origin of the object.</p>
+     * 
+     * <p>You then pass the Point instance that you created as the parameter 
+     * to the localToGlobal() method. The method returns a new Point object 
+     * with x and y values that relate to the origin of the Stage instead of 
+     * the origin of the object.</p>
+     * 
+     * @param point The name or identifier of a point created with the Point 
+     * class, specifying the x and y coordinates as properties.
+     * 
+     * @return A Point object with coordinates relative to the Stage.
+     * 
+     * @see flash.display.DisplayObject#localToGlobal
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function localToGlobal(point:Point):Point
+    {
+        // If there is not yet a displayObject or it's not parented, just
+        // return its local position
+        if (!displayObject || !displayObject.parent)
+            return new Point(x, y);
+            
+        var returnVal:Point = displayObject.localToGlobal(point);
+
+        if (!needsDisplayObject)
+        {
+            // If we're sharing the displayObject, add in our offset
+            returnVal.x += drawX;
+            returnVal.y += drawY;
+        }
+        
+        return returnVal;
+    }
+    
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function createDisplayObject():DisplayObject
+    {
+        setDisplayObject(new InvalidatingSprite());
+        return displayObject;
+    }
+    
+    /**
+     *  True if the element requires an exclusive DisplayObject.
+     *
+     *  Developers don't usually call this method directly, but override it in
+     *  their subclasses to indicate that an exclusive DisplayObject is needed.
+     *
+     *  Usually a subclass needs a DisplayObject when it has to set its properties.  
+     *
+     *  Some examples of such DisplayObject properties are <code>filters</code>,
+     *  <code>blendMode</code>, <code>mask</code>,
+     *  <code>rotation</code>, <code>alpha</code>.
+     * 
+     *  @return Returns true when the element requires an exclusive DisplayObject.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function get needsDisplayObject():Boolean
+    {
+        var result:Boolean = (alwaysCreateDisplayObject ||
+        (_filters && _filters.length > 0) || 
+            (_blendMode != BlendMode.NORMAL && _blendMode != "auto") || _mask ||
+            (layoutFeatures != null && (layoutFeatures.layoutScaleX != 1 || layoutFeatures.layoutScaleY != 1 || layoutFeatures.layoutScaleZ != 1 ||
+            layoutFeatures.layoutRotationX != 0 || layoutFeatures.layoutRotationY != 0 || layoutFeatures.layoutRotationZ != 0 ||
+            layoutFeatures.layoutZ  != 0 || layoutFeatures.mirror)) ||  
+            _colorTransform != null ||
+            _effectiveAlpha != 1);
+    
+        if (layoutFeatures != null && layoutFeatures.postLayoutTransformOffsets != null)
+        {
+            var o:TransformOffsets = layoutFeatures.postLayoutTransformOffsets;
+            result = result || (o.scaleX != 1 || o.scaleY != 1 || o.scaleZ != 1 ||
+            o.rotationX != 0 || o.rotationY != 0 || o.rotationZ != 0 || o.z  != 0);       
+        }
+        
+        return result;
+    }
+    
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function setSharedDisplayObject(sharedDisplayObject:DisplayObject):Boolean
+    {
+        if (!(sharedDisplayObject is Sprite) || _alwaysCreateDisplayObject || needsDisplayObject)
+            return false;
+        setDisplayObject(sharedDisplayObject);
+        return true;
+    }
+    
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function canShareWithPrevious(element:IGraphicElement):Boolean
+    {
+        // No need to check _alwaysCreateDisplayObject or needsDisplayObject,
+        // as those will be checked in setSharedDisplayObject
+        return element is GraphicElement;
+    }
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function canShareWithNext(element:IGraphicElement):Boolean
+    {
+        return element is GraphicElement && !_alwaysCreateDisplayObject && !needsDisplayObject;
+    }
+
+    /**
+     *  The actual DisplayObject that is drawn into by the GraphicElement. Typically this is 
+     *  equivalent to the <code>displayObject</code> property. Subclasses should perform drawing commands on
+     *  this property instead of on <code>displayObject</code>. 
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */ 
+    protected function get drawnDisplayObject():DisplayObject
+    {
+        // _drawnDisplayObject is non-null if we needed to create a mask
+        return _drawnDisplayObject ? _drawnDisplayObject : displayObject; 
+    }
+
+    /**
+     *  Returns a bitmap snapshot of the GraphicElement.
+     *  The bitmap contains all transformations and is reduced
+     *  to fit the visual bounds of the object.
+     *  
+     *  @param transparent Whether or not the bitmap image supports per-pixel transparency. 
+     *  The default value is true (transparent). To create a fully transparent bitmap, set the value of the 
+     *  transparent parameter to true and the value of the fillColor parameter to 0x00000000 (or to 0). 
+     *  Setting the transparent property to false can result in minor improvements in rendering performance. 
+     *  
+     *  @param fillColor A 32-bit ARGB color value that you use to fill the bitmap image area. 
+     *  The default value is 0xFFFFFFFF (solid white).
+     *  
+     *  @param useLocalSpace Whether or not the bitmap shows the GraphicElement in the local or global 
+     *  coordinate space. If true, then the snapshot is in the local space. The default value is true. 
+     * 
+     *  @param clipRect A Rectangle object that defines the area of the source object to draw. 
+     *  If you do not supply this value, no clipping occurs and the entire source object is drawn.
+     *  The clipRect should be defined in the coordinate space specified by useLocalSpace
+     * 
+     *  @return A bitmap snapshot of the GraphicElement or null if the input element has no visible bounds.
+     *  
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    mx_internal function captureBitmapData(transparent:Boolean = true, fillColor:uint = 0xFFFFFFFF, useLocalSpace:Boolean = true, clipRect:Rectangle = null):BitmapData
+    {
+        if (!layoutFeatures || !layoutFeatures.is3D)
+        {               
+            var restoreDisplayObject:Boolean = false;
+            var oldDisplayObject:DisplayObject;
+            
+            if (!displayObject || displayObjectSharingMode != DisplayObjectSharingMode.OWNS_UNSHARED_OBJECT)
+            {
+                restoreDisplayObject = true;
+                oldDisplayObject = displayObject;
+                setDisplayObject(new InvalidatingSprite());
+                if (parent is UIComponent)
+                    UIComponent(parent).$addChild(displayObject);
+                else
+                    parent.addChild(displayObject);
+                invalidateDisplayList();
+                validateDisplayList();
+            }
+            
+            var topLevel:Sprite = Sprite(IUIComponent(parent).systemManager.getSandboxRoot());
+            var rectBounds:Rectangle = useLocalSpace ? 
+                        new Rectangle(getLayoutBoundsX(), getLayoutBoundsY(), getLayoutBoundsWidth(), getLayoutBoundsHeight()) :
+                        displayObject.getBounds(topLevel); 
+            
+            if (rectBounds.width == 0 || rectBounds.height == 0)
+                return null;
+            
+            var bitmapData:BitmapData = new BitmapData(Math.ceil(rectBounds.width), Math.ceil(rectBounds.height), transparent, fillColor);
+                
+            // Can't use target's concatenatedMatrix, as it is sometimes wrong
+            var m:Matrix = useLocalSpace ? 
+                displayObject.transform.matrix : 
+                MatrixUtil.getConcatenatedMatrix(displayObject, null);
+            
+            if (m)
+                m.translate(-rectBounds.x, -rectBounds.y);
+            
+            bitmapData.draw(displayObject, m, null, null, clipRect);
+           
+            if (restoreDisplayObject)
+            {
+                if (parent is UIComponent)
+                    UIComponent(parent).$removeChild(displayObject);
+                else
+                    parent.removeChild(displayObject);
+                setDisplayObject(oldDisplayObject);
+            }
+            return bitmapData;
+        
+        }
+        else
+        {
+            return get3DSnapshot(transparent, fillColor, useLocalSpace);
+        }
+    }
+
+   /**
+     *  @private 
+     *  Returns a bitmap snapshot of a 3D transformed displayObject. Since BitmapData.draw ignores
+     *  the transform matrix of its target when it draws, we need to parent the target in a temporary
+     *  sprite and call BitmapData.draw on that temp sprite. We can't take a bitmap snapshot of the 
+     *  real parent because it might have other children. 
+     */
+    private function get3DSnapshot(transparent:Boolean = true, fillColor:uint = 0xFFFFFFFF, useLocalSpace:Boolean = true):BitmapData
+    {
+        var topLevel:Sprite = Sprite(IUIComponent(parent).systemManager); 
+        var dispObjParent:DisplayObjectContainer = displayObject.parent;
+        var drawSprite:Sprite = new Sprite();
+                
+        // Get the visual bounds of the target in both local and global coordinates
+        var topLevelRect:Rectangle = displayObject.getBounds(topLevel);
+        var displayObjectRect:Rectangle = displayObject.getBounds(dispObjParent);  
+        
+        // Keep a reference to the original 3D matrix. We will restore this later.
+        var oldMat3D:Matrix3D = displayObject.transform.matrix3D.clone();
+        
+        // Get the concatenated 3D matrix which we will use to position the target when we reparent it
+        var globalMat3D:Matrix3D = displayObject.transform.getRelativeMatrix3D(topLevel);
+        var newMat3D:Matrix3D = oldMat3D.clone();      
+        
+        
+        // Remove the target from its current parent, making sure to store the child index
+        var displayObjectIndex:int = parent.getChildIndex(displayObject);
+        if (parent is UIComponent)
+            UIComponent(parent).$removeChild(displayObject);
+        else
+            parent.removeChild(displayObject);
+        
+        // Parent the target to the drawSprite and then attach the drawSprite to the stage
+        topLevel.addChild(drawSprite);
+        drawSprite.addChild(displayObject);
+
+        // Assign the globally translated matrix to the target
+        if (useLocalSpace)
+        {
+            newMat3D.position = globalMat3D.position;
+            displayObject.transform.matrix3D = newMat3D;
+        }
+        else
+        {
+            displayObject.transform.matrix3D = globalMat3D;
+        }
+        // Translate the bitmap so that the left-top bounds ends up at (0,0)
+        var m:Matrix = new Matrix();
+        m.translate(-topLevelRect.left, - topLevelRect.top);
+               
+        // Draw to the bitmapData
+        var snapshot:BitmapData = new BitmapData( topLevelRect.width, topLevelRect.height, transparent, fillColor);
+        snapshot.draw(drawSprite, m, null, null, null, true);
+
+        // Remove target from temporary sprite and remove temp sprite from stage
+        drawSprite.removeChild(displayObject);
+        topLevel.removeChild(drawSprite);
+        
+        // Reattach the target to its original parent at its original child position
+        if (parent is UIComponent)
+            UIComponent(parent).$addChildAt(displayObject, displayObjectIndex);
+        else
+            parent.addChildAt(displayObject, displayObjectIndex);
+            
+        // Restore the original 3D matrix
+        displayObject.transform.matrix3D = oldMat3D;
+
+        return snapshot; 
+    }
+
+    /**
+     *  @private
+     */
+    protected function layer_PropertyChange(event:PropertyChangeEvent):void
+    {
+        switch (event.property)
+        {
+            case "effectiveVisibility":
+            {
+                var newValue:Boolean = (event.newValue && _visible);
+                
+                if (newValue != _effectiveVisibility)
+                {
+                    _effectiveVisibility = newValue;
+                    visibleChanged = true;
+                    invalidateProperties();
+                }
+                break;
+            }
+            case "effectiveAlpha":
+            {
+                var newAlpha:Number = Number(event.newValue) * _alpha;
+                if (newAlpha != _effectiveAlpha)
+                {
+                    _effectiveAlpha = newAlpha;
+                    alphaChanged = true;
+                    
+                    var mxTransform:mx.geom.Transform = _transform as mx.geom.Transform;
+                    if (mxTransform)
+                        mxTransform.applyColorTransformAlpha = false;        
+                    
+                    invalidateDisplayObjectSharing(); 
+                    invalidateProperties();
+                }
+                break;
+            }
+        }
+    }
+    
+    /**
+     *  Dispatches a propertyChange event.
+     *  
+     *  @param prop The property that changed.
+     *  
+     *  @param oldValue The previous value of the property.
+     *  
+     *  @param value The new value of the property.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    mx_internal function dispatchPropertyChangeEvent(prop:String, oldValue:*,
+                                                   value:*):void
+    {
+        if (hasEventListener("propertyChange"))
+            dispatchEvent(PropertyChangeEvent.createUpdateEvent(
+                this, prop, oldValue, value));
+
+    }
+
+    /**
+     *  Utility method that notifies the host that this element has changed and needs
+     *  its layer to be updated.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function invalidateDisplayObjectSharing():void
+    {
+        if (parent)
+            IGraphicElementContainer(parent).invalidateGraphicElementSharing(this);
+    }
+
+    /**
+     *  Calling this method results in a call to the elements's
+     *  <code>validateProperties()</code> method
+     *  before the display list is rendered.
+     *
+     *  <p>Subclasses should do their work in 
+     *  <code>commitProperties()</code>.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function invalidateProperties():void
+    {
+        if (invalidatePropertiesFlag)
+            return;
+        invalidatePropertiesFlag = true;
+
+        if (parent)
+            IGraphicElementContainer(parent).invalidateGraphicElementProperties(this);
+    }
+
+    /**
+     *  Calling this method results in a call to the elements's
+     *  <code>validateSize()</code> method
+     *  before the display list is rendered.
+     *
+     *  <p>Subclasses should override and do their measurement in
+     *  <code>measure()</code>.
+     *  By default when <code>explicitWidth</code> and <code>explicitHeight</code>
+     *  are set, <code>measure()</code> will not be called. To override this
+     *  default behavior subclasses should override <code>skipMeasure()</code>.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function invalidateSize():void
+    {
+        if (invalidateSizeFlag)
+            return;
+        invalidateSizeFlag = true;
+
+        if (parent)
+            IGraphicElementContainer(parent).invalidateGraphicElementSize(this);
+    }
+
+    /**
+     *  Helper method to invalidate parent size and display list if
+     *  this object affects its layout (includeInLayout is true).
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function invalidateParentSizeAndDisplayList():void
+    {
+        if (!includeInLayout)
+            return;
+
+        // We want to invalidate both the parent size and parent display list.
+        if (parent && parent is IInvalidating)
+        {
+            IInvalidating(parent).invalidateSize();
+            IInvalidating(parent).invalidateDisplayList();
+        }
+    }
+
+    /**
+     *  Calling this method results in a call to the elements's
+     *  <code>validateDisplayList()</code> method
+     *  before the display list is rendered.
+     *
+     *  <p>Subclasses should override and do their work in
+     *  <code>updateDisplayList()</code>.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function invalidateDisplayList():void
+    {
+        if (invalidateDisplayListFlag)
+            return;
+        invalidateDisplayListFlag = true;
+
+        // The IGraphicElementContainer will take care of redrawing all graphic elements that
+        // share the display object with this element.
+        if (parent)
+            IGraphicElementContainer(parent).invalidateGraphicElementDisplayList(this);
+    }
+
+    /**
+     *  Validates and updates the properties and layout of this object
+     *  by immediately calling <code>validateProperties()</code>,
+     *  <code>validateSize()</code>, and <code>validateDisplayList()</code>,
+     *  if necessary.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function validateNow():void
+    {
+        if (parent)
+        {
+            UIComponentGlobals.layoutManager.validateClient(
+                ILayoutManagerClient(parent));
+        }
+    }
+
+    /**
+     *  Used by layout logic to validate the properties of a component
+     *  by calling the <code>commitProperties()</code> method.
+     *  In general, subclasses should
+     *  override the <code>commitProperties()</code> method and not this method.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function validateProperties():void
+    {
+        if (!invalidatePropertiesFlag)
+            return;
+        commitProperties();
+        invalidatePropertiesFlag = false;
+        
+        // If we aren't doing any more invalidation, send out an UpdateComplete event
+        if (!invalidatePropertiesFlag && !invalidateSizeFlag && !invalidateDisplayListFlag)
+            dispatchUpdateComplete();        
+    }
+
+    /**
+     *  Processes the properties set on the element.
+     *  This is an advanced method that you might override
+     *  when creating a subclass.
+     *
+     *  <p>You do not call this method directly.
+     *  Flex calls the <code>commitProperties()</code> method when you
+     *  use the <code>addElement()</code> method to add an element to an 
+     *  <code>IGraphicElementContainer</code> container such as Group,
+     *  or when you call the <code>invalidateProperties()</code> method of the element.
+     *  Calls to the <code>commitProperties()</code> method occur before calls to the
+     *  <code>measure()</code> method. This lets you set property values that might
+     *  be used by the <code>measure()</code> method.</p>
+     *
+     *  <p>Some elements have properties that
+     *  interact with each other.
+     *  It is often best at startup time to process all of these
+     *  properties at one time to avoid duplicating work.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function commitProperties():void
+    {
+        //trace("GraphicElement.commitProperties displayObject",displayObject,"this",this);
+        var updateTransform:Boolean = false;
+        var mxTransform:mx.geom.Transform;
+        
+        // If we are the first in the sequence, setup the displayObject properties
+        if (displayObjectSharingMode != DisplayObjectSharingMode.USES_SHARED_OBJECT && displayObject)
+        {
+            if (colorTransformChanged || displayObjectChanged)
+            {
+                colorTransformChanged = false;
+                if (_colorTransform)
+                    displayObject.transform.colorTransform = _colorTransform;
+            }
... 1096 lines suppressed ...