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/25 16:32:58 UTC

[royale-asjs] 01/11: Some spark subs

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

commit ce49439ce40f700f0b546d15625be9f09bfbf941
Author: Yishay Weiss <yi...@yell.com>
AuthorDate: Thu Sep 24 22:45:18 2020 +0100

    Some spark subs
---
 .../royale/spark/components/ButtonBarButton.as     |  284 +++
 .../main/royale/spark/components/IItemRenderer.as  |  120 +
 .../spark/components/SkinnableDataContainer.as     |  810 +++++++
 .../main/royale/spark/components/VideoDisplay.as   | 2449 ++++++++++++++++++++
 .../spark/components/mediaClasses/ScrubBar.as      |  273 +++
 .../spark/components/mediaClasses/VolumeBar.as     |  573 +++++
 .../supportClasses/ButtonBarHorizontalLayout.as    |  285 +++
 .../src/main/royale/spark/effects/AnimateColor.as  |  163 ++
 .../src/main/royale/spark/effects/Fade.as          |  257 ++
 .../src/main/royale/spark/effects/Resize.as        |  294 +++
 .../src/main/royale/spark/effects/SetAction.as     |  179 ++
 .../src/main/royale/spark/effects/easing/IEaser.as |   71 +
 .../src/main/royale/spark/effects/easing/Power.as  |  154 ++
 .../royale/spark/events/TitleWindowBoundsEvent.as  |  283 +++
 .../main/royale/spark/filters/ColorMatrixFilter.as |  144 ++
 .../main/royale/spark/filters/DropShadowFilter.as  |  361 +++
 .../src/main/royale/spark/filters/GlowFilter.as    |  266 +++
 .../src/main/royale/spark/primitives/Ellipse.as    |  355 +++
 .../src/main/royale/spark/skins/SparkButtonSkin.as |  529 +++++
 .../fullScreen/PlayPauseButtonSkin.mxml            |  142 ++
 .../mediaClasses/fullScreen/ScrubBarSkin.mxml      |  135 ++
 .../mediaClasses/fullScreen/VolumeBarSkin.mxml     |   88 +
 .../mediaClasses/normal/PlayPauseButtonSkin.mxml   |  214 ++
 .../spark/mediaClasses/normal/ScrubBarSkin.mxml    |  178 ++
 .../spark/mediaClasses/normal/VolumeBarSkin.mxml   |  112 +
 .../src/main/royale/spark/utils/LabelUtil.as       |  130 ++
 26 files changed, 8849 insertions(+)

diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/components/ButtonBarButton.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/ButtonBarButton.as
new file mode 100644
index 0000000..eac31eb
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/ButtonBarButton.as
@@ -0,0 +1,284 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.components
+{
+import flash.events.MouseEvent;
+import flash.events.Event;
+
+import spark.components.IItemRenderer;
+import mx.core.mx_internal;
+
+use namespace mx_internal;
+
+/**
+ *  Dispatched when the <code>data</code> property changes.
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ * 
+ *  @eventType mx.events.FlexEvent.DATA_CHANGE
+ * 
+ */
+[Event(name="dataChange", type="mx.events.FlexEvent")]
+
+/**
+ *  The ButtonBarButton class defines the custom item renderer
+ *  used by the ButtonBar control. 
+ *  This item renderer is used in the ButtonBarSkin class, 
+ *  the default skin for the ButtonBar.
+ *
+ *  @see spark.components.ButtonBar
+ *  @see spark.skins.spark.ButtonBarSkin
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class ButtonBarButton extends ToggleButton implements IItemRenderer
+{
+    /**
+     *  Constructor. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 2.5
+     *  @productversion Flex 4.5
+     */    
+    public function ButtonBarButton()
+    {
+        super();
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+
+    //----------------------------------
+    //  allowDeselection
+    //----------------------------------
+
+    /**
+     *  @private
+     *  Storage for the allowDeselection property 
+     */
+    private var _allowDeselection:Boolean = true;
+
+    /**
+     *  If <code>true</code>, the user click on a currently selected button to deselect it.
+     *  If <code>false</code>, the user must select a different button 
+     *  to deselect the currently selected button.
+     *
+     *  @default true
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */    
+    public function get allowDeselection():Boolean
+    {
+        return _allowDeselection;
+    }
+    
+    /**
+     *  @private
+     */    
+    public function set allowDeselection(value:Boolean):void
+    {
+        _allowDeselection = value;
+    }
+
+    //----------------------------------
+    //  showsCaret
+    //----------------------------------
+
+    /**
+     *  @private
+     *  Storage for the showsCaret property 
+     */
+    private var _showsCaret:Boolean = false;
+
+    /**
+     *  @inheritDoc 
+     *
+     *  @default false
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */    
+    public function get showsCaret():Boolean
+    {
+        return _showsCaret;
+    }
+    
+    /**
+     *  @private
+     */    
+    public function set showsCaret(value:Boolean):void
+    {
+        if (value == _showsCaret)
+            return;
+
+        _showsCaret = value;
+        drawFocusAnyway = true;
+        drawFocus(value);
+    }
+
+    //----------------------------------
+    //  dragging
+    //----------------------------------
+
+    /**
+     *  @private  
+     */
+    public function get dragging():Boolean
+    {
+        return false;
+    }
+
+    /**
+     *  @private  
+     */
+    public function set dragging(value:Boolean):void
+    {
+    }
+
+    //----------------------------------
+    //  data
+    //----------------------------------
+
+    [Bindable("dataChange")]
+    /**
+     *  @inheritDoc 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get data():Object
+    {
+         return content;
+    }
+
+    /**
+     *  @private 
+     */
+    public function set data(value:Object):void
+    {
+         content = value;
+         dispatchEvent(new Event("dataChange"));
+    }
+    
+    //----------------------------------
+    //  itemIndex
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  storage for the itemIndex property 
+     */    
+    private var _itemIndex:int;
+    
+    /**
+     *  @inheritDoc 
+     *
+     *  @default false
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 2.5
+     *  @productversion Flex 4.5
+     */    
+    public function get itemIndex():int
+    {
+        return _itemIndex;
+    }
+    
+    /**
+     *  @private
+     */    
+    public function set itemIndex(value:int):void
+    {
+        _itemIndex = value;
+    }
+    
+    //----------------------------------
+    //  label
+    //----------------------------------
+    
+    /**
+     *  @private 
+     */
+    private var _label:String = "";
+    
+    /**
+     *  @inheritDoc  
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override public function get label():String
+    {
+        return _label;
+    }
+    
+    /**
+     *  @private 
+     */
+    override public function set label(value:String):void
+    {
+        if (value != _label)
+        {
+            _label = value;
+
+            if (labelDisplay)
+                labelDisplay.text = _label;
+        }
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden functions: ButtonBase
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     */ 
+    override protected function buttonReleased():void
+    {
+        if (selected && !allowDeselection)
+            return;
+        
+        super.buttonReleased();
+    }
+}
+
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/components/IItemRenderer.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/IItemRenderer.as
new file mode 100644
index 0000000..cbcb14b
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/IItemRenderer.as
@@ -0,0 +1,120 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.components
+{
+
+import spark.components.IItemRendererOwner; 
+import mx.core.IDataRenderer;
+import mx.core.IVisualElement; 
+
+/**
+ *  The IItemRenderer interface defines the basic set of APIs
+ *  that a class must implement to create an item renderer that can 
+ *  communicate with a host component.
+ *  The host component, such as the List or ButtonBar controls, 
+ *  must implement the IItemRendererOwner interface. 
+ *   
+ *  
+ *  @see spark.components.IItemRendererOwner
+ *
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ *  
+ */
+public interface IItemRenderer extends IDataRenderer, IVisualElement
+{
+    
+    /**
+     *  The index of the item in the data provider
+     *  of the host component of the item renderer.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     *  
+     */
+    function get itemIndex():int;
+    function set itemIndex(value:int):void;
+    
+    /**
+     *  Contains <code>true</code> if the item renderer is being dragged.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     *  
+     */
+    function get dragging():Boolean;
+    function set dragging(value:Boolean):void;
+
+    /**
+     *  The String to display in the item renderer. 
+     *
+     *  <p>The host component of the item renderer can use the 
+     *  <code>itemToLabel()</code> method to convert the data item 
+     *  to a String for display by the item renderer. </p>
+     * 
+     *  <p>For controls like List and ButtonBar, you can use the 
+     *  <code>labelField</code> or <code>labelFunction</code> properties 
+     *  to specify the field of the data item that contains the String.
+     *  Otherwise the host component uses the <code>toString()</code> method 
+     *  to convert the data item to a String. </p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     *  
+     */
+    function get label():String;
+    function set label(value:String):void;
+    
+    /**
+     *  Contains <code>true</code> if the item renderer 
+     *  can show itself as selected.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     *  
+     */
+    function get selected():Boolean;
+    function set selected(value:Boolean):void;
+
+    /**
+     *  Contains <code>true</code> if the item renderer 
+     *  can show itself as focused. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     *  
+     */
+    function get showsCaret():Boolean;
+    function set showsCaret(value:Boolean):void;
+}
+
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/components/SkinnableDataContainer.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/SkinnableDataContainer.as
new file mode 100644
index 0000000..e6c2210
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/SkinnableDataContainer.as
@@ -0,0 +1,810 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.components
+{
+
+import flash.events.Event;
+
+import mx.collections.IList;
+import mx.core.IDataRenderer;
+import mx.core.IFactory;
+import mx.core.IVisualElement;
+import mx.core.mx_internal;
+import mx.events.PropertyChangeEvent;
+import mx.utils.BitFlagUtil;
+
+import spark.components.supportClasses.SkinnableContainerBase;
+import spark.core.IViewport;
+import spark.events.RendererExistenceEvent;
+import spark.layouts.supportClasses.LayoutBase;
+
+use namespace mx_internal;
+
+/**
+ *  Dispatched when a renderer is added to the container.
+ *  The <code>event.renderer</code> property contains 
+ *  the renderer that was added.
+ *
+ *  @eventType spark.events.RendererExistenceEvent.RENDERER_ADD
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+[Event(name="rendererAdd", type="spark.events.RendererExistenceEvent")]
+
+/**
+ *  Dispatched when a renderer is removed from the container.
+ *  The <code>event.renderer</code> property contains 
+ *  the renderer that was removed.
+ *
+ *  @eventType spark.events.RendererExistenceEvent.RENDERER_REMOVE
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+[Event(name="rendererRemove", type="spark.events.RendererExistenceEvent")]
+
+include "../styles/metadata/BasicInheritingTextStyles.as"
+
+/**
+ *  The alpha of the focus ring for this component.
+ *
+ *  @default 0.55
+ * 
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+[Style(name="focusAlpha", type="Number", inherit="no", theme="spark, mobile", minValue="0.0", maxValue="1.0")]
+
+/**
+ *  Color of focus ring when the component is in focus.
+ *
+ *  @default 0x70B2EE
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */ 
+[Style(name="focusColor", type="uint", format="Color", inherit="yes", theme="spark, mobile")]
+
+/**
+ *  Thickness, in pixels, of the focus rectangle outline.
+ *
+ *  @default 2
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+[Style(name="focusThickness", type="Number", format="Length", inherit="no", minValue="0.0")]
+
+[DefaultProperty("dataProvider")]
+
+[IconFile("SkinnableDataContainer.png")]
+
+/**
+ *  The SkinnableDataContainer class is the base container class for
+ *  data items.  The SkinnableDataContainer class converts data 
+ *  items to visual elements for display.
+ *  While this container can hold visual elements, it is often used only 
+ *  to hold data items as children.
+ *
+ *  <p>The SkinnableDataContainer class takes as children data items 
+ *  or visual elements that implement the IVisualElement interface
+ *  and are Display Objects.
+ *  Data items can be simple data items such String and Number objects, 
+ *  and more complicated data items such as Object and XMLNode objects. 
+ *  While these containers can hold visual elements, 
+ *  they are often used only to hold data items as children.</p>
+ *
+ *  <p>An item renderer defines the visual representation of the 
+ *  data item in the container. 
+ *  The item renderer converts the data item into a format that can 
+ *  be displayed by the container. 
+ *  You must pass an item renderer to a SkinnableDataContainer to 
+ *  render data items appropriately.</p>
+ *
+ *  <p>If you want a container of data items and don't need a skin, then 
+ *  it is recommended to use a DataGroup (which cannot be skinned) to 
+ *  improve performance and application size.</p>
+ *
+ *  <p>The SkinnableDataContainer container has the following default characteristics:</p>
+ *  <table class="innertable">
+ *     <tr><th>Characteristic</th><th>Description</th></tr>
+ *     <tr><td>Default size</td><td>Large enough to display its children</td></tr>
+ *     <tr><td>Minimum size</td><td>0 pixels</td></tr>
+ *     <tr><td>Maximum size</td><td>10000 pixels wide and 10000 pixels high</td></tr>
+ *  </table>
+ * 
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:SkinnableDataContainer&gt;</code> tag inherits all of the tag 
+ *  attributes of its superclass and adds the following tag attributes:</p>
+ *
+ *  <pre>
+ *  &lt;s:SkinnableDataContainer
+ *    <strong>Properties</strong>
+ *    autoLayout="true"
+ *    dataProvider="null"
+ *    itemRenderer="null"
+ *    itemRendererFunction="null"
+ *    layout="VerticalLayout"
+ *    typicalItem="null"
+ *  
+ *    <strong>Styles</strong>
+ *    alignmentBaseline="useDominantBaseline"
+ *    baselineShift="0.0"
+ *    cffHinting="horizontal_stem"
+ *    color="0"
+ *    digitCase="default"
+ *    digitWidth="default"
+ *    direction="LTR"
+ *    dominantBaseline="auto"
+ *    focusAlpha="0.55"
+ *    focusColor=""
+ *    focusThickness="2"
+ *    fontFamily="Arial"
+ *    fontLookup="device"
+ *    fontSize="12"
+ *    fontStyle="normal"
+ *    fontWeight="normal"
+ *    justificationRule="auto"
+ *    justificationStyle="auto"
+ *    kerning="auto"
+ *    ligatureLevel="common"
+ *    lineHeight="120%"
+ *    lineThrough="false"
+ *    locale="en"
+ *    renderingMode="CFF"
+ *    textAlign="start"
+ *    textAlignLast="start"
+ *    textAlpha="1"
+ *    textJustify="inter_word"
+ *    trackingLeft="0"
+ *    trackingRight="0"
+ *    typographicCase="default"
+ *  
+ *    <strong>Events</strong>
+ *    rendererAdd="<i>No default</i>"
+ *    rendererRemove="<i>No default</i>"
+ *  /&gt;
+ *  </pre>
+ *
+ *  @see SkinnableContainer
+ *  @see DataGroup
+ *  @see spark.skins.spark.SkinnableDataContainerSkin
+ *
+ *  @includeExample examples/SkinnableDataContainerExample.mxml
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class SkinnableDataContainer extends SkinnableContainerBase implements IItemRendererOwner
+{
+    include "../core/Version.as";
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Class constants
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     */
+    private static const AUTO_LAYOUT_PROPERTY_FLAG:uint = 1 << 0;
+    
+    /**
+     *  @private
+     */
+    private static const DATA_PROVIDER_PROPERTY_FLAG:uint = 1 << 1;
+    
+    /**
+     *  @private
+     */
+    private static const ITEM_RENDERER_PROPERTY_FLAG:uint = 1 << 2;
+    
+    /**
+     *  @private
+     */
+    private static const ITEM_RENDERER_FUNCTION_PROPERTY_FLAG:uint = 1 << 3;
+    
+    /**
+     *  @private
+     */
+    private static const LAYOUT_PROPERTY_FLAG:uint = 1 << 4;
+    
+    /**
+     *  @private
+     */
+    private static const TYPICAL_ITEM_PROPERTY_FLAG:uint = 1 << 5;
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Constructor. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function SkinnableDataContainer()
+    {
+        super();
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Skin Parts
+    //
+    //--------------------------------------------------------------------------
+    
+    [Bindable]
+    [SkinPart(required="false")]
+    
+    /**
+     *  An optional skin part that defines the DataGroup in the skin class 
+     *  where data items get pushed into, rendered, and laid out.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var dataGroup:DataGroup;
+    
+    /**
+     *  @private
+     *  Several properties are proxied to dataGroup.  However, when dataGroup
+     *  is not around, we need to store values set on SkinnableDataContainer.  This object 
+     *  stores those values.  If dataGroup is around, the values are stored 
+     *  on the dataGroup directly.  However, we need to know what values 
+     *  have been set by the developer on the SkinnableDataContainer (versus set on 
+     *  the dataGroup or defaults of the dataGroup) as those are values 
+     *  we want to carry around if the dataGroup changes (via a new skin). 
+     *  In order to store this info effeciently, dataGroupProperties becomes 
+     *  a uint to store a series of BitFlags.  These bits represent whether a 
+     *  property has been explicitly set on this SkinnableDataContainer.  When the 
+     *  dataGroup is not around, dataGroupProperties is a typeless 
+     *  object to store these proxied properties.  When dataGroup is around,
+     *  dataGroupProperties stores booleans as to whether these properties 
+     *  have been explicitly set or not.
+     */
+    private var dataGroupProperties:Object = {};
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties 
+    //
+    //--------------------------------------------------------------------------
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties proxied to dataGroup
+    //
+    //--------------------------------------------------------------------------
+    
+    //----------------------------------
+    //  autoLayout
+    //----------------------------------
+
+    [Inspectable(defaultValue="true")]
+
+    /**
+     *  @copy spark.components.supportClasses.GroupBase#autoLayout
+     *
+     *  @default true
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get autoLayout():Boolean
+    {
+        if (dataGroup)
+            return dataGroup.autoLayout;
+        else
+        {
+            // want the default to be true
+            var v:* = dataGroupProperties.autoLayout;
+            return (v === undefined) ? true : v;
+        }
+    }
+
+    /**
+     *  @private
+     */
+    public function set autoLayout(value:Boolean):void
+    {
+        if (dataGroup)
+        {
+            dataGroup.autoLayout = value;
+            dataGroupProperties = BitFlagUtil.update(dataGroupProperties as uint, 
+                                                     AUTO_LAYOUT_PROPERTY_FLAG, true);
+        }
+        else
+            dataGroupProperties.autoLayout = value;
+    }
+    
+    //----------------------------------
+    //  dataProvider
+    //----------------------------------    
+    
+    /**
+     *  @copy spark.components.DataGroup#dataProvider
+     *
+     *  @see #itemRenderer
+     *  @see #itemRendererFunction
+     *  @see mx.collections.IList
+     *  @see mx.collections.ArrayCollection
+     *  @see mx.collections.ArrayList
+     *  @see mx.collections.XMLListCollection
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    [Bindable("dataProviderChanged")]
+    [Inspectable(category="Data")]
+    
+    public function get dataProvider():IList
+    {       
+        return (dataGroup) 
+            ? dataGroup.dataProvider 
+            : dataGroupProperties.dataProvider;
+    }
+    
+    public function set dataProvider(value:IList):void
+    {
+        if (dataGroup)
+        {
+            dataGroup.dataProvider = value;
+            dataGroupProperties = BitFlagUtil.update(dataGroupProperties as uint, 
+                                                     DATA_PROVIDER_PROPERTY_FLAG, true);
+        }
+        else
+            dataGroupProperties.dataProvider = value;
+        dispatchEvent(new Event("dataProviderChanged"));
+    }
+    
+    //----------------------------------
+    //  itemRenderer
+    //----------------------------------
+    
+    [Inspectable(category="Data")]
+    
+    /**
+     *  @copy spark.components.DataGroup#itemRenderer
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get itemRenderer():IFactory
+    {
+        return (dataGroup) 
+            ? dataGroup.itemRenderer 
+            : dataGroupProperties.itemRenderer;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set itemRenderer(value:IFactory):void
+    {
+        if (dataGroup)
+        {
+            dataGroup.itemRenderer = value;
+            dataGroupProperties = BitFlagUtil.update(dataGroupProperties as uint, 
+                                                     ITEM_RENDERER_PROPERTY_FLAG, true);
+        }
+        else
+            dataGroupProperties.itemRenderer = value;
+    }
+    
+    //----------------------------------
+    //  itemRendererFunction
+    //----------------------------------
+    
+    [Inspectable(category="Data")]
+    
+    /**
+     *  @copy spark.components.DataGroup#itemRendererFunction
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get itemRendererFunction():Function
+    {
+        return (dataGroup) 
+            ? dataGroup.itemRendererFunction 
+            : dataGroupProperties.itemRendererFunction;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set itemRendererFunction(value:Function):void
+    {
+        if (dataGroup)
+        {
+            dataGroup.itemRendererFunction = value;
+            dataGroupProperties = BitFlagUtil.update(dataGroupProperties as uint, 
+                                                     ITEM_RENDERER_FUNCTION_PROPERTY_FLAG, true);
+        }
+        else
+            dataGroupProperties.itemRendererFunction = value;
+    }
+    
+    //----------------------------------
+    //  layout
+    //----------------------------------
+    
+    [Inspectable(category="General")]
+    
+    /**
+     *  @copy spark.components.supportClasses.GroupBase#layout
+     *
+     *  @default VerticalLayout
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */     
+    public function get layout():LayoutBase
+    {
+        return (dataGroup) 
+            ? dataGroup.layout 
+            : dataGroupProperties.layout;
+    }
+
+    /**
+     *  @private
+     */
+    public function set layout(value:LayoutBase):void
+    {
+        if (dataGroup)
+        {
+            dataGroup.layout = value;
+            dataGroupProperties = BitFlagUtil.update(dataGroupProperties as uint, 
+                                                     LAYOUT_PROPERTY_FLAG, true);
+        }
+        else
+            dataGroupProperties.layout = value;
+    }
+    
+    //----------------------------------
+    //  typicalItem
+    //----------------------------------
+
+    [Inspectable(category="Data")]
+    
+    /**
+     *  @copy spark.components.DataGroup#typicalItem
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get typicalItem():Object
+    {
+        return (dataGroup) 
+            ? dataGroup.typicalItem 
+            : dataGroupProperties.typicalItem;
+    }
+
+    /**
+     *  @private
+     */
+    public function set typicalItem(value:Object):void
+    {
+        if (dataGroup)
+        {
+            dataGroup.typicalItem = value;
+            dataGroupProperties = BitFlagUtil.update(dataGroupProperties as uint, 
+                                                     TYPICAL_ITEM_PROPERTY_FLAG, true);
+        }
+        else
+            dataGroupProperties.typicalItem = value;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Methods
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  From the specified data item, return the String representation 
+     *  of the data item for an item renderer to display.
+     *  This method uses the <code>toString()</code> method of 
+     *  the data item to convert it to a String representation.
+     *  A Null data item returns an empty string.
+     *
+     *  @param item The data item.
+     *
+     *  @return The String representation of the data item.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function itemToLabel(item:Object):String
+    {
+        if (item !== null)
+            return item.toString();
+        else return " ";
+    }
+
+    /**
+     *  Updates an item renderer for use or reuse. 
+     *  When an item renderer is first created,
+     *  or when it is recycled because of virtualization, this 
+     *  SkinnableDataContainer instance can set the 
+     *  item renderer's <code>label</code> property and 
+     *  <code>owner</code> property. 
+     *  
+     *  @param renderer The renderer being updated. 
+     *  
+     *  @param itemIndex The index of the data item in the data provider. 
+     *  
+     *  @param data The data object this item renderer is representing. 
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     * 
+     */
+    public function updateRenderer(renderer:IVisualElement, itemIndex:int, data:Object):void
+    {
+        // set the owner
+        renderer.owner = this;
+        
+        // Set the index
+        if (renderer is IItemRenderer)
+            IItemRenderer(renderer).itemIndex = itemIndex;
+
+        // set the label to the toString() of the data 
+        if (renderer is IItemRenderer)
+            IItemRenderer(renderer).label = itemToLabel(data);
+        
+        // always set the data last
+        if ((renderer is IDataRenderer) && (renderer !== data))
+            IDataRenderer(renderer).data = data;
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden methods
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @private
+     */
+    override protected function partAdded(partName:String, instance:Object):void
+    {
+        super.partAdded(partName, instance);
+
+        if (instance == dataGroup)
+        {
+            // copy proxied values from dataGroupProperties (if set) to dataGroup
+            
+            var newDataGroupProperties:uint = 0;
+            
+            if (dataGroupProperties.layout !== undefined)
+            {
+                dataGroup.layout = dataGroupProperties.layout;
+                newDataGroupProperties = BitFlagUtil.update(newDataGroupProperties as uint, 
+                                                            LAYOUT_PROPERTY_FLAG, true);;
+            }
+            
+            if (dataGroupProperties.autoLayout !== undefined)
+            {
+                dataGroup.autoLayout = dataGroupProperties.autoLayout;
+                newDataGroupProperties = BitFlagUtil.update(newDataGroupProperties as uint, 
+                                                            AUTO_LAYOUT_PROPERTY_FLAG, true);
+            }
+            
+            if (dataGroupProperties.dataProvider !== undefined)
+            {
+                dataGroup.dataProvider = dataGroupProperties.dataProvider;
+                newDataGroupProperties = BitFlagUtil.update(newDataGroupProperties as uint, 
+                                                            DATA_PROVIDER_PROPERTY_FLAG, true);
+            }
+            
+            if (dataGroupProperties.itemRenderer !== undefined)
+            {
+                dataGroup.itemRenderer = dataGroupProperties.itemRenderer;
+                newDataGroupProperties = BitFlagUtil.update(newDataGroupProperties as uint, 
+                                                            ITEM_RENDERER_PROPERTY_FLAG, true);
+            }
+            
+            if (dataGroupProperties.itemRendererFunction !== undefined)
+            {
+                dataGroup.itemRendererFunction = dataGroupProperties.itemRendererFunction;
+                newDataGroupProperties = BitFlagUtil.update(newDataGroupProperties as uint, 
+                                                            ITEM_RENDERER_FUNCTION_PROPERTY_FLAG, true);
+            }
+                       
+            if (dataGroupProperties.typicalItem !== undefined)
+            {
+                dataGroup.typicalItem = dataGroupProperties.typicalItem;
+                newDataGroupProperties = BitFlagUtil.update(newDataGroupProperties as uint, 
+                                                            TYPICAL_ITEM_PROPERTY_FLAG, true);
+            }
+            
+            dataGroupProperties = newDataGroupProperties;
+            
+            // Register our instance as the dataGroup's item renderer update delegate.
+            dataGroup.rendererUpdateDelegate = this;
+            
+            // The only reason we have these listeners is to re-dispatch events.  
+            // We only add as necessary.
+            
+            if (hasEventListener(RendererExistenceEvent.RENDERER_ADD))
+            {
+                dataGroup.addEventListener(
+                    RendererExistenceEvent.RENDERER_ADD, dispatchEvent);
+            }
+            
+            if (hasEventListener(RendererExistenceEvent.RENDERER_REMOVE))
+            {
+                dataGroup.addEventListener(
+                    RendererExistenceEvent.RENDERER_REMOVE, dispatchEvent);
+            }
+        }
+    }
+    
+    /**
+     * @private
+     */
+    override protected function partRemoved(partName:String, instance:Object):void
+    {
+        super.partRemoved(partName, instance);
+
+        if (instance == dataGroup)
+        {
+            dataGroup.removeEventListener(
+                RendererExistenceEvent.RENDERER_ADD, dispatchEvent);
+            dataGroup.removeEventListener(
+                RendererExistenceEvent.RENDERER_REMOVE, dispatchEvent);
+            
+            // copy proxied values from dataGroup (if explicitly set) to dataGroupProperties
+            
+            var newDataGroupProperties:Object = {};
+            
+            if (BitFlagUtil.isSet(dataGroupProperties as uint, LAYOUT_PROPERTY_FLAG))
+                newDataGroupProperties.layout = dataGroup.layout;
+            
+            if (BitFlagUtil.isSet(dataGroupProperties as uint, AUTO_LAYOUT_PROPERTY_FLAG))
+                newDataGroupProperties.autoLayout = dataGroup.autoLayout;
+            
+            if (BitFlagUtil.isSet(dataGroupProperties as uint, DATA_PROVIDER_PROPERTY_FLAG))
+                newDataGroupProperties.dataProvider = dataGroup.dataProvider;
+            
+            if (BitFlagUtil.isSet(dataGroupProperties as uint, ITEM_RENDERER_PROPERTY_FLAG))
+                newDataGroupProperties.itemRenderer = dataGroup.itemRenderer;
+            
+            if (BitFlagUtil.isSet(dataGroupProperties as uint, ITEM_RENDERER_FUNCTION_PROPERTY_FLAG))
+                newDataGroupProperties.itemRendererFunction = dataGroup.itemRendererFunction;
+            
+            if (BitFlagUtil.isSet(dataGroupProperties as uint, TYPICAL_ITEM_PROPERTY_FLAG))
+                newDataGroupProperties.typicalItem = dataGroup.typicalItem;
+            
+            dataGroupProperties = newDataGroupProperties;
+            
+            dataGroup.dataProvider = null;
+            dataGroup.layout = null;
+            dataGroup.rendererUpdateDelegate = null;
+        }
+    }
+    
+    /**
+     *  @private
+     * 
+     *  This method is overridden so we can figure out when someone starts listening
+     *  for property change events.  If no one's listening for them, then we don't 
+     *  listen for them on our dataGroup.
+     */
+    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);
+        
+        // TODO (rfrishbe): this isn't ideal as we should deal with the useCapture, 
+        // priority, and useWeakReference parameters.
+        
+        // if it's a different type of event or the dataGroup doesn't
+        // exist, don't worry about it.  When the dataGroup, 
+        // gets created up, we'll check to see whether we need to add this 
+        // event listener to the dataGroup.
+        
+        if (type == RendererExistenceEvent.RENDERER_ADD && dataGroup)
+        {
+            dataGroup.addEventListener(
+                RendererExistenceEvent.RENDERER_ADD, dispatchEvent);
+        }
+        
+        if (type == RendererExistenceEvent.RENDERER_REMOVE && dataGroup)
+        {
+            dataGroup.addEventListener(
+                RendererExistenceEvent.RENDERER_REMOVE, dispatchEvent);
+        }
+    }
+    
+    /**
+     *  @private
+     * 
+     *  This method is overridden so we can figure out when someone stops listening
+     *  for property change events.  If no one's listening for them, then we don't 
+     *  listen for them on our dataGroup.
+     */
+    override public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false) : void
+    {
+        super.removeEventListener(type, listener, useCapture);
+        
+        // if no one's listening to us for this event any more, let's 
+        // remove our underlying event listener from the dataGroup.
+        if (type == RendererExistenceEvent.RENDERER_ADD && dataGroup)
+        {
+            if (!hasEventListener(RendererExistenceEvent.RENDERER_ADD))
+            {
+                dataGroup.removeEventListener(
+                    RendererExistenceEvent.RENDERER_ADD, dispatchEvent);
+            }
+        }
+        
+        if (type == RendererExistenceEvent.RENDERER_REMOVE && dataGroup)
+        {
+            if (!hasEventListener(RendererExistenceEvent.RENDERER_REMOVE))
+            {
+                dataGroup.removeEventListener(
+                    RendererExistenceEvent.RENDERER_REMOVE, dispatchEvent);
+            }
+        }
+    }
+}
+
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/components/VideoDisplay.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/VideoDisplay.as
new file mode 100644
index 0000000..136fc4d
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/VideoDisplay.as
@@ -0,0 +1,2449 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.components
+{
+
+import flash.events.Event;
+import flash.geom.Point;
+import flash.geom.Rectangle;
+import flash.media.Video;
+
+import mx.core.IUIComponent;
+import mx.core.IVisualElement;
+import mx.core.UIComponent;
+import mx.core.mx_internal;
+import mx.events.FlexEvent;
+import mx.events.PropertyChangeEvent;
+import mx.resources.IResourceManager;
+import mx.resources.ResourceManager;
+
+
+import org.osmf.containers.MediaContainer;
+import org.osmf.elements.VideoElement;
+import org.osmf.events.AudioEvent;
+import org.osmf.events.DisplayObjectEvent;
+import org.osmf.events.LoadEvent;
+import org.osmf.events.MediaPlayerCapabilityChangeEvent;
+import org.osmf.events.MediaPlayerStateChangeEvent;
+import org.osmf.events.SeekEvent;
+import org.osmf.events.TimeEvent;
+import org.osmf.layout.HorizontalAlign;
+import org.osmf.layout.LayoutMetadata;
+import org.osmf.layout.ScaleMode;
+import org.osmf.layout.VerticalAlign;
+import org.osmf.media.DefaultMediaFactory;
+import org.osmf.media.MediaElement;
+import org.osmf.media.MediaFactory;
+import org.osmf.media.MediaFactoryItem;
+import org.osmf.media.MediaPlayer;
+import org.osmf.media.MediaPlayerState;
+import org.osmf.media.MediaResourceBase;
+import org.osmf.media.URLResource;
+import org.osmf.media.MediaType;
+import org.osmf.net.NetLoader;
+import org.osmf.net.DynamicStreamingItem;
+import org.osmf.net.rtmpstreaming.RTMPDynamicStreamingNetLoader;
+import org.osmf.net.DynamicStreamingResource;
+import org.osmf.net.FMSURL;
+import org.osmf.utils.OSMFStrings;
+import org.osmf.utils.URL;
+
+import spark.components.mediaClasses.DynamicStreamingVideoItem;
+import spark.components.mediaClasses.DynamicStreamingVideoSource;
+import spark.primitives.BitmapImage;
+
+use namespace mx_internal;
+
+//--------------------------------------
+//  Events
+//--------------------------------------
+
+/**
+ *  Dispatched when the data is received as a download operation progresses.
+ *  This event is only dispatched when playing a video by downloading it 
+ *  directly from a server, typically by issuing an HTTP request.
+ *  It is not displatched when playing a video from a special media server, 
+ *  such as Flash Media Server.
+ * 
+ *  <p>This event may not be dispatched when the source is set to null or a playback
+ *  error occurs.</p>
+ *
+ *  @eventType org.osmf.events.LoadEvent.BYTES_LOADED_CHANGE
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.0
+ *  @productversion Flex 4
+ */
+[Event(name="bytesLoadedChange",type="org.osmf.events.LoadEvent")]
+
+/**
+ *  Dispatched when the playhead reaches the duration for playable media.
+ * 
+ *  @eventType org.osmf.events.TimeEvent.COMPLETE
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.0
+ *  @productversion Flex 4
+ */  
+[Event(name="complete", type="org.osmf.events.TimeEvent")]
+
+/**
+ *  Dispatched when the <code>currentTime</code> property of the MediaPlayer has changed.
+ * 
+ *  <p>This event may not be dispatched when the source is set to null or a playback
+ *  error occurs.</p>
+ *
+ *  @eventType org.osmf.events.TimeEvent.CURRENT_TIME_CHANGE
+ *
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.0
+ *  @productversion Flex 4
+ */
+[Event(name="currentTimeChange",type="org.osmf.events.TimeEvent")]
+
+/**
+ *  Dispatched when the <code>duration</code> property of the media has changed.
+ * 
+ *  <p>This event may not be dispatched when the source is set to null or a playback
+ *  error occurs.</p>
+ * 
+ *  @eventType org.osmf.events.TimeEvent.DURATION_CHANGE
+ * 
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.0
+ *  @productversion Flex 4
+ */
+[Event(name="durationChange", type="org.osmf.events.TimeEvent")]
+
+/**
+ *  Dispatched when the MediaPlayer's state has changed.
+ * 
+ *  @eventType org.osmf.events.MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.0
+ *  @productversion Flex 4
+ */ 
+[Event(name="mediaPlayerStateChange", type="org.osmf.events.MediaPlayerStateChangeEvent")]
+
+//--------------------------------------
+//  Other metadata
+//--------------------------------------
+
+[DefaultProperty("source")]
+
+[ResourceBundle("osmf")]
+
+[IconFile("VideoDisplay.png")]
+
+/**
+ * Because this component does not define a skin for the mobile theme, Adobe
+ * recommends that you not use it in a mobile application. Alternatively, you
+ * can define your own mobile skin for the component. For more information,
+ * see <a href="http://help.adobe.com/en_US/Flex/4.0/UsingSDK/WS53116913-F952-4b21-831F-9DE85B647C8A.html">Spark Skinning</a>.
+ */
+[DiscouragedForProfile("mobileDevice")]
+
+/**
+ *  The VideoDisplay class is chromeless video player that supports
+ *  progressive download, multi-bitrate, and streaming video.
+ * 
+ *  <p><code>VideoDisplay</code> is the chromeless version that does not support skinning.
+ *  It is useful when you do not want the user to interact with the control.</p>
+ * 
+ *  <p><code>VideoPlayer</code> is the skinnable version.</p>
+ *
+ *  <p>The VideoDisplay control has the following default characteristics:</p>
+ *     <table class="innertable">
+ *        <tr>
+ *           <th>Characteristic</th>
+ *           <th>Description</th>
+ *        </tr>
+ *        <tr>
+ *           <td>Default size</td>
+ *           <td>0 pixels wide by 0 pixels high with no content, 
+ *             and the width and height of the video with content</td>
+ *        </tr>
+ *        <tr>
+ *           <td>Minimum size</td>
+ *           <td>0</td>
+ *        </tr>
+ *        <tr>
+ *           <td>Maximum size</td>
+ *           <td>10000 pixels wide and 10000 pixels high</td>
+ *        </tr>
+ *     </table>
+ *
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:VideoDisplay&gt;</code> tag inherits all of the tag 
+ *  attributes of its superclass and adds the following tag attributes:</p>
+ *
+ *  <pre>
+ *  &lt;s:VideoDisplay 
+ *    <strong>Properties</strong>
+ *    autoDisplayFirstFrame="true"
+ *    autoPlay="true"
+ *    autoRewind="true"
+ *    loop="false"
+ *    muted="false"
+ *    pauseWhenHidden="true"
+ *    scaleMode="letterbox"
+ *    source=""
+ *    volume="1"
+ *  
+ *    <strong>Events</strong>
+ *    bytesLoadedChange="<i>No default</i>"
+ *    complete="<i>No default</i>"
+ *    currentTimeChange="<i>No default</i>"
+ *    durationChange="<i>No default</i>"
+ *    mediaPlayerStateChange="<i>No default</i>"
+ *  
+ *  /&gt;
+ *  </pre>
+ *
+ *  @see spark.components.VideoPlayer
+ *
+ *  @includeExample examples/VideoDisplayExample.mxml
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class VideoDisplay extends UIComponent
+{
+    include "../core/Version.as";
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Class methods
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @private
+     *  Set as the OSMF.resourceBundleFunction and used to look up
+     *  strings so the OSMF RTEs are localized in Flex.
+     */
+    private static function getResourceString(resourceName:String,
+                                              args:Array = null):String
+    {
+        var resourceManager:IResourceManager = ResourceManager.getInstance();
+        return resourceManager.getString("osmf", resourceName, args);
+    }
+    
+    /**
+     * Copied from OSMF ScaleModeUtils.getScaledSize. ScaleModeUtils became
+     * an internal OSMF class in OSMF 1.0 so it is copied here.
+     * 
+     * Calculates the scaled size based on the scaling algorithm.  
+     * The available width and height are the width and height of the container.
+     * The intrinsic width and height are the width and height of the content.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion OSMF 1.0
+     */ 
+    private static function getScaledSize
+        ( scaleMode:String
+          , availableWidth:Number, availableHeight:Number
+            , intrinsicWidth:Number, intrinsicHeight:Number
+        ):Point
+    {
+        var result:Point;
+        
+        switch (scaleMode)
+        {
+            case ScaleMode.ZOOM:
+            case ScaleMode.LETTERBOX:
+                
+                var availableRatio:Number
+                = availableWidth
+                / availableHeight;
+                
+                var componentRatio:Number 
+                = (intrinsicWidth || availableWidth)
+                / (intrinsicHeight || availableHeight);
+                
+                if  (   (scaleMode == ScaleMode.ZOOM && componentRatio < availableRatio) 
+                    ||  (scaleMode == ScaleMode.LETTERBOX && componentRatio > availableRatio)
+                )
+                {
+                    result 
+                    = new Point
+                        ( availableWidth
+                            , availableWidth / componentRatio
+                        );
+                }
+                else
+                {
+                    result
+                    = new Point
+                        ( availableHeight * componentRatio
+                            , availableHeight
+                        );
+                }
+                
+                break;
+            
+            case ScaleMode.STRETCH:
+                
+                result 
+                = new Point
+                ( availableWidth
+                    , availableHeight
+                );
+                break;
+            
+            case ScaleMode.NONE:
+                
+                result
+                = new Point
+                ( intrinsicWidth    || availableWidth
+                    , intrinsicHeight   || availableHeight
+                );
+                
+                break;
+        }
+        
+        return result;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  Constructor.
+     *   
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function VideoDisplay()
+    {
+        super();
+        
+        // create the underlying MediaPlayer class first.
+        createUnderlyingVideoPlayer();
+        
+        // added and removed event listeners to see whether we should
+        // start or stop the video
+        addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
+        addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
+        
+        // Set the OSMF hook used for localizing runtime error messages.
+        // OSMF itself has English-only messages,
+        // but higher layers like Flex can provide localized versions.
+        OSMFStrings.resourceStringFunction = getResourceString;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Variables
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @private
+     *  This is the underlying VideoPlayer object. At some point in the 
+     *  future, we may change to a new implementation.
+     */
+    mx_internal var videoPlayer:MediaPlayer;
+    
+    /**
+     *  @private
+     *  This is the underlying container used to display
+     *  the underlying videoPlayer.
+     */
+    mx_internal var videoContainer:MediaContainer;
+      
+    /**
+     *  @private
+     *  How the correct media elements are created based on the url of
+     *  the resource.
+     */
+    mx_internal var mediaFactory:MediaFactory;
+      
+    /**
+     *  @private
+     *  Whether the video is on the display list or not
+     */
+    private var _isOnDisplayList:Boolean = false;
+    
+    /**
+     *  @private
+     *  Whether the we should play the video when the video 
+     *  becomes playable again (visible, on display list, and enabled).
+     *  This starts out as true, but when we pause the video is changePlayback(), 
+     *  we set it to false.  Also, when a user action occurs, like pause() or play()
+     *  or stop() is called, we set it to false as well.
+     */
+    private var playTheVideoOnVisible:Boolean = true;
+    
+    /**
+     *  @private
+     */
+    private var effectiveVisibility:Boolean = false;
+    
+    /**
+     *  @private
+     */
+    private var effectiveVisibilityChanged:Boolean = false;
+        
+    /**
+     *  @private
+     */
+    private var effectiveEnabled:Boolean = false;
+    
+    /**
+     *  @private
+     */
+    private var effectiveEnabledChanged:Boolean = false;
+    
+    /**
+     *  @private
+     *  We do different things in the source setter based on if we 
+     *  are initialized or not.
+     */
+    private var initializedOnce:Boolean = false;
+    
+    /**
+     *  @private
+     *  Keeps track of the muted property while loading up a 
+     *  video because of autoDisplayFirstFrame.
+     */
+    private var beforeLoadMuted:Boolean;
+    
+    /**
+     *  @private
+     *  Keeps track whether we are loading up the
+     *  video because of autoDisplayFirstFrame.
+     * 
+     *  <p>In this case we are in "state1" of loading, 
+     *  which means we are waiting for the READY 
+     *  MediaPlayerStateChangeEvent and haven't done anything yet.</p>
+     */
+    private var inLoadingState1:Boolean;
+    
+    /**
+     *  @private
+     *  Keeps track whether we are loading up the
+     *  video because of autoDisplayFirstFrame.
+     * 
+     *  <p>In this case we are in "state2" of loading, 
+     *  which means have set videoPlayer.displayObject.visible=false  
+     *  and videoPlayer.muted=true.  We've also called play() and are 
+     *  waiting for the DimensionChangeEvent.</p>
+     * 
+     *  <p>Note: At this point, inLoadingState1 = true as well.</p>
+     */
+    private var inLoadingState2:Boolean;
+    
+    /**
+     *  @private
+     *  Keeps track whether we are loading up the
+     *  video because of autoDisplayFirstFrame.
+     * 
+     *  <p>In this case we are in "state3" of loading, 
+     *  which means have received the DimensionChangeEvent and have called 
+     *  pause() and seek(0).  We are currently waiting for the 
+     *  SEEK_END event, at which point we will be completely loaded up.</p>
+     * 
+     *  <p>Note: At this point, inLoadingState1 = inLoadingState2 = true.</p>
+     */
+    private var inLoadingState3:Boolean;
+    
+    /**
+     *  @private
+     * 
+     *  Old value of videoPlayer.currentTimeUpdateInterval. We store the old 
+     *  value when we are taken off the stage and pauseWhenHidden is true 
+     *  (removedFromStageHandler). We restore the old value when we are added
+     *  to the stage (addedToStageHandler). The value is used in setupSource()
+     *  to turn the timers back on if they were disabled. The value if also used
+     *  to check if we should turn off the timers in videoPlayer_seekChangeHandler
+     *  and videoPlayer_currentTimeChangeHandler.
+     *  This is done to keep this component from being pinned in memory by the timer 
+     *  associated the currentTimeUpdateInterval property.
+     */
+    private var oldCurrentTimeUpdateInterval:Number = NaN;
+    
+    /**
+     *  @private
+     * 
+     *  Old value of videoPlayer.bytesLoadedUpdateInterval. We store the old 
+     *  value when we are taken off the stage and pauseWhenHidden is true 
+     *  (removedFromStageHandler). We restore the old value when we are added
+     *  to the stage (addedToStageHandler). The value is used in setupSource()
+     *  to turn the timers back on if they were disabled. The value if also used
+     *  to check if we should turn off the timers in videoPlayer_seekChangeHandler
+     *  and videoPlayer_currentTimeChangeHandler.
+     *  This is done to keep this component from being pinned in memory by the timer 
+     *  associated the bytesLoadedUpdateInterval property.
+     */
+    private var oldBytesLoadedUpdateInterval:Number = NaN;
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+    
+    //----------------------------------
+    //  autoDisplayFirstFrame
+    //----------------------------------
+    
+    /**
+     *  @private
+     */
+    private var _autoDisplayFirstFrame:Boolean = true;
+        
+    [Inspectable(category="General", defaultValue="true")]
+    
+    /**
+     *  If <code>autoPlay = false</code>, then 
+     *  <code>autoDisplayFirstFrame</code> controls whether the video 
+     *  is loaded when the <code>source</code> is set.  
+     *  If <code>autoDisplayFirstFrame</code>
+     *  is set to <code>true</code>, then the first frame of the video is 
+     *  loaded and the video is sized correctly.  
+     *  If <code>autoDisplayFirstFrame</code> is set to <code>false</code>, then no 
+     *  connection to the source is made, the first frame is not shown, 
+     *  and the video's size is not determined until someone tries to play
+     *  the video.
+     * 
+     *  <p>If <code>autoPlay = true</code>, then this flag is ignored.</p>
+     *  
+     *  @default true
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get autoDisplayFirstFrame():Boolean
+    {
+        return _autoDisplayFirstFrame;
+    }
+    
+    /**
+     * @private
+     */
+    public function set autoDisplayFirstFrame(value:Boolean):void
+    {
+        _autoDisplayFirstFrame = value;
+    }
+    
+    //----------------------------------
+    //  autoPlay
+    //----------------------------------
+    
+    /**
+     * @private
+     */
+    private var _autoPlay:Boolean = true;
+    
+    [Inspectable(category="General", defaultValue="true")]
+    
+    /**
+     *  Specifies whether the video starts playing immediately when the
+     *  <code>source</code> property is set.
+     *  If <code>true</code>, the video file immediately begins to buffer and
+     *  play.
+     *
+     *  <p>Even if <code>autoPlay</code> is set to <code>false</code>, Flex
+     *  starts loading the video after the <code>initialize()</code> method is
+     *  called.
+     *  For Flash Media Server, this means creating the stream and loading
+     *  the first frame to display.
+     *  In the case of an HTTP download, Flex starts downloading the stream
+     *  and shows the first frame.</p>
+     * 
+     *  <p>If <code>playWhenHidden</code> is set to <code>false</code>, then 
+     *  <code>autoPlay</code> also affects what happens when the video 
+     *  comes back on stage and is enabled and visible.</p>
+     *  
+     *  @default true
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get autoPlay():Boolean
+    {
+        return _autoPlay;
+    }
+    
+    /**
+     * @private (setter)
+     */
+    public function set autoPlay(value:Boolean):void
+    {
+        if (autoPlay == value)
+            return;
+        
+        _autoPlay = value;
+        
+        // call changePlayback() but don't immediately play or pause
+        // based on this change to autoPlay
+        changePlayback(false, false);
+    }
+    
+    //----------------------------------
+    //  autoRewind
+    //----------------------------------
+    
+    [Inspectable(category="General", defaultValue="true")]
+    
+    /**
+     *  Specifies whether the FLV file should rewind to the first frame
+     *  when play stops, either by calling the <code>stop()</code> method or by
+     *  reaching the end of the stream.
+     *
+     *  <p>This property has no effect for live streaming video.</p>
+     *
+     *  @default true
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get autoRewind():Boolean
+    {
+        return videoPlayer.autoRewind;
+    }
+    
+    public function set autoRewind(value:Boolean):void
+    {
+        videoPlayer.autoRewind = value;
+    }
+    
+    //----------------------------------
+    //  bytesLoaded
+    //----------------------------------
+    
+    [Inspectable(Category="General", defaultValue="0")]
+    [Bindable("bytesLoadedChange")]
+    [Bindable("mediaPlayerStateChange")]
+    
+    /**
+     *  The number of bytes of data that have been downloaded into the application.
+     *
+     *  @return The number of bytes of data that have been downloaded into the application.
+     *  @default 0
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get bytesLoaded():Number
+    {
+        return videoPlayer.bytesLoaded;
+    }
+    
+    //----------------------------------
+    //  bytesTotal
+    //----------------------------------
+    
+    [Inspectable(Category="General", defaultValue="0")]
+    [Bindable("mediaPlayerStateChange")]
+    
+    /**
+     *  The total size in bytes of the data being downloaded into the application.
+     *
+     *  @return The total size in bytes of the data being downloaded into the application.
+     *  @default 0
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get bytesTotal():Number
+    {
+        return videoPlayer.bytesTotal;
+    }
+    
+    //----------------------------------
+    //  currentTime
+    //----------------------------------
+    
+    [Inspectable(Category="General", defaultValue="0")]
+    [Bindable("currentTimeChange")]
+    [Bindable("mediaPlayerStateChange")]
+    
+    /**
+     *  Current time of the playhead, measured in seconds, 
+     *  since the video starting playing. 
+     *
+     *  @default 0
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get currentTime():Number
+    {
+        return videoPlayer.currentTime;
+    }
+    
+    //----------------------------------
+    //  duration
+    //----------------------------------
+    
+    [Inspectable(Category="General", defaultValue="0")]
+    [Bindable("durationChange")]
+    [Bindable("mediaPlayerStateChange")]
+    
+    /**
+     *  Duration of the video's playback, in seconds
+     *
+     *  @return The total running time of the video in seconds
+     *  @default 0
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get duration():Number
+    {
+        return videoPlayer.duration;
+    }
+    
+    //----------------------------------
+    //  loop
+    //----------------------------------
+    
+    [Inspectable(Category="General", defaultValue="false")]
+    
+    /**
+     *  Indicates whether the media should play again after playback has completed. 
+     *  The <code>loop</code> property takes precedence over the <code>autoRewind</code>
+     *  property, so if loop is set to <code>true</code>, the <code>autoRewind</code> 
+     *  property is ignored. 
+     *
+     *  @default false
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get loop():Boolean
+    {
+        return videoPlayer.loop;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set loop(value:Boolean):void
+    {
+        if (loop == value)
+            return;
+        
+        videoPlayer.loop = value;
+    }
+    
+    //----------------------------------
+    //  mediaPlayerState
+    //----------------------------------
+    
+    [Inspectable(category="General", defaultValue="uninitialized")]
+    [Bindable("mediaPlayerStateChange")]
+    
+    /**
+     *  The current state of the video.  See 
+     *  org.osmf.media.MediaPlayerState for available values.
+     *  
+     *  @default uninitialized
+     * 
+     *  @see org.osmf.media.MediaPlayerState
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get mediaPlayerState():String
+    {
+        return videoPlayer.state;
+    }
+    
+    //----------------------------------
+    //  muted
+    //----------------------------------
+    
+    [Inspectable(category="General", defaultValue="false")]
+    [Bindable("volumeChanged")]
+    
+    /**
+     *  Set to <code>true</code> to mute the video, <code>false</code> 
+     *  to unmute the video.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get muted():Boolean
+    {
+        // if inLoadingState2, we've got to 
+        // fake the muted value
+        if (inLoadingState2)
+            return beforeLoadMuted;
+        
+        return videoPlayer.muted;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set muted(value:Boolean):void
+    {
+        if (muted == value)
+            return;
+        
+        // if inLoadingState2, don't change muted...just fake it
+        if (inLoadingState2)
+        {
+            beforeLoadMuted = value;
+            return;
+        }
+        
+        videoPlayer.muted = value;
+    }
+    
+    //----------------------------------
+    //  pauseWhenHidden
+    //----------------------------------
+    
+    /**
+     *  @private
+     *  Storage variable for pauseWhenHidden
+     */
+    private var _pauseWhenHidden:Boolean = true;
+    
+    [Inspectable(category="General", defaultValue="true")]
+    
+    /**
+     *  Controls whether the video continues to play when it is
+     *  "hidden".  The video is "hidden" when either <code>visible</code>
+     *  is set to <code>false</code> on it or one of its ancestors,  
+     *  or when the video is taken off 
+     *  of the display list.  If set to <code>true</code>, the video 
+     *  pauses playback until the video is visible again.  If set to 
+     *  <code>false</code> the video continues to play when it is hidden.
+     * 
+     *  <p>If the video is disabled (or one of the video's parents are 
+     *  disabled), the video pauses as well, but when it is re-enabled, 
+     *  the video does not play again.  This behavior is not controlled through 
+     *  <code>pauseWhenHidden</code>; it is defined in the VideoDisplay component.</p>
+     * 
+     *  @default true
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get pauseWhenHidden():Boolean
+    {
+        return _pauseWhenHidden;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set pauseWhenHidden(value:Boolean):void
+    {
+        if (_pauseWhenHidden == value)
+            return;
+        
+        _pauseWhenHidden = value;
+        
+        if (_pauseWhenHidden)
+        {
+            addVisibilityListeners();
+            computeEffectiveVisibilityAndEnabled();
+        }
+        else
+        {
+            removeVisibilityListeners();
+        }
+        
+        // call changePlayback().  If we're invisible or off the stage, 
+        // setting this to true can pause the video.  However, setting it 
+        // to false should have no immediate impact.
+        changePlayback(value, false);
+    }
+    
+    //----------------------------------
+    //  playing
+    //----------------------------------
+    
+    [Inspectable(category="General")]
+    [Bindable("mediaPlayerStateChange")]
+    
+    /**
+     *  Contains <code>true</code> if the video is playing or is attempting to play.
+     *  
+     *  <p>The video may not be currently playing, as it may be seeking 
+     *  or buffering, but the video is attempting to play.</p> 
+     *
+     *  @see #play()
+     *  @see #pause()
+     *  @see #stop()
+     *  @see #autoPlay
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get playing():Boolean
+    {
+        return videoPlayer.playing;
+    }
+    
+    //----------------------------------
+    //  scaleMode
+    //----------------------------------
+    
+    /**
+     *  @private
+     */
+    private var _scaleMode:String = ScaleMode.LETTERBOX;
+    
+    [Inspectable(Category="General", enumeration="none,stretch,letterbox,zoom", defaultValue="letterbox")]
+    
+    /**
+     *  The <code>scaleMode</code> property describes different ways of 
+     *  sizing the video content.  
+     *  You can set <code>scaleMode</code> to 
+     *  <code>"none"</code>, <code>"stretch"</code>, 
+     *  <code>"letterbox"</code>, or <code>"zoom"</code>.
+     * 
+     *  <p>If no width, height, or constraints are specified on the component, 
+     *  this property has no effect.</p>
+     *
+     *  @default "letterbox"
+     *
+     *  @see org.osmf.display.ScaleMode
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get scaleMode():String
+    {
+        return _scaleMode;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set scaleMode(value:String):void
+    {
+        if (scaleMode == value)
+            return;
+        
+        switch(value)
+        {
+            case ScaleMode.NONE:
+                _scaleMode = ScaleMode.NONE;
+                break;
+            case ScaleMode.STRETCH:
+                _scaleMode = ScaleMode.STRETCH;
+                break;
+            case ScaleMode.LETTERBOX:
+                _scaleMode = ScaleMode.LETTERBOX;
+                break;
+            case ScaleMode.ZOOM:
+                _scaleMode = ScaleMode.ZOOM;
+                break;
+            default:
+                _scaleMode = ScaleMode.LETTERBOX;
+                break;
+        }
+        
+        // set scaleMode on the videoElement object
+        if (videoPlayer.media)
+        {
+            var layout:LayoutMetadata = videoPlayer.media.
+                                getMetadata(LayoutMetadata.LAYOUT_NAMESPACE) as LayoutMetadata;
+            if (layout)
+                layout.scaleMode = _scaleMode;
+        }
+        
+        invalidateSize();
+        invalidateDisplayList();
+    }
+    
+    //----------------------------------
+    //  source
+    //----------------------------------
+    
+    private var _source:Object;
+    private var sourceChanged:Boolean;
+        
+    [Inspectable(category="General", defaultValue="null")]
+    [Bindable("sourceChanged")]
+    
+    /**
+     *  The video source.
+     * 
+     *  <p>For progressive download, the source is just a path or URL pointing 
+     *  to the video file to play.</p>
+     * 
+     *  <p>For streaming (recorded streaming, live streaming, 
+     *  or multi-bitrate streaming), the source property is a 
+     *  DynamicStreamingVideoSource object.  If you just want to play 
+     *  a recorded or live streaming video with no multi-bitrate support, 
+     *  you can just pass in a String URL pointing to the video 
+     *  stream.  However, if you do this, the streamType is assumed to be "any," 
+     *  and you don't have as much control over the streaming as you would if 
+     *  you used the DynamicStreamingVideoSource object.</p>
+     * 
+     *  <p>Note: Setting the source on a MediaPlayerStateChangeEvent.LOADING or a 
+     *  MediaPlayerStateChangeEvent.READY is not recommended if the source was 
+     *  previously set.  This could cause an infinite loop or an RTE.  
+     *  If you must do an operation like that, wait an additional frame to 
+     *  set the source.</p>
+     *
+     *  @see spark.components.mediaClasses.DynamicStreamingVideoSource
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get source():Object
+    {
+        return _source;
+    }
+    
+    /**
+     * @private (setter)
+     */
+    public function set source(value:Object):void
+    {
+        _source = value;
+        
+        // if we haven't initialized, let's wait to set up the 
+        // source in commitProperties() as it is dependent on other 
+        // properties, like autoPlay and enabled, and those may not 
+        // be set yet, especially if they are set via MXML.
+        // Otherwise, if we have initialized, let's just set up the 
+        // source immediately.  This way people can change the source 
+        // and immediately call methods like seek().
+        if (!initializedOnce)
+        {
+            sourceChanged = true;
+            invalidateProperties();
+        }
+        else
+        {
+            setUpSource();
+        }
+        
+        dispatchEvent(new Event("sourceChanged"));
+    }
+    
+    //----------------------------------
+    //  thumbnailSource
+    //----------------------------------
+    
+    /**
+     *  @private
+     */
+    private var _thumbnailSource:Object;
+    
+    /**
+     *  @private
+     *  Group that holds the BitmapImage for the thumbnail
+     */
+    private var thumbnailGroup:Group;
+    
+    /**
+     *  @private
+     *  BitmapImage for the thumbnail
+     */
+    private var thumbnailBitmapImage:BitmapImage;
+    
+    [Inspectable(Category="General")]
+    
+    /**
+     *  @private
+     *  Thumbnail source is an internal property used to replace the video with a thumbnail.
+     *  This is for places where we just want to load in a placeholder object for the video 
+     *  and don't want to incur the extra load-time or memory of loading up the video.
+     * 
+     *  <p>Thumbnail source can take any valid source that can be passed in to 
+     *  <code>spark.primitivies.BitmapImage#source</code>.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    mx_internal function get thumbnailSource():Object
+    {
+        return _thumbnailSource;
+    }
+    
+    /**
+     *  @private
+     */
+    mx_internal function set thumbnailSource(value:Object):void
+    {
+        if (_thumbnailSource == value)
+            return;
+        
+        _thumbnailSource = value;
+        
+        // if we haven't initialized, let's wait to set up the 
+        // source in commitProperties() as it is dependent on other 
+        // properties, like autoPlay and enabled, and those may not 
+        // be set yet, especially if they are set via MXML.
+        // Otherwise, if we have initialized, let's just set up the 
+        // source immediately.  This way people can change the source 
+        // and immediately call methods like seek().
+        if (!initializedOnce)
+        {
+            sourceChanged = true;
+            invalidateProperties();
+        }
+        else
+        {
+            setUpThumbnailSource();
+        }
+    }
+    
+    /**
+     *  @private
+     *  Sets up the thumbnail source for use.
+     */
+    private function setUpThumbnailSource():void
+    {
+        if (thumbnailSource)
+        {            
+            // create thumbnail group if there isn't one
+            if (!thumbnailGroup)
+            {
+                thumbnailBitmapImage = new BitmapImage();
+                thumbnailBitmapImage.includeInLayout = false;
+                
+                thumbnailGroup = new Group();
+                // add thumbnailGroup to the display list first in case
+                // bitmap needs to check its layoutDirection.
+                addChild(thumbnailGroup);
+                thumbnailGroup.clipAndEnableScrolling = true;
+                thumbnailGroup.addElement(thumbnailBitmapImage);
+            }
+            
+            // if thumbnailGroup isn't on the display list, then add it.
+            if (!this.contains(thumbnailGroup))
+                addChild(thumbnailGroup);
+            
+            thumbnailBitmapImage.source = thumbnailSource;
+            invalidateSize();
+            invalidateDisplayList();
+        }
+        else
+        {
+            if (thumbnailGroup)
+            {
+                // null out the source and remove the thumbnail group
+                if (thumbnailBitmapImage)
+                    thumbnailBitmapImage.source = null;
+                if (this.contains(thumbnailGroup))
+                    removeChild(thumbnailGroup);
+                invalidateSize();
+            }
+        }
+    }
+    
+    //----------------------------------
+    //  videoObject
+    //----------------------------------
+    
+    [Inspectable(category="General", defaultValue="null")]
+    
+    /**
+     *  The underlying flash player <code>flash.media.Video</code> object.
+     * 
+     *  <p>If the source is <code>null</code>, then there may be no 
+     *  underlying <code>flash.media.Video object</code> yet.  In that 
+     *  case, <code>videoObject</code> returns <code>null</code>.</p>
+     * 
+     *  @default null
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get videoObject():Video
+    {
+        return videoPlayer.displayObject as Video;
+    }
+    
+    //----------------------------------
+    //  volume
+    //----------------------------------
+    
+    [Inspectable(category="General", defaultValue="1.0", minValue="0.0", maxValue="1.0")]
+    [Bindable("volumeChanged")]
+    
+    /**
+     *  The volume level, specified as a value between 0 and 1.
+     * 
+     *  @default 1
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get volume():Number
+    {
+        return videoPlayer.volume;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set volume(value:Number):void
+    {
+        if (volume == value)
+            return;
+        
+        videoPlayer.volume = value;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden methods
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @private
+     */
+    override protected function commitProperties():void
+    {
+        super.commitProperties();
+        
+        initializedOnce = true;
+        
+        if (effectiveVisibilityChanged || effectiveEnabledChanged)
+        {
+            // if either visibility of enabled changed, re-compute them here
+            computeEffectiveVisibilityAndEnabled();
+            
+            // if visibility changed and we care about it, we can 
+            // cause a play or a pause depending on our visibility
+            var causePause:Boolean = false;
+            var causePlay:Boolean = false;
+            if (effectiveVisibilityChanged && pauseWhenHidden)
+            {
+                causePause = !effectiveVisibility;
+                causePlay = effectiveVisibility;
+            }
+            
+            // if enabled changed, we can only cause a pause.  
+            // Re-enabling a component doesn't cause a play.
+            if (effectiveEnabledChanged)
+            {
+                if (!effectiveEnabled)
+                    causePause = true;
+            }
+            
+            changePlayback(causePause, causePlay);
+            
+            effectiveVisibilityChanged = false;
+            effectiveEnabledChanged = false;
+        }
+        
+        if (sourceChanged)
+        {
+            sourceChanged = false;
+            
+            if (thumbnailSource)
+                setUpThumbnailSource();
+            else
+                setUpSource();
+        }
+    }
+    
+    /**
+     *  @private
+     */
+    override protected function measure() : void
+    {
+        super.measure();
+        
+        var intrinsicWidth:Number;
+        var intrinsicHeight:Number;
+        
+        // if showing the thumbnail, just use the thumbnail's size
+        if (thumbnailSource && thumbnailGroup)
+        {
+            intrinsicWidth = thumbnailBitmapImage.getPreferredBoundsWidth();
+            intrinsicHeight = thumbnailBitmapImage.getPreferredBoundsHeight();
+        }
+        else
+        {
+            // If there is no media the width/height will be NaN.
+            // Convert it to zero for our purposes.
+            intrinsicWidth = videoPlayer.mediaWidth;
+            if (isNaN(intrinsicWidth))
+                intrinsicWidth = 0;
+
+            intrinsicHeight = videoPlayer.mediaHeight;
+            if (isNaN(intrinsicHeight))
+                intrinsicHeight = 0;
+        }
+
+        measuredWidth = intrinsicWidth;
+        measuredHeight = intrinsicHeight;
+        
+        // Determine whether 'width' and 'height' have been set.
+        var bExplicitWidth:Boolean = !isNaN(explicitWidth);
+        var bExplicitHeight:Boolean = !isNaN(explicitHeight);
+
+        // If only one has been set, calculate the other based on aspect ratio.
+        if (bExplicitWidth && !bExplicitHeight && intrinsicWidth > 0)
+            measuredHeight = explicitWidth * intrinsicHeight / intrinsicWidth;
+        else if (bExplicitHeight && !bExplicitWidth && intrinsicHeight > 0)
+            measuredWidth = explicitHeight * intrinsicWidth / intrinsicHeight;
+    }
+    
+    /**
+     *  @private
+     */
+    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void
+    {
+        super.updateDisplayList(unscaledWidth, unscaledHeight);
+        
+        // if just showing the thumbnail, push this width/height in to the thumbnail
+        // otherwise we'll push it in to the video object
+        if (thumbnailSource && thumbnailGroup)
+        {
+            // get what the size of our image should be
+            var newSize:Point = getScaledSize(scaleMode, unscaledWidth, unscaledHeight, 
+                thumbnailBitmapImage.getPreferredBoundsWidth(), thumbnailBitmapImage.getPreferredBoundsHeight());
+            
+            // set the thumbnailGroup to be the size of the component.
+            // set the bitmap image to be the size it should be according to OSMF
+            thumbnailGroup.setLayoutBoundsSize(unscaledWidth, unscaledHeight);
+            thumbnailBitmapImage.setLayoutBoundsSize(newSize.x, newSize.y);
+            
+            // center the thumnail image within the thumbnail group.
+            // if it's too big to fit, the thumbnail group will crop it
+            thumbnailBitmapImage.x = (unscaledWidth - newSize.x)/2;
+            thumbnailBitmapImage.y = (unscaledHeight - newSize.y)/2;
+            
+            return;
+        }
+        
+        videoContainer.width = Math.floor(unscaledWidth);
+        videoContainer.height = Math.floor(unscaledHeight);
+
+        // need to validate the gateway immediately--otherwise we may run out of synch
+        // as they may wait a frame by default before validating (see SDK-24880)
+        videoContainer.validateNow();
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Methods
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  Pauses playback without moving the playhead. 
+     *  If playback is already is paused or is stopped, this method has no
+     *  effect.  
+     *
+     *  <p>To start playback again, call the <code>play()</code> method.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function pause():void
+    {
+        // check to see if we can call methods on the video player object yet
+        if (!videoPlayerResponsive())
+            return;
+        
+        playTheVideoOnVisible = false;
+        
+        // if we're loading up, then we will pause automatically, so let's 
+        // not interrupt this process
+        // if inLoadingState1 && pausable, then let loading state handle it
+        // if inLoadingState1 && !pausable, then let the loading state handle it
+        // if !inLoadingState1 && pausable, then just pause
+        // if !inLoadingState1 && !pausable, then load (if needed to show first frame)
+        if (!inLoadingState1 && videoPlayer.canPause)
+            videoPlayer.pause();
+        else if (!videoPlayer.canPause && autoDisplayFirstFrame)
+            load();
+    }
+    
+    /**
+     *  Causes the video to play.  Can be called while the video is
+     *  paused, stopped, or while the video is already playing.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function play():void
+    {
+        // check to see if we can call methods on the video player object yet
+        if (!videoPlayerResponsive())
+            return;
+        
+        playTheVideoOnVisible = false;
+        
+        // if we're loading up, use a special method to cancel the load
+        // and to start playing again.  Otherwise, go ahead and play
+        if (inLoadingState1)
+            cancelLoadAndPlay();
+        else if (videoPlayer.canPlay)
+            videoPlayer.play();
+    }
+    
+    /**
+     *  Seeks to given time in the video. If the video is playing,
+     *  continue playing from that point. If the video is paused, seek to
+     *  that point and remain paused. If the video is stopped, seek to
+     *  that point and enters paused state. 
+     *  This method has no effect with live video streams.
+     *
+     *  <p>If time is less than 0 or NaN, throws exception. If time
+     *  is past the end of the stream, or past the amount of file
+     *  downloaded so far, then attempts to seek and, if it fails, it then recovers.</p>
+     * 
+     *  <p>The <code>currentTime</code> property might not have the expected value 
+     *  immediately after you call <code>seek()</code>. 
+     *  For a progressive download,
+     *  you can seek only to a keyframe; therefore, a seek takes you to the 
+     *  time of the first keyframe after the specified time.</p>
+     *  
+     *  <p><strong>Note</strong>: When streaming, a seek always goes to the precise specified 
+     *  time even if the source FLV file doesn't have a keyframe there.</p>
+     *
+     *  <p>Seeking is asynchronous, so if you call the <code>seek()</code> method, 
+     *  <code>currentTime</code> does not update immediately. </p>
+     *
+     *  @param time The seek time, in seconds.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function seek(time:Number):void
+    {
+        // check to see if we can call methods on the video player object yet
+        if (!videoPlayerResponsive())
+            return;
+        
+        // TODO (rfrishbe): could handle what to do if this gets called when loading() better.
+        // Need to store where we want to seek to.
+        if (videoPlayer.canSeek)
+            videoPlayer.seek(time);
+    }
+    
+    /**
+     *  Stops video playback.  If <code>autoRewind</code> is set to
+     *  <code>true</code>, rewinds to first frame.  If video is already
+     *  stopped, has no effect.  To start playback again, call
+     *  <code>play()</code>.
+     *
+     *  @see #autoRewind
+     *  @see #play()
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function stop():void
+    {
+        // check to see if we can call methods on the video player object yet
+        if (!videoPlayerResponsive())
+            return;
+        
+        playTheVideoOnVisible = false;
+        
+        // if we're loading up, then we will stop automatically, so let's 
+        // not interrupt this process
+        // if inLoadingState1 && pausable, then let loading state handle it
+        // if inLoadingState1 && !pausable, then let the loading state handle it
+        // if !inLoadingState1 && pausable, then just pause
+        // if !inLoadingState1 && !pausable, then load (if needed to show first frame)
+        if (!inLoadingState1 && videoPlayer.canPause)
+            videoPlayer.stop();
+        else if (!videoPlayer.canPause && autoDisplayFirstFrame)
+            load();
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Private Methods
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @private
+     *  If the video player is responsive, then methods can be called on the underlying 
+     *  video player.
+     */
+    private function videoPlayerResponsive():Boolean
+    {
+        // can't call any methods before we've initialized
+        if (!initializedOnce)
+            return false;
+        
+        // if displaying a thumbnail, no methods can be called b/c there's no video 
+        // loaded up
+        if (thumbnailSource)
+            return false;
+        
+        // if the video player's in a bad state, we can't do anything
+        if (videoPlayer.state == MediaPlayerState.PLAYBACK_ERROR || 
+            videoPlayer.state == MediaPlayerState.UNINITIALIZED || 
+            videoPlayer.state == MediaPlayerState.LOADING)
+        {
+            return false;
+        }
+        
+        // if no source, return false as well
+        if (!source)
+            return false;
+        
+        // otherwise, we are in a good state and have a source, so let's go
+        return true;
+    }
+    
+    /**
+     *  @private
+     */
+    private function createUnderlyingVideoPlayer():void
+    {
+        // create new video player
+        videoPlayer = new MediaPlayer();
+        videoContainer = new MediaContainer();
+        videoContainer.clipChildren = true;
+        
+        mediaFactory = new DefaultMediaFactory();
+        
+        // remove unsupport media types
+        var unsupportedMediaTypes:Array = ["org.osmf.elements.video.dvr.dvrcast",
+                                           "org.osmf.elements.image", 
+                                           "org.osmf.elements.swf"];
+        
+        for each (var mediaType:String in unsupportedMediaTypes)
+        {
+            var mediaFactoryItem:MediaFactoryItem = mediaFactory.getItemById(mediaType);
+            if (mediaFactoryItem)
+                mediaFactory.removeItem(mediaFactoryItem);
+        }
+
+        // internal events
+        videoPlayer.addEventListener(DisplayObjectEvent.MEDIA_SIZE_CHANGE, videoPlayer_mediaSizeChangeHandler);
+        videoPlayer.addEventListener(AudioEvent.VOLUME_CHANGE, videoPlayer_volumeChangeHandler);
+        videoPlayer.addEventListener(AudioEvent.MUTED_CHANGE, videoPlayer_mutedChangeHandler);
+        
+        // public events
+        videoPlayer.addEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoPlayer_mediaPlayerStateChangeHandler);
+        videoPlayer.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, dispatchEvent);
+        videoPlayer.addEventListener(LoadEvent.BYTES_LOADED_CHANGE, dispatchEvent);
+        videoPlayer.addEventListener(TimeEvent.DURATION_CHANGE, videoPlayer_durationChangeHandler);
+        videoPlayer.addEventListener(TimeEvent.COMPLETE, dispatchEvent);
+        
+        addChild(videoContainer);
+    }
+    
+    /**
+     *  @private
+     *  Sets up the source for use.
+     */
+    private function setUpSource():void
+    {
+        // clean up any listeners from the old source, especially if we 
+        // are in the processing of loading that video file up
+        cleanUpSource()
+        
+        // if was playing a previous video, let's remove it now
+        if (videoPlayer.media && videoContainer.containsMediaElement(videoPlayer.media))
+        {
+            videoContainer.removeMediaElement(videoPlayer.media);
+        }
+        
+        var videoElement:org.osmf.media.MediaElement = null;
+        
+        // check for 4 cases: streaming video, progressive download, 
+        // an IMediaResource, or a VideoElement.  
+        // The latter 2 are undocumented but allowed for flexibility until we 
+        // can support OSMF better after they ship OSMF 1.0.  At that point, support 
+        // for a source as an IMediaResource or a VideoElement may be removed.
+        if (source is DynamicStreamingVideoSource)
+        {
+            // the streaming video case.
+            // build up a DynamicStreamingResource to pass in to OSMF
+            var streamingSource:DynamicStreamingVideoSource = source as DynamicStreamingVideoSource;
+            var dsr:DynamicStreamingResource;
+            
+            // check for two cases for host: String and URL.
+            // Technically, we only support URL, but we secretly allow 
+            // them to send in an OSMF URL or FMSURL here to help resolve any ambiguity
+            // around serverName vs. streamName.
+            if (streamingSource.host is String)
+            {
+                dsr = new DynamicStreamingResource(streamingSource.host as String, 
+                                                   streamingSource.streamType);
+            }
+            else if (streamingSource.host is URL)
+            {
+                dsr = new DynamicStreamingResource(URL(streamingSource.host).host, 
+                                                   streamingSource.streamType);
+            }       
+            
+            if (dsr)
+            {
+                var n:int = streamingSource.streamItems.length;
+                var item:DynamicStreamingVideoItem;
+                var dsi:DynamicStreamingItem;
+                var streamItems:Vector.<DynamicStreamingItem> = new Vector.<DynamicStreamingItem>(n);
+                
+                for (var i:int = 0; i < n; i++)
+                {
+                    item = streamingSource.streamItems[i];
+                    dsi = new DynamicStreamingItem(item.streamName, item.bitrate);
+                    streamItems[i] = dsi;
+                }
+                dsr.streamItems = streamItems;
+                
+                dsr.initialIndex = streamingSource.initialIndex;
+                
+                // add video type metadata so if the URL is ambiguous, OSMF will 
+                // know what type of file we're trying to connect to
+                dsr.mediaType = MediaType.VIDEO;
+                
+                videoElement = new org.osmf.elements.VideoElement(dsr, new RTMPDynamicStreamingNetLoader());
+            }
+        }
+        else if (source is String && source != "")
+        {
+            var urlResource:URLResource = new URLResource(source as String);
+            videoElement = mediaFactory.createMediaElement(urlResource);
+            
+            // If the url could not be resolved to a media element then try 
+            // telling osmf the media is a video and try again.
+            // We do not specify the media type as video the first time,
+            // so we can have the chance to play audio.
+            if (videoElement == null)
+            {
+                urlResource.mediaType = MediaType.VIDEO;
+                videoElement = mediaFactory.createMediaElement(urlResource);                
+            }
+        }
+        else if (source is MediaResourceBase)
+        {
+            videoElement = mediaFactory.createMediaElement(MediaResourceBase(source));
+        }
+        else if (source is org.osmf.elements.VideoElement)
+        {
+            videoElement = source as org.osmf.elements.VideoElement;
+        }
+        
+        // reset the visibilityPausedTheVideo flag
+        playTheVideoOnVisible = true;
+        // set up videoPlayer.autoPlay based on whether this.autoPlay is 
+        // set and whether we are visible and the other typical conditions.
+        changePlayback(false, false);
+        
+        // if we're not going to autoPlay (or couldn't autoPlay because 
+        // we're hidden or for some other reason), but we need to seek 
+        // to the first frame, then we have to do this on our own 
+        // by using our load() method.
+        if (videoElement && (!autoPlay || !shouldBePlaying) && autoDisplayFirstFrame)
+            load();
+        
+        // set videoPlayer's element to the newly constructed VideoElement
+        // set the newly constructed videoElement's gateway to be the videoGateway
+        videoPlayer.media = videoElement;
+        
+        if (videoElement)
+        {
+            // If we are loading a video, make sure the timers are restored in case
+            // they had been disabled. The timers will be disabled again if we are 
+            // only loading the first frame.
+            if (!isNaN(oldCurrentTimeUpdateInterval))
+            {
+                videoPlayer.currentTimeUpdateInterval = oldCurrentTimeUpdateInterval;
+                videoPlayer.bytesLoadedUpdateInterval = oldBytesLoadedUpdateInterval;
+            }
+
+            if (videoElement.getMetadata(LayoutMetadata.LAYOUT_NAMESPACE) == null)
+            {
+                var layout:LayoutMetadata = new LayoutMetadata();
+                layout.scaleMode = scaleMode;
+                layout.verticalAlign = VerticalAlign.MIDDLE;
+                layout.horizontalAlign = HorizontalAlign.CENTER;
+                layout.percentWidth = 100;
+                layout.percentHeight = 100;
+                videoElement.addMetadata(LayoutMetadata.LAYOUT_NAMESPACE, layout);
+            }               
+
+            if (videoElement && !videoContainer.containsMediaElement(videoElement) )
+            {
+                videoContainer.addMediaElement(videoElement);
+            }               
+        }
+        else
+        {
+            // if our source is null, let's invalidateSize() here.
+            // if it's a bad source, we'll get a playbackError and invalidate
+            // the size down there.  If it's a good source, we'll get a 
+            // dimensionChange event and invalidate the size in there.
+            invalidateSize();
+        }
+    }
+    
+    /**
+     *  @private
+     *  Our own internal load() method to handle the case 
+     *  where autoPlay = false and autoDisplayFirstFrame = true 
+     *  so that we can load up the video, figure out its size, 
+     *  and show the first frame
+     */
+    private function load():void
+    {
+        inLoadingState1 = true;
+        
+        // wait until we can mute, play(), pause(), and seek() before doing anything.
+        // We should be able to do all of these operations on the READY state change event.
+        videoPlayer.addEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoPlayer_mediaPlayerStateChangeHandlerForLoading);
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  pauseWhenHidden: Event handlers and Private Methods
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @private
+     *  Whether the video should be playing based on enabled, 
+     *  pauseWhenHidden, whether it's on the display list, and its
+     *  effective visibility.
+     */
+    private function get shouldBePlaying():Boolean
+    {
+        // if disabled, return false
+        if (!effectiveEnabled)
+            return false;
+        
+        // if we want to look at visibility, check to 
+        // see if we are on the display list and check out 
+        // effectiveVisibility (which looks up our parent chain 
+        // to make sure us and all of our ancestors are visible)
+        if (pauseWhenHidden)
+        {
+            if (!_isOnDisplayList)
+                return false;
+            
+            if (!effectiveVisibility)
+                return false;
+        }
+        
+        return true;
+    }
+    
+    /**
+     *  @private
+     *  This method will pause or play the video by looking at the state of 
+     *  the component and determining whether it should play or pause.  This 
+     *  method gets called when an important event occurs, such as 
+     *  the component being added/removed from the stage, the component's 
+     *  effective visibility changing, or when autoPlay is set.  
+     * 
+     *  <p>Only certain events are "action events" which can cause the video 
+     *  to pause or play immediately.  For example, when autoPlay is set to 
+     *  true/false, that shouldn't cause any immediate action, but changePlayback()
+     *  is still called so that autoPlay can be set on the underlying media player.</p>
+     * 
+     *  <p>Actions that can pause the video are:
+     *  <ul>
+     *      <li>Changes in effective enablement</li>
+     *      <li>Changes in effective visibility</li>
+     *      <li>Changes in staging (added or removed from display list)</li>
+     *      <li>Setting pauseWhenHidden = true</li>
+     *  </ul></p>
+     * 
+     *  <p>Actions that can play the video are:
+     *  <ul>
+     *      <li>Changes in effective visibility</li>
+     *      <li>Changes in staging (added or removed from display list)</li>
+     *  </ul></p>
+     * 
+     *  @param causePause Whether this action can cause a currently playing video to pause
+     *  @param causePlay Whether this action can cause a currently paused video to play
+     */
+    private function changePlayback(causePause:Boolean, causePlay:Boolean):void
+    {
+        // if we shouldn't be playing, we pause the video.
+        // if we come back up and should be playing, we will
+        // start playing the video again if the video wasn't paused 
+        // by the user or developer and autoPlay is true.       
+        if (shouldBePlaying)
+        {
+            videoPlayer.autoPlay = autoPlay;
+            
+            // only play the video if visibility caused it to pause 
+            // (instead of a user or developer calling video.pause()).
+            // Also, only play if autoPlay is true.  Otherwise when 
+            // the visibility changes, we won't automatically 
+            // play the video
+            if (causePlay && (playTheVideoOnVisible && autoPlay))
+            {
+                playTheVideoOnVisible = false;
+                
+                // set autoplay and call play() if the 
+                // source has loaded up and it's playable
+                if (inLoadingState1)
+                    cancelLoadAndPlay();
+                else if (videoPlayer.canPlay)
+                    videoPlayer.play();
+            }
+        }
+        else
+        {
+            // there are really three states the video player can 
+            // be in with respect to play vs. paused:
+            // 1) playing
+            // 2) paused
+            // 3) loading
+            // Here we are checking if we are playing or loading
+            // and going to play soon (autoPlay = true)
+            if (causePause && (playing || (videoPlayer.state == MediaPlayerState.LOADING && autoPlay)))
+                playTheVideoOnVisible = true;
+
+            // always set autoPlay to false here and 
+            // if pausable, pause the video
+            videoPlayer.autoPlay = false;
+            if (causePause)
+            {
+                // if we're loading up, then we will pause automatically, so let's 
+                // not interrupt this process
+                // if inLoadingState1 && pausable, then let loading state handle it
+                // if inLoadingState1 && !pausable, then let the loading state handle it
+                // if !inLoadingState1 && pausable, then just pause
+                // if !inLoadingState1 && !pausable, then load (if needed to show first frame)
+                if (!inLoadingState1 && videoPlayer.canPause)
+                    videoPlayer.pause();
+                else if (!videoPlayer.canPause && autoDisplayFirstFrame)
+                    load();
+            }
+        }
+    }
+    
+    /**
+     *  @private
+     *  Cancels the load, no matter what state it's in, and starts to play().
+     */
+    private function cancelLoadAndPlay():void
+    {
+        if (inLoadingState1)
+        {
+            if (!inLoadingState2)
+            {
+                // first step
+                
+                // Don't need to do anything but set inLoadingState1 = false (done down below).
+                // This is handled in videoPlayer_mediaPlayerStateChangeHandlerForLoading which will still 
+                // be fired and will handle calling videoPlayer.play() without the rest of the loading 
+                // junk because inLoadingState1 = false now
+            }
+            else if (!inLoadingState3)
+            {
+                // second step
+                videoPlayer.muted = beforeLoadMuted;
+                
+                if (videoPlayer.displayObject)
+                    videoPlayer.displayObject.visible = true;
+                
+                videoPlayer.removeEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoPlayer_currentTimeChangeHandler);
+                videoPlayer.removeEventListener(MediaPlayerCapabilityChangeEvent.CAN_SEEK_CHANGE, videoPlayer_canSeekChangeHandler);
+            }
+            else
+            {
+                // third step
+                videoPlayer.removeEventListener(SeekEvent.SEEKING_CHANGE, videoPlayer_seekChangeHandler);
+                videoPlayer.muted = beforeLoadMuted;
+                if (videoPlayer.displayObject)
+                    videoPlayer.displayObject.visible = true;
+                
+                // wasn't playing
+                if (videoPlayer.canPlay)
+                    videoPlayer.play();
+            }
+            
+            inLoadingState1 = false;
+            inLoadingState2 = false;
+            inLoadingState3 = false;
+        }
+    }
+    
+    /**
+     *  @private
+     *  Cancels the load, no matter what state it's in.  This is used when changing the source.
+     */
+    private function cleanUpSource():void
+    {
+        // TODO (rfrishbe): very similar to cancelLoadAndPlay(). Should collapse it down.
+        
+        // always remove listener as we could be out of loadState1 but still "loading to play"
+        videoPlayer.removeEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoPlayer_mediaPlayerStateChangeHandlerForLoading);
+        
+        if (inLoadingState1)
+        {
+            if (!inLoadingState2)
+            {
+                // first step
+                
+                // Just need to remove event listeners as we did above
+            }
+            else if (!inLoadingState3)
+            {
+                // second step
+                videoPlayer.muted = beforeLoadMuted;
+                videoPlayer.displayObject.visible = true;
+                
+                // going to call pause() now to stop immediately
+                videoPlayer.pause();
+            }
+            else
+            {
+                // third step
+                videoPlayer.removeEventListener(SeekEvent.SEEKING_CHANGE, videoPlayer_seekChangeHandler);
+                videoPlayer.muted = beforeLoadMuted;
+                videoPlayer.displayObject.visible = true;
+                
+                // already called pause(), so don't do anything
+            }
+            
+            inLoadingState1 = false;
+            inLoadingState2 = false;
+            inLoadingState3 = false;
+        }
+    }
+    
+    /**
+     *  @private
+     */
+    private function addedToStageHandler(event:Event):void
+    {
+        _isOnDisplayList = true;
+        
+        // add listeners to current parents to see if their visibility has changed
+        if (pauseWhenHidden)
+            addVisibilityListeners();
+        
+        addEnabledListeners();
+        
+        computeEffectiveVisibilityAndEnabled();
+
+        // When added to the stage, restore some videoPlayer timers that we had
+        // disabled when we went offstage.
+        if (!isNaN(oldCurrentTimeUpdateInterval))
+        {
+            videoPlayer.currentTimeUpdateInterval = oldCurrentTimeUpdateInterval;
+            videoPlayer.bytesLoadedUpdateInterval = oldBytesLoadedUpdateInterval;
+
+            oldCurrentTimeUpdateInterval = NaN;
+            oldBytesLoadedUpdateInterval = NaN;
+        }
+        
+        // being added to the display list will not pause the video, but 
+        // it may play the video if pauseWhenHidden = true
+        changePlayback(false, pauseWhenHidden);
+    }
+    
+    /**
+     *  @private
+     */
+    private function removedFromStageHandler(event:Event):void
+    {
+        _isOnDisplayList = false;
+        
+        // remove listeners from old parents
+        if (pauseWhenHidden)
+            removeVisibilityListeners();
+        
+        removeEnabledListeners();
+        
+        // Stop the timers associated with these intervals when we go 
+        // offscreen so we are not pinned in memory. Save the old
+        // values of the timers so we can restore them when we come
+        // back on stage.
+        if (pauseWhenHidden)
+        {
+            oldCurrentTimeUpdateInterval = videoPlayer.currentTimeUpdateInterval;
+            oldBytesLoadedUpdateInterval = videoPlayer.bytesLoadedUpdateInterval;
+            videoPlayer.currentTimeUpdateInterval = -1;
+            videoPlayer.bytesLoadedUpdateInterval = -1;
+        }
+        
+        // being removed from the display list will pause the video if 
+        // pauseWhenHidden = true
+        changePlayback(pauseWhenHidden, false);
+    }
+    
+    /**
+     *  @private
+     *  Add event listeners for SHOW and HIDE on all the ancestors up the parent chain.
+     *  Adding weak event listeners just to be safe.
+     */
+    private function addVisibilityListeners():void
+    {
+        var current:IVisualElement = this;
+        while (current)
+        {
+            // add visibility listeners to the parent
+            current.addEventListener(FlexEvent.HIDE, visibilityChangedHandler, false, 0, true);
+            current.addEventListener(FlexEvent.SHOW, visibilityChangedHandler, false, 0, true);
+            
+            // add listeners to the design layer too
+            if (current.designLayer)
+            {
+                current.designLayer.addEventListener("layerPropertyChange", 
+                    designLayer_layerPropertyChangeHandler, false, 0, true);
+            }
+            
+            current = current.parent as IVisualElement;
+        }
+    }
+    
+    /**
+     *  @private
+     *  Add event listeners for "enabledChanged" event on all ancestors up the parent chain.
+     *  Adding weak event listeners just to be safe.
+     */
+    private function addEnabledListeners():void
+    {
+        var current:IVisualElement = this;
+        while (current)
+        {
+            current.addEventListener("enabledChanged", enabledChangedHandler, false, 0, true);
+            current.addEventListener("enabledChanged", enabledChangedHandler, false, 0, true);
+            
+            current = current.parent as IVisualElement;
+        }
+    }
+    
+    /**
+     *  @private
+     *  Remove event listeners for SHOW and HIDE on all the ancestors up the parent chain.
+     */
+    private function removeVisibilityListeners():void
+    {
+        var current:IVisualElement = this;
+        while (current)
+        {
+            current.removeEventListener(FlexEvent.HIDE, visibilityChangedHandler, false);
+            current.removeEventListener(FlexEvent.SHOW, visibilityChangedHandler, false);
+            
+            if (current.designLayer)
+            {
+                current.designLayer.removeEventListener("layerPropertyChange", 
+                    designLayer_layerPropertyChangeHandler, false);
+            }
+            
+            current = current.parent as IVisualElement;
+        }
+    }
+    
+    /**
+     *  @private
+     *  Remove event listeners for "enabledChanged" event on all ancestors up the parent chain.
+     */
+    private function removeEnabledListeners():void
+    {
+        var current:IVisualElement = this;
+        while (current)
+        {
+            current.removeEventListener("enabledChanged", enabledChangedHandler, false);
+            current.removeEventListener("enabledChanged", enabledChangedHandler, false);
+            
+            current = current.parent as IVisualElement;
+        }
+    }
+    
+    /**
+     *  @private
+     *  Event call back whenever the visibility of us or one of our ancestors 
+     *  changes
+     */
+    private function visibilityChangedHandler(event:FlexEvent):void
+    {
+        effectiveVisibilityChanged = true;
+        invalidateProperties();
+    }
+    
+    /**
+     *  @private
+     *  Event call back whenever the visibility of our designLayer or one of our parent's
+     *  designLayers change.
+     */
+    private function designLayer_layerPropertyChangeHandler(event:PropertyChangeEvent):void
+    {
+        if (event.property == "effectiveVisibility")
+        {
+            effectiveVisibilityChanged = true;
+            invalidateProperties();
+        }
+    }
+    
+    /**
+     *  @private
+     *  Event call back whenever the enablement of us or one of our ancestors 
+     *  changes
+     */
+    private function enabledChangedHandler(event:Event):void
+    {
+        effectiveEnabledChanged = true;
+        invalidateProperties();
+    }
+    
+    /**
+     *  @private
+     */
+    private function computeEffectiveVisibilityAndEnabled():void
+    {
+        // start out with true visibility and enablement
+        // then loop up parent-chain to see if any of them are false
+        effectiveVisibility = true;
+        effectiveEnabled = true;
+        var current:IVisualElement = this;
+        
+        while (current)
+        {
+            if (!current.visible || 
+                (current.designLayer && !current.designLayer.effectiveVisibility))
+            {
+                effectiveVisibility = false;
+                if (!effectiveEnabled)
+                    break;
+            }
+            
+            if (current is IUIComponent && !IUIComponent(current).enabled)
+            {
+                effectiveEnabled = false;
+                if (!effectiveVisibility)
+                    break;
+            }
+            
+            current = current.parent as IVisualElement;
+        }
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Event handlers
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @private
+     */
+    private function videoPlayer_volumeChangeHandler(event:AudioEvent):void
+    {
+        dispatchEvent(new Event("volumeChanged"));
+    }
+    
+    /**
+     *  @private
+     */
+    private function videoPlayer_mutedChangeHandler(event:AudioEvent):void
+    {
+        dispatchEvent(new Event("volumeChanged"));
+    }
+    
+    /**
+     *  @private
+     *  Event handler for mediaPlayerStateChange event.
+     */
+    private function videoPlayer_mediaPlayerStateChangeHandler(event:MediaPlayerStateChangeEvent):void
+    {
+        // if the event change caused us to go in to a state where 
+        // nothing is loaded up and we've no chance of getting a 
+        // dimensionChangeEvent, then let's invalidate our size here
+        if (event.state == MediaPlayerState.PLAYBACK_ERROR)
+            invalidateSize();
+        
+        // this is a public event, so let's re-dispatch it
+        dispatchEvent(event);
+    }
+    
+    /**
+     *  @private
+     *  Event handler for mediaPlayerStateChange event--used only  
+     *  when trying to load up the video without playing it.
+     */
+    private function videoPlayer_mediaPlayerStateChangeHandlerForLoading(event:MediaPlayerStateChangeEvent):void
+    {
+        // only come in here when we want to load the video without playing it.
+        //trace("videoPlayer_mediaPlayerStateChangeHandlerForLoading: mediaPlayerState = " + event.state);
+        
+        // wait until we are ready so that we can set mute, play, pause, and seek
+        if (event.state == MediaPlayerState.READY)
+        {
+            // now that we are loading up, let's remove the event listener:
+            videoPlayer.removeEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, videoPlayer_mediaPlayerStateChangeHandlerForLoading);
+            
+            // if we are already playing() for some reason because someone called play(), then
+            // we don't need to do anything.
+            if (videoPlayer.playing)
+                return;
+            
+            // if this load wasn't cancelled, then we'll do the load stuff.
+            // otherwise, we'll just cause play().
+            if (inLoadingState1)
+            {
+                //trace("videoPlayer_mediaPlayerStateChangeHandlerForLoading: inLoadingState1");
+
+                beforeLoadMuted = videoPlayer.muted;
+                videoPlayer.muted = true;
+                
+                if (videoPlayer.displayObject)
+                    videoPlayer.displayObject.visible = false;
+                
+                inLoadingState2 = true;
+            }
+            
+            // call play(), here, then wait to call pause() and seek(0) in the 
+            // mediaSizeChangeHandler
+            //trace("videoPlayer_mediaPlayerStateChangeHandlerForLoading: call videoPlayer.play()");
+            videoPlayer.play();
+        }
+    }
+    
+    /**
+     *  @private
+     */
+    private function videoPlayer_mediaSizeChangeHandler(event:DisplayObjectEvent):void
+    {
+        //trace("videoPlayer_mediaSizeChangeHandler");
+        invalidateSize();
+        
+        // if we're loading up the video, then let's finish the load in here
+        if (inLoadingState2)
+        {
+            //trace("videoPlayer_mediaSizeChangeHandler: inLoadingState2");
+
+            if (videoPlayer.canSeek && videoPlayer.canSeekTo(0))
+            {
+                //trace("videoPlayer_mediaSizeChangeHandler: canSeek to first frame");
+                inLoadingState3 = true;
+                
+                // Don't call pause and seek inside this handler because OSMF is 
+                // not expecting us to change its HTTPStreamingState value in 
+                // HTTPNetStream.onMainTimer as a result of dispatching this 
+                // event (see SDK-27028).
+                callLater(pauseAndSeekCallBack);
+            }
+            else if (duration < 0)
+            {
+                // Work around for negative durations - FM-1009
+                // We want to seek to the first frame but we can't because the
+                // duration of the video is reported as negative. As a work around,
+                // listen for the first time change event and then pause the video.
+                //trace("videoPlayer_mediaSizeChangeHandler: negative duration - wait for first current time change event");
+                videoPlayer.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoPlayer_currentTimeChangeHandler);
+            }
+            else
+            {
+                //trace("videoPlayer_mediaSizeChangeHandler: waiting for media to become seekable");
+                
+                // wait for the media to become seekable.
+                videoPlayer.addEventListener(MediaPlayerCapabilityChangeEvent.CAN_SEEK_CHANGE, videoPlayer_canSeekChangeHandler);
+            }
+        }
+    }
+
+    private function pauseAndSeekCallBack():void
+    {
+        // the seek(0) is asynchronous so let's add an event listener to see when it's finsished:
+        videoPlayer.addEventListener(SeekEvent.SEEKING_CHANGE, videoPlayer_seekChangeHandler);
+        
+        // called play(), now call pause() and seek(0);
+        videoPlayer.pause();
+        videoPlayer.seek(0);
+                
+    }
+    
+    /**
+     *  @private
+     *  Wait until the media is seekable before we call pause() and seek().
+     */  
+    private function videoPlayer_canSeekChangeHandler(event:Event):void
+    {
+        //trace("videoPlayer_canSeekChangeHandler: seeking = " + videoPlayer.canSeek);
+        
+        videoPlayer.removeEventListener(MediaPlayerCapabilityChangeEvent.CAN_SEEK_CHANGE, videoPlayer_canSeekChangeHandler);
+
+        if (inLoadingState2)
+        {
+            if (videoPlayer.canSeek && videoPlayer.canSeekTo(0))
+            {
+                inLoadingState3 = true;
+                
+                // Don't call pause and seek inside this handler because OSMF is 
+                // not expecting us to change its HTTPStreamingState value in 
+                // HTTPNetStream.onMainTimer as a result of dispatching this 
+                // event (see SDK-27028).
+                callLater(pauseAndSeekCallBack);
+            }
+        }
+    }
+    
+    /**
+     *  @private
+     *  Event handler for seekEnd events.  We only use this 
+     *  when trying to load up the video without playing it.
+     *  This will be called after the video has loaded up and 
+     *  we have finished seeking back to the first frame.
+     */
+    private function videoPlayer_seekChangeHandler(event:SeekEvent):void
+    {
+        if (!event.seeking)
+        {
+            inLoadingState1 = false;
+            inLoadingState2 = false;
+            inLoadingState3 = false;
+            
+            videoPlayer.removeEventListener(SeekEvent.SEEKING_CHANGE, videoPlayer_seekChangeHandler);
+            videoPlayer.muted = beforeLoadMuted;
+            if (videoPlayer.displayObject)
+                videoPlayer.displayObject.visible = true;
+
+            // Disable the TimeEvents again that we had
+            // enabled for loading a video while offstage.
+            if (!isNaN(oldCurrentTimeUpdateInterval))
+            {
+                videoPlayer.currentTimeUpdateInterval = -1;
+                videoPlayer.bytesLoadedUpdateInterval = -1;
+            }
+        }
+    }
+    
+    
+    /**
+     *  @private
+     *
+     *  Work around for negative durations - see FM-1009.
+     *  See want to seek to the first frame but can't because
+     *  the video has a negative duration. So we listen to the
+     *  current time. When we get a time change so we must be at
+     *  least the first frame so pause the video now and clean 
+     *  up the load state variables.
+     */ 
+    private function videoPlayer_currentTimeChangeHandler(event:TimeEvent):void
+    {
+        //trace("videoPlayer_currentTimeChangeHandler: time = " + event.time);
+        
+        videoPlayer.removeEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoPlayer_currentTimeChangeHandler);
+        videoPlayer.removeEventListener(MediaPlayerCapabilityChangeEvent.CAN_SEEK_CHANGE, videoPlayer_canSeekChangeHandler);
+
+        videoPlayer.pause();
+        videoPlayer.muted = beforeLoadMuted;
+        
+        if (videoPlayer.displayObject)
+            videoPlayer.displayObject.visible = true;
+
+        inLoadingState1 = false;
+        inLoadingState2 = false;
+        inLoadingState3 = false;
+
+        // Disable the TimeEvents again that we had
+        // enabled for loading a video while offstage.
+        if (!isNaN(oldCurrentTimeUpdateInterval))
+        {
+            videoPlayer.currentTimeUpdateInterval = -1;
+            videoPlayer.bytesLoadedUpdateInterval = -1;
+        }
+    }
+
+    /**
+     *  @private
+     * 
+     *  Work around for negative durations - see FM-1009.
+     * 
+     *  If we get a duration event that is negative while in 
+     *  inLoadingState2 is true, then listen for the first time
+     *  change event so we can pause the video.
+     */ 
+    private function videoPlayer_durationChangeHandler(event:TimeEvent):void
+    {
+        //trace("videoPlayer_durationChangeHandler: time = " + event.time);
+        dispatchEvent(event);
+        
+        if (inLoadingState2)
+        {
+            if (event.time < 0)
+            {
+                // Work around for negative durations - FM-1009
+                // We want to seek to the first frame but we can't because the
+                // duration of the video is reported as negative. As a work around,
+                // listen for the first time change event and then pause the video.
+                //trace("videoPlayer_durationChangeHandler: negative duration - wait for first current time change event");
+                videoPlayer.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, videoPlayer_currentTimeChangeHandler);
+            }
+        }
+    }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/components/mediaClasses/ScrubBar.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/mediaClasses/ScrubBar.as
new file mode 100644
index 0000000..2883fe7
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/mediaClasses/ScrubBar.as
@@ -0,0 +1,273 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.components.mediaClasses
+{
+
+import flash.display.DisplayObject;
+import flash.display.DisplayObjectContainer;
+import flash.display.InteractiveObject;
+import flash.events.Event;
+
+import mx.core.IVisualElement;
+
+import spark.components.HSlider;
+
+/**
+ *  The VideoScrubBar class defines a video timeline that shows the
+ *  current play head location in the video, the amount of the video previously played, 
+ *  and the loaded in part of the video.  
+ *  The timeline appears at the bottom of the VideoPlayer control.
+ *
+ *  @see spark.components.VideoPlayer 
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */  
+public class ScrubBar extends HSlider
+{
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Constructor. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function ScrubBar()
+    {
+        super();
+        
+        dataTipFormatFunction = formatTimeValue;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Skin Parts
+    //
+    //--------------------------------------------------------------------------
+    
+    [SkinPart(required="false")]
+    
+    /**
+     *  An optional skin part for the area on the track 
+     *  representing the video that's been played.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var playedArea:IVisualElement;
+    
+    [SkinPart(required="false")]
+    
+    /**
+     *  An optional skin part for the area on the track 
+     *  representing the currently loaded in part of the video.
+     * 
+     *  <p>For a progressive download video, this will correspond 
+     *  to the number of bytes downloaded.  For a streaming video, 
+     *  the whole video is "loaded in" as it's quick to seek to 
+     *  any spot in the video.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var loadedRangeArea:IVisualElement;
+    
+    //--------------------------------------------------------------------------
+    //
+    // Properties
+    //
+    //--------------------------------------------------------------------------
+    
+    //--------------------------------- 
+    // loadedRangeEnd
+    //---------------------------------
+    
+    private var _loadedRangeEnd:Number;
+    
+    /**
+     *  The range of currently loaded in values.  This 
+     *  property corresponds to the end of that range.
+     * 
+     *  <p>For a progressive download video, this will correspond 
+     *  to the number of bytes downloaded.  For a streaming video, 
+     *  the whole video is "loaded in" as it's quick to seek to 
+     *  any spot in the video.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get loadedRangeEnd():Number
+    {
+        return _loadedRangeEnd;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set loadedRangeEnd(value:Number):void
+    {
+        if (value == _loadedRangeEnd)
+            return;
+        
+        _loadedRangeEnd = value;
+        invalidateDisplayList();
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    // Methods
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @private
+     */
+    override protected function partAdded(partName:String, instance:Object):void
+    {
+        super.partAdded(partName, instance);
+        
+        if (instance == playedArea)
+        {
+            if (playedArea is InteractiveObject)
+                InteractiveObject(playedArea).mouseEnabled = false;
+            if (playedArea is DisplayObjectContainer)
+                DisplayObjectContainer(playedArea).mouseChildren = false;
+            
+            invalidateDisplayList();
+        }
+        else if (instance == loadedRangeArea)
+        {
+            if (loadedRangeArea is InteractiveObject)
+                InteractiveObject(loadedRangeArea).mouseEnabled = false;
+            if (loadedRangeArea is DisplayObjectContainer)
+                DisplayObjectContainer(loadedRangeArea).mouseChildren = false;
+            
+            invalidateDisplayList();
+        }
+    }
+    
+    /**
+     *  @private
+     */
+    private function calculateAreaSize(value:Number):Number
+    {
+        var trackPos:Number = track.getLayoutBoundsX();
+        var trackSize:Number = track.getLayoutBoundsWidth();
+        var thumbSize:Number = thumb.getLayoutBoundsWidth();
+        var range:Number = maximum - minimum;
+        var thumbPos:Number = (range > 0) ? (value - minimum) * ((trackSize - thumbSize) / range) : 0;
+        return thumbSize + thumbPos;
+    }
+    
+    /**
+     *  @private
+     */
+    override protected function updateSkinDisplayList():void
+    {
+        super.updateSkinDisplayList();
+        
+        if (!thumb || !track)
+            return;
+        
+        sizeLoadedRangeArea(calculateAreaSize(loadedRangeEnd));
+        sizePlayedArea(calculateAreaSize(pendingValue));
+    }
+    
+    /**
+     *  Sets the size of the loaded range area.
+     *  The loaded range area defines the area on the timeline that represents 
+     *  the currently loaded portion of the video.
+     *
+     *  @param loadedRangeAreaSize The new size of the loaded in range area.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function sizeLoadedRangeArea(loadedRangeAreaSize:Number):void
+    {
+        if (loadedRangeArea)
+            loadedRangeArea.setLayoutBoundsSize(Math.round(loadedRangeAreaSize), NaN);
+    }
+    
+    /**
+     *  Sets the size of the played area.
+     *  The played area defines the area on the timeline that represents 
+     *  the part of the video that has been played. 
+     *
+     *  @param playedAreaSize The new size of the played area.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function sizePlayedArea(playedAreaSize:Number):void
+    {
+        if (playedArea)
+            playedArea.setLayoutBoundsSize(Math.round(playedAreaSize), NaN);            
+    }
+    
+    /**
+     *  @private
+     */
+    private function formatTimeValue(value:Number):String
+    {
+        // default format: hours:minutes:seconds
+        var hours:uint = Math.floor(value/3600) % 24;
+        var minutes:uint = Math.floor(value/60) % 60;
+        var seconds:uint = Math.round(value) % 60;
+        
+        var result:String = "";
+        if (hours != 0)
+            result = hours + ":";
+        
+        if (result && minutes < 10)
+            result += "0" + minutes + ":";
+        else
+            result += minutes + ":";
+        
+        if (seconds < 10)
+            result += "0" + seconds;
+        else
+            result += seconds;
+        
+        return result;
+    }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/components/mediaClasses/VolumeBar.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/mediaClasses/VolumeBar.as
new file mode 100644
index 0000000..4b7bf09
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/mediaClasses/VolumeBar.as
@@ -0,0 +1,573 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.components.mediaClasses
+{
+
+import flash.display.DisplayObject;
+import flash.events.Event;
+import flash.events.FocusEvent;
+import flash.events.KeyboardEvent;
+import flash.events.MouseEvent;
+import flash.geom.Point;
+import flash.ui.Keyboard;
+import flash.ui.Mouse;
+
+import mx.collections.IList;
+import mx.core.IUIComponent;
+import mx.core.UIComponent;
+import mx.core.mx_internal;
+import mx.events.CollectionEvent;
+import mx.events.FlexEvent;
+import mx.managers.LayoutManager;
+
+import spark.components.VSlider;
+import spark.components.supportClasses.ButtonBase;
+import spark.components.supportClasses.DropDownController;
+import spark.components.supportClasses.ListBase;
+import spark.events.DropDownEvent;
+import spark.utils.LabelUtil;
+
+use namespace mx_internal;
+
+//--------------------------------------
+//  Events
+//--------------------------------------
+
+/**
+ *  Dispatched when the volume drop-down slider is dismissed for any reason, 
+ *  such as when the user:
+ *  <ul>
+ *      <li>Selects an item in the drop-down slider</li>
+ *      <li>Clicks outside of the drop-down slider</li>
+ *  </ul>
+ *
+ *  @eventType spark.events.DropDownEvent.CLOSE
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+[Event(name="close", type="spark.events.DropDownEvent")]
+
+/**
+ *  Dispatched when the user mouses over the drop-down slider 
+ *  to display it. It is also dispatched if the user
+ *  uses the keyboard and types Ctrl-Down to open the drop-down slider.
+ *
+ *  @eventType spark.events.DropDownEvent.OPEN
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+[Event(name="open", type="spark.events.DropDownEvent")]
+
+/**
+ *  Dispatched when the user mutes or unmutes the volume.
+ *
+ *  @eventType mx.events.FlexEvent.MUTED_CHANGE
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+[Event(name="mutedChange", type="mx.events.FlexEvent")]
+
+//--------------------------------------
+//  Styles
+//--------------------------------------
+    
+/**
+ *  The delay, in milliseconds, before opening the volume slider 
+ *  after the user moves the mouse over the volume icon in 
+ *  the VideoDisplay control.
+ *  
+ *  @default 200
+ * 
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+[Style(name="rollOverOpenDelay", type="Number", inherit="no")]
+
+//--------------------------------------
+//  SkinStates
+//--------------------------------------
+
+/**
+ *  Open state of the drop-down slider.
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+[SkinState("open")]
+
+/**
+ *  The VolumeBar class defines a drop-down slider to control 
+ *  the volume of the VideoDisplay control.  
+ *  By default, the drop-down slider opens when the user moves the mouse
+ *  over the volume icon of the VideoDisplay control. 
+ *  The <code>rollOverOpenDelay</code> style defines a delay of 200 milliseconds 
+ *  before opening the drop-down slider.  
+ *
+ *  @see spark.components.VideoDisplay
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class VolumeBar extends VSlider
+{
+
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  Constructor. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function VolumeBar()
+    {
+        super();
+        
+        dropDownController = new DropDownController();
+        
+        // add change listener so we know when the user has interacted 
+        // with the volume bar to change the value so we can automatically 
+        // unmute the volume when the user does that.
+        addEventListener(Event.CHANGE, changeHandler);
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Skin Parts
+    //
+    //--------------------------------------------------------------------------    
+    
+    /**
+     *  A skin part that defines the mute/unmute button.  
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    [SkinPart(required="false")]
+    public var muteButton:MuteButton;
+    
+    
+    /**
+     *  A skin part that defines the drop-down area of the volume drop-down slider. 
+     *  When the volume slider drop down is open, 
+     *  clicking anywhere outside of the <code>dropDown</code> skin part
+     *  closes the drop-down slider. 
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    [SkinPart(required="false")]
+    public var dropDown:DisplayObject;
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+    
+    //----------------------------------
+    //  dropDownController
+    //----------------------------------
+    
+    private var _dropDownController:DropDownController;    
+    
+    /**
+     *  Instance of the DropDownController class that handles all of the mouse, keyboard 
+     *  and focus user interactions.  
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    protected function get dropDownController():DropDownController
+    {
+        return _dropDownController;
+    }
+    
+    /**
+     *  @private
+     */
+    protected function set dropDownController(value:DropDownController):void
+    {
+        if (_dropDownController == value)
+            return;
+            
+        _dropDownController = value;
+            
+        _dropDownController.addEventListener(DropDownEvent.OPEN, dropDownController_openHandler);
+        _dropDownController.addEventListener(DropDownEvent.CLOSE, dropDownController_closeHandler);
+            
+        _dropDownController.rollOverOpenDelay = getStyle("rollOverOpenDelay");
+            
+        if (muteButton)
+            _dropDownController.openButton = muteButton;
+        if (dropDown)
+            _dropDownController.dropDown = dropDown;    
+    }
+    
+    //----------------------------------
+    //  isDropDownOpen
+    //----------------------------------
+    
+    /**
+     *  @copy spark.components.supportClasses.DropDownController#isOpen
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get isDropDownOpen():Boolean
+    {
+        if (dropDownController)
+            return dropDownController.isOpen;
+        else
+            return false;
+    }
+    
+    //----------------------------------
+    //  muted
+    //----------------------------------
+    
+    /**
+     *  @private
+     */
+    private var _muted:Boolean = false;
+    
+    [Bindable("mutedChange")]
+    
+    /**
+     *  Contains <code>true</code> if the volume of the video is muted, 
+     *  and <code>false</code> if not.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get muted():Boolean
+    {
+        return _muted;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set muted(value:Boolean):void
+    {
+        if (_muted == value)
+            return;
+        
+        _muted = value;
+        
+        // invalidateDisplayList() because we take in to account value and muted when 
+        // determining where to draw the thumb on the track.
+        invalidateDisplayList();
+        
+        if (muteButton)
+            muteButton.muted = value;
+        
+        dispatchEvent(new FlexEvent(FlexEvent.MUTED_CHANGE));
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden Properties
+    //
+    //--------------------------------------------------------------------------
+    
+    //----------------------------------
+    //  baselinePosition
+    //----------------------------------
+    
+    /**
+     *  @private
+     */
+    override public function get baselinePosition():Number
+    {
+        return getBaselinePositionForPart(muteButton);
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden Methods
+    //
+    //--------------------------------------------------------------------------   
+    
+    /**
+     *  @private
+     *  Overridden to handle the muted case where the value's not actually changed, 
+     *  but we want it to show up as 0.
+     */
+    override protected function updateSkinDisplayList():void
+    {
+        if (!thumb || !track)
+            return;
+    
+        var thumbRange:Number = track.getLayoutBoundsHeight() - thumb.getLayoutBoundsHeight();
+        var range:Number = maximum - minimum;
+        
+        // calculate new thumb position.
+        var thumbPosTrackY:Number;
+        
+        // if muted, it's 0.  otherwise, calculate it normally
+        // TODO (rfrishbe): should probably use setValue(0) and listen for CHANGE on the VideoPlayer 
+        // instead of VALUE_COMMIT.
+        if (!muted)
+            thumbPosTrackY = (range > 0) ? thumbRange - (((pendingValue - minimum) / range) * thumbRange) : 0;
+        else
+            thumbPosTrackY = thumbRange;
+        
+        // convert to parent's coordinates.
+        var thumbPos:Point = track.localToGlobal(new Point(0, thumbPosTrackY));
+        var thumbPosParentY:Number = thumb.parent.globalToLocal(thumbPos).y;
+        
+        thumb.setLayoutBoundsPosition(thumb.getLayoutBoundsX(), Math.round(thumbPosParentY));
+    }
+    
+    /**
+     *  @private
+     */
+    override public function styleChanged(styleProp:String):void
+    {
+        super.styleChanged(styleProp);
+        var allStyles:Boolean = (styleProp == null || styleProp == "styleName");
+         
+        if (allStyles || styleProp == "rollOverOpenDelay")
+        {
+            if (dropDownController)
+                dropDownController.rollOverOpenDelay = getStyle("rollOverOpenDelay");
+        }
+    }
+     
+         
+    /**
+     *  @private
+     */
+    override protected function setValue(value:Number):void
+    {
+        super.setValue(value);
+        
+        if (muteButton)
+            muteButton.volume = value;
+    }
+    
+        /**
+      *  @private
+      */ 
+    override protected function getCurrentSkinState():String
+    {
+        return !enabled ? "disabled" : dropDownController.isOpen ? "open" : "normal";
+    }   
+       
+    /**
+     *  @private
+     */ 
+    override protected function partAdded(partName:String, instance:Object):void
+    {
+        super.partAdded(partName, instance);
+ 
+        if (instance == muteButton)
+        {
+            if (dropDownController)
+                dropDownController.openButton = muteButton;
+            
+            muteButton.addEventListener(FlexEvent.MUTED_CHANGE, muteButton_mutedChangeHandler);
+            muteButton.volume = value;
+            muteButton.muted = muted;
+        }
+        else if (instance == dropDown && dropDownController)
+        {
+            dropDownController.dropDown = dropDown;
+        }
+    }
+    
+    /**
+     *  @private
+     */
+    override protected function partRemoved(partName:String, instance:Object):void
+    {
+        if (instance == muteButton)
+        {
+            muteButton.removeEventListener(FlexEvent.MUTED_CHANGE, muteButton_mutedChangeHandler);
+        }
+        else if (instance == dropDownController)
+        {
+            if (instance == muteButton)
+                dropDownController.openButton = null;
+        
+            if (instance == dropDown)
+                dropDownController.dropDown = null;
+        }
+         
+        super.partRemoved(partName, instance);
+    }
+    
+    /**
+     *  @private
+     *  On focus, pop open the drop down and validate everything so 
+     *  we can draw focus on one of the drop-down parts (the thumb)
+     */
+    override public function setFocus():void
+    {
+        openDropDown();
+        LayoutManager.getInstance().validateNow();
+        super.setFocus();
+    }
+    
+    /**
+     *  @private
+     */
+    override protected function focusOutHandler(event:FocusEvent):void
+    {
+        dropDownController.processFocusOut(event);
+
+        super.focusOutHandler(event);
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Methods
+    //
+    //--------------------------------------------------------------------------   
+
+    /**
+     *  Opens the drop-down slider. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */ 
+    public function openDropDown():void
+    {
+        dropDownController.openDropDown();
+    }
+    
+     /**
+     *  Closes the drop-down slider. 
+     * 
+     *  @param commit Set to <code>true</code> if the component should commit the value
+     *  from the drop-down slider. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function closeDropDown(commit:Boolean):void
+    {
+        dropDownController.closeDropDown(commit);
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Event handling
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  Event handler for the <code>dropDownController</code> 
+     *  <code>DropDownEvent.OPEN</code> event. Updates the skin's state and 
+     *  ensures that the selectedItem is visible. 
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    mx_internal function dropDownController_openHandler(event:DropDownEvent):void
+    {
+        invalidateSkinState();
+        
+        dispatchEvent(event);
+    }
+    
+    /**
+     *  Event handler for the <code>dropDownController</code> 
+     *  <code>DropDownEvent.CLOSE</code> event. Updates the skin's state.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    mx_internal function dropDownController_closeHandler(event:DropDownEvent):void
+    {
+        invalidateSkinState();
+        
+        // In this implementation, the volume is always changed immediately, 
+        // so no need to handle the case when 
+        // commit==false and event.preventDefault() is called on this DropDownEvent
+        
+        dispatchEvent(event);
+    }
+    
+    /**
+     *  @private
+     *  When the value is changed via a user-interaction, we will 
+     *  automatically unmute the volume
+     */
+    private function changeHandler(event:Event):void
+    {
+        // when the value is set, this volume bar unmutes the 
+        // video player automatically
+        if (muted)
+            muted = false;
+    }
+    
+    /**
+     *  @private
+     *  When the mute button changes the muted value, we need to change 
+     *  our own.
+     */
+    private function muteButton_mutedChangeHandler(event:FlexEvent):void
+    {
+        muted = muteButton.muted;
+    }
+
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/components/supportClasses/ButtonBarHorizontalLayout.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/supportClasses/ButtonBarHorizontalLayout.as
new file mode 100644
index 0000000..a537d05
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/components/supportClasses/ButtonBarHorizontalLayout.as
@@ -0,0 +1,285 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.components.supportClasses
+{
+
+import spark.components.supportClasses.GroupBase;
+import mx.core.ILayoutElement;
+import spark.layouts.supportClasses.LayoutBase;
+
+/**
+ *  The ButtonBarHorizontalLayout class is a layout specifically designed for the
+ *  Spark ButtonBar skins.
+ *  The layout lays out the children horizontally, left to right.
+ *  
+ *  <p>The layout attempts to size all children to their preferred size.
+ *  If there is enough space, each child is set to its preferred size, plus any
+ *  excess space evenly distributed among the children.</p>
+ * 
+ *  <p>If there is not enough space for all the children to be sized to their
+ *  preferred size, then the children that are smaller than the average width
+ *  are allocated their preferred size and the rest of the elements are
+ *  reduced equally.</p>
+ * 
+ *  <p>All children are set to the height of the parent.</p>
+ * 
+ *  @see spark.skins.spark.ButtonBarSkin
+ *
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class ButtonBarHorizontalLayout extends LayoutBase
+{
+    include "../../core/Version.as";
+
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Constructor.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function ButtonBarHorizontalLayout():void
+    {
+        super();
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+
+    //----------------------------------
+    //  gap
+    //----------------------------------
+
+    private var _gap:int = 0;
+
+    [Inspectable(category="General")]
+
+    /**
+     *  The horizontal space between layout elements.
+     * 
+     *  Note that the gap is only applied between layout elements, so if there's
+     *  just one element, the gap has no effect on the layout.
+     * 
+     *  @default 0
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */    
+    public function get gap():int
+    {
+        return _gap;
+    }
+
+    /**
+     *  @private
+     */
+    public function set gap(value:int):void
+    {
+        if (_gap == value) 
+            return;
+    
+        _gap = value;
+
+        var g:GroupBase = target;
+        if (g)
+        {
+            g.invalidateSize();
+            g.invalidateDisplayList();
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private 
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override public function measure():void
+    {
+        super.measure();
+        
+        var layoutTarget:GroupBase = target;
+        if (!layoutTarget)
+            return;
+
+        var elementCount:int = 0;
+        var gap:Number = this.gap;
+
+        var width:Number = 0;
+        var height:Number = 0;
+
+        var count:int = layoutTarget.numElements;
+        for (var i:int = 0; i < count; i++)
+        {
+            var layoutElement:ILayoutElement = layoutTarget.getElementAt(i);
+            if (!layoutElement || !layoutElement.includeInLayout)
+                continue;
+
+            width += layoutElement.getPreferredBoundsWidth();
+            elementCount++;
+            height = Math.max(height, layoutElement.getPreferredBoundsHeight());
+
+        }
+
+        if (elementCount > 1)
+            width += gap * (elementCount - 1);
+
+        layoutTarget.measuredWidth = width;
+        layoutTarget.measuredHeight = height;
+    }
+
+    /**
+     *  @private 
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override public function updateDisplayList(width:Number, height:Number):void
+    {
+        var gap:Number = this.gap;
+        super.updateDisplayList(width, height);
+        
+        var layoutTarget:GroupBase = target;
+        if (!layoutTarget)
+            return;
+
+        // Pass one: calculate the excess space
+        var totalPreferredWidth:Number = 0;            
+        var count:int = layoutTarget.numElements;
+        var elementCount:int = count;
+        var layoutElement:ILayoutElement;
+        for (var i:int = 0; i < count; i++)
+        {
+            layoutElement = layoutTarget.getElementAt(i);
+            if (!layoutElement || !layoutElement.includeInLayout)
+            {
+                elementCount--;
+                continue;
+            }
+            totalPreferredWidth += layoutElement.getPreferredBoundsWidth();
+        }
+        
+        // Special case for no elements
+        if (elementCount == 0)
+        {
+            layoutTarget.setContentSize(0, 0);
+            return;
+        }
+        
+        // The content size is always the parent size
+        layoutTarget.setContentSize(width, height);
+
+        // Special case: if width is zero, make the gap zero as well
+        if (width == 0)
+            gap = 0;
+
+        // excessSpace can be negative
+        var excessSpace:Number = width - totalPreferredWidth - gap * (elementCount - 1);
+        var widthToDistribute:Number = width - gap * (elementCount - 1);
+        
+        // Special case: when we don't have enough space we need to count
+        // the number of children smaller than the averager size.
+        var averageWidth:Number;
+        var largeChildrenCount:int = elementCount;
+        if (excessSpace < 0)
+        {
+            averageWidth = width / elementCount;
+            for (i = 0; i < count; i++)
+            {
+                layoutElement = layoutTarget.getElementAt(i);
+                if (!layoutElement || !layoutElement.includeInLayout)
+                    continue;
+
+                var preferredWidth:Number = layoutElement.getPreferredBoundsWidth();
+                if (preferredWidth <= averageWidth)
+                {
+                    widthToDistribute -= preferredWidth;
+                    largeChildrenCount--;
+                    continue;
+                }
+            }
+            widthToDistribute = Math.max(0, widthToDistribute);
+        }
+        
+        // Resize and position children
+        var x:Number = 0;
+        var childWidth:Number = NaN;
+        var childWidthRounded:Number = NaN;
+        var roundOff:Number = 0;
+        for (i = 0; i < count; i++)
+        {
+            layoutElement = layoutTarget.getElementAt(i);
+            if (!layoutElement || !layoutElement.includeInLayout)
+                continue;
+
+            if (excessSpace > 0)
+            {
+                childWidth = widthToDistribute * layoutElement.getPreferredBoundsWidth() / totalPreferredWidth;
+            }
+            else if (excessSpace < 0)
+            {
+                childWidth = (averageWidth < layoutElement.getPreferredBoundsWidth()) ? widthToDistribute / largeChildrenCount : NaN;  
+            }
+            
+            if (!isNaN(childWidth))
+            {
+                // Round, we want integer values
+                childWidthRounded = Math.round(childWidth + roundOff);
+                roundOff += childWidth - childWidthRounded;
+            }
+
+            layoutElement.setLayoutBoundsSize(childWidthRounded, height);
+            layoutElement.setLayoutBoundsPosition(x, 0);
+            
+            // No need to round, width should be an integer number
+            x += gap + layoutElement.getLayoutBoundsWidth(); 
+            
+            // Reset childWidthRounded
+            childWidthRounded = NaN;
+        }
+    }
+}
+
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/AnimateColor.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/AnimateColor.as
new file mode 100644
index 0000000..753eab8
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/AnimateColor.as
@@ -0,0 +1,163 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 spark.effects.supportClasses.AnimateColorInstance;
+
+import mx.effects.IEffectInstance;
+import mx.styles.StyleManager;
+
+/**
+ *  The AnimateColor effect animates a change in a color property over time, interpolating
+ *  between given from/to color values on a per-channel basis. 
+ *  use this effect, rather than the Animate or other effect, when animating color properties. 
+ *  
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:AnimateColor&gt;</code> tag
+ *  inherits all of the tag attributes of its superclass,
+ *  and adds the following tag attributes:</p>
+ *
+ *  <pre>
+ *  &lt;s:AnimateColor
+ *    <b>Properties</b>
+ *    id="ID"
+ *    colorFrom="no default"
+ *    colorPropertyName="color"
+ *    colorTo="no default"
+ *  /&gt;
+ *  </pre>
+ *
+ *  @see spark.effects.supportClasses.AnimateColorInstance
+ *
+ *  @includeExample examples/AnimateColorEffectExample.mxml
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class AnimateColor extends Animate
+{
+    include "../core/Version.as";
+
+    //--------------------------------------------------------------------------
+    //
+    //  Class constants
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     */
+    private static var AFFECTED_PROPERTIES:Array = ["color"];
+    // Some objects have a 'color' style instead
+    private static var RELEVANT_STYLES:Array = ["color"];
+
+    [Inspectable(category="General", format="Color")]
+    /**
+     *  The starting color value. 
+     *
+     *  @default 0xFFFFFF
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var colorFrom:uint = StyleManager.NOT_A_COLOR;
+    
+    [Inspectable(category="General", format="Color")]
+    /**
+     * The ending color value.
+     *
+     *  @default 0xFFFFFF
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var colorTo:uint = StyleManager.NOT_A_COLOR;
+    
+    /**
+     *  The name of the color property on the target object affected
+     *  by this animation. 
+     *  A color property is a property that takes 32-bit color value.
+     * 
+     *  @default "color"
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var colorPropertyName:String = "color";
+    
+    /**
+     *  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 AnimateColor(target:Object=null)
+    {
+        super(target);
+        instanceClass = AnimateColorInstance;
+    }
+
+    /**
+     *  @private
+     */
+    override public function getAffectedProperties():Array /* of String */
+    {
+        return (colorPropertyName == "color") ? AFFECTED_PROPERTIES : [colorPropertyName];
+    }
+
+    /**
+     *  @private
+     */
+    override public function get relevantStyles():Array /* of String */
+    {
+        return (colorPropertyName == "color") ? RELEVANT_STYLES : [colorPropertyName];
+    }   
+
+    /**
+     *  @private
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override protected function initInstance(instance:IEffectInstance):void
+    {
+        super.initInstance(instance);
+        
+        var animateColorInstance:AnimateColorInstance = AnimateColorInstance(instance);
+        animateColorInstance.colorFrom = colorFrom;
+        animateColorInstance.colorTo = colorTo;
+        animateColorInstance.colorPropertyName = colorPropertyName;
+    }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/Fade.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/Fade.as
new file mode 100644
index 0000000..af9a0e0
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/Fade.as
@@ -0,0 +1,257 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 flash.display.DisplayObject;
+import flash.display.MovieClip;
+
+import mx.core.IVisualElement;
+import mx.core.IVisualElementContainer;
+import mx.core.mx_internal;
+import mx.effects.IEffectInstance;
+
+import spark.effects.supportClasses.FadeInstance;
+
+use namespace mx_internal;
+
+/**
+ *  The Fade effect animates the <code>alpha</code> property of a component.
+ *  If played manually (outside of a transition) on an object whose
+ *  <code>visible</code> property is set to false, and told to animate
+ *  <code>alpha</code> from zero to a nonzero value, it will set <code>visible</code>
+ *  to true as a side-effect of fading it in. When run as part of a
+ *  transition, it will respect state-specified values, but may use
+ *  the <code>visible</code> property as well as whether the object
+ *  is parented in the before/after states to determine the 
+ *  values to animate <code>alpha</code> from and to if <code>alphaFrom</code>
+ *  and <code>alphaTo</code> are not specified for the effect.
+ *  
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:Fade&gt;</code> tag
+ *  inherits the tag attributes of its superclass,
+ *  and adds the following tag attributes:</p>
+ *  
+ *  <pre>
+ *  &lt;s:Fade 
+ *    id="ID"
+ *    alphaFrom="val"
+ *    alphaTo="val"
+ *  /&gt;
+ *  </pre>
+ *
+ *  @see spark.effects.supportClasses.FadeInstance
+ * 
+ *  @includeExample examples/FadeEffectExample.mxml
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class Fade extends Animate
+{
+   /**
+    *  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 Fade(target:Object=null)
+    {
+        super(target);
+        instanceClass = FadeInstance;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+
+    //----------------------------------
+    //  alphaFrom
+    //----------------------------------
+
+    [Inspectable(category="General", defaultValue="undefined", minValue="0.0", maxValue="1.0")]
+    
+    /** 
+     *  Initial value of the <code>alpha</code> property, between 0.0 and 1.0, 
+     *  where 0.0 means transparent and 1.0 means fully opaque. 
+     * 
+     *  <p>If the effect causes the target component to disappear,
+     *  the default value is the current value of the target's
+     *  <code>alpha</code> property.
+     *  If the effect causes the target component to appear,
+     *  the default value is 0.0.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var alphaFrom:Number;
+    
+    //----------------------------------
+    //  alphaTo
+    //----------------------------------
+
+    [Inspectable(category="General", defaultValue="NaN", minValue="0.0", maxValue="1.0")]
+    
+    /** 
+     *  Final value of the <code>alpha</code> property, between 0.0 and 1.0,
+     *  where 0.0 means transparent and 1.0 means fully opaque.
+     *
+     *  <p>If the effect causes the target component to disappear,
+     *  the default value is 0.0.
+     *  If the effect causes the target component to appear,
+     *  the default value is the current value of the target's
+     *  <code>alpha</code> property.</p>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var alphaTo:Number;
+
+    
+    //--------------------------------------------------------------------------
+    //
+    // Methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private 
+     */
+    override protected function initInstance(instance:IEffectInstance):void
+    {
+        super.initInstance(instance);
+        
+        var fadeInstance:FadeInstance = FadeInstance(instance);
+
+        fadeInstance.alphaFrom = alphaFrom;
+        fadeInstance.alphaTo = alphaTo;
+    }
+
+    /**
+     *  @private
+     */
+    override public function getAffectedProperties():Array /* of String */
+    {
+        return ["alpha", "visible", "parent", "index", 
+            "explicitWidth", "explicitHeight", "rotation", "x", "y",
+            "left", "right", "top", "bottom",
+            "percentWidth", "percentHeight"];
+    }
+
+    /**
+     *  @private
+     */
+    override protected function getValueFromTarget(target:Object, property:String):*
+    {
+        // We track 'index' for use in the addDisappearingTarget() function in
+        // AnimateInstance, in order to add the item in the correct order
+        if (property == "index" && "parent" in target)
+        {
+            var container:* = target.parent;
+            // if the target has no parent, return undefined for index to indicate that
+            // it has no index value.
+            if (container === undefined || container === null ||
+                ("mask" in container && container.mask == target))
+                return undefined;
+            if (container is IVisualElementContainer)
+                return IVisualElementContainer(container).
+                    getElementIndex(target as IVisualElement);
+            else if ("getChildIndex" in container)
+                return container.getChildIndex(target);
+        }
+        
+        return super.getValueFromTarget(target, property);
+    }
+
+    /**
+     * @private
+     * This override handles the case caused by transition interruption
+     * where the target object may not have reached its final fade-in value, or 
+     * may not have had the end value of '1' applied correctly because the
+     * transition was interrupted and the animation stopped. The logic
+     * checks to see whether the object was being faded in or out, based on
+     * the 'visible' or 'parent' properties. It then sets the end alpha to either
+     * the proper state value (the typical case) or to 1 (as a backup).
+     * Note that a faded-out object due to going away or becoming invisible
+     * should still have an alpha value of 1; it just won't be visible because
+     * it is either invisible or has no parent. But we want the alpha value
+     * to be opaque the next time it is made visible.
+     */
+    override mx_internal function applyEndValues(propChanges:Array,
+                                                 targets:Array):void
+    {
+        super.applyEndValues(propChanges, targets);
+        if (transitionInterruption && propChanges)
+        {
+            var n:int = propChanges.length;
+            for (var i:int = 0; i < n; i++)
+            {
+                var target:Object = propChanges[i].target;
+                if (this.targets.indexOf(target ) >= 0 &&
+                    (propChanges[i].start["parent"] !== undefined &&
+                     propChanges[i].end["parent"] !== undefined &&
+                     propChanges[i].start["parent"] != propChanges[i].end["parent"]) ||
+                    (propChanges[i].start["visible"] !== undefined &&
+                        propChanges[i].end["visible"] !== undefined &&
+                        propChanges[i].start["visible"] != propChanges[i].end["visible"]))
+                {
+                    target.alpha = (propChanges[i].end["alpha"] !== undefined) ?
+                            propChanges[i].end["alpha"] : 1;
+                }
+            }
+        }
+    }
+
+    /**
+     *  @private
+     */
+    override protected function applyValueToTarget(target:Object,
+                                                   property:String, 
+                                                   value:*,
+                                                   props:Object):void
+    {
+        // We only want to track "parent" as it affects how
+        // we fade; we don't actually want to change target properties
+        // other than alpha or visibility
+        if (property == "parent" || property == "index" || 
+            property == "explicitWidth" || property == "explicitHeight" ||
+            property == "percentWidth" || property == "percentHeight" ||
+            property == "rotation" || property == "x" || property == "y" ||
+            property == "left" || property == "right" || property == "top" || property == "bottom")
+        {
+            return;
+        }
+            
+        super.applyValueToTarget(target, property, value, props);
+    }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/Resize.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/Resize.as
new file mode 100644
index 0000000..ad3e45f
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/Resize.as
@@ -0,0 +1,294 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.IEffectInstance;
+
+import spark.effects.supportClasses.ResizeInstance;
+
+use namespace mx_internal;
+
+/**
+ *  The Resize effect changes the width, height, or both dimensions
+ *  of a component over a specified time interval. 
+ *  
+ *  <p>If you specify only two of the three values of the
+ *  <code>widthFrom</code>, <code>widthTo</code>, and
+ *  <code>widthBy</code> properties, Flex calculates the third.
+ *  If you specify all three, Flex ignores the <code>widthBy</code> value.
+ *  If you specify only the <code>widthBy</code> or the
+ *  <code>widthTo</code> value, the <code>widthFrom</code> property
+ *  is set to be the object's current width.
+ *  The same is true for <code>heightFrom</code>, <code>heightTo</code>,
+ *  and <code>heightBy</code> property values.</p>
+ *  
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:Resize&gt;</code> tag
+ *  inherits all of the tag attributes of its superclass, 
+ *  and adds the following tab attributes:</p>
+ *  
+ *  <pre>
+ *  &lt;s:Resize
+ *    id="ID"
+ *    widthFrom="val"
+ *    heightFrom="val"
+ *    widthTo="val"
+ *    heightTo="val"
+ *    widthBy="val"
+ *    heightBy="val"
+ *  /&gt;
+ *  </pre>
+ *
+ *  @see spark.effects.supportClasses.ResizeInstance
+ *
+ *  @includeExample examples/ResizeEffectExample.mxml
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class Resize extends Animate
+{
+    include "../core/Version.as";
+
+    //--------------------------------------------------------------------------
+    //
+    //  Class constants
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     */
+    private static var AFFECTED_PROPERTIES:Array =
+    [
+        "width", "height",
+        "explicitWidth", "explicitHeight",
+        "percentWidth", "percentHeight",
+        "left", "right", "top", "bottom"
+    ];
+    private static var RELEVANT_STYLES:Array = 
+        ["left", "right", "top", "bottom", "percentWidth", "percentHeight"];
+
+    //--------------------------------------------------------------------------
+    //
+    //  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 Resize(target:Object=null)
+    {
+        super(target);
+
+        instanceClass = ResizeInstance;
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+    
+    //----------------------------------
+    //  heightBy
+    //----------------------------------
+
+    [Inspectable(category="General", defaultValue="NaN")]
+
+    /** 
+     *  Number of pixels by which to modify the height of the component.
+     *  Values may be negative.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var heightBy:Number;
+    
+    //----------------------------------
+    //  heightFrom
+    //----------------------------------
+
+    [Inspectable(category="General", defaultValue="NaN")]
+
+    /** 
+     *  Initial height, in pixels.
+     *  If omitted, Flex uses the current height of the target.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var heightFrom:Number;
+
+    //----------------------------------
+    //  heightTo
+    //----------------------------------
+
+    [Inspectable(category="General", defaultValue="NaN")]
+
+    /** 
+     *  Final height of the target, in pixels.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var heightTo:Number;
+            
+    //----------------------------------
+    //  widthBy
+    //----------------------------------
+
+    [Inspectable(category="General", defaultValue="NaN")]
+
+    /** 
+     *  Number of pixels by which to modify the width of the target.
+     *  Values may be negative.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var widthBy:Number;
+
+    //----------------------------------
+    //  widthFrom
+    //----------------------------------
+
+    [Inspectable(category="General", defaultValue="NaN")]
+
+    /** 
+     *  Initial width of the target, in pixels.
+     *  If omitted, Flex uses the current width.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var widthFrom:Number;
+    
+    //----------------------------------
+    //  widthTo
+    //----------------------------------
+
+    [Inspectable(category="General", defaultValue="NaN")]
+
+    /** 
+     *  Final width of the target, in pixels.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var widthTo:Number;
+
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     */
+    override public function getAffectedProperties():Array /* of String */
+    {
+        return AFFECTED_PROPERTIES;
+    }
+
+    /**
+     *  @private
+     */
+    override public function get relevantStyles():Array /* of String */
+    {
+        return RELEVANT_STYLES;
+    }   
+
+    /**
+     *  @private
+     */
+    override protected function initInstance(instance:IEffectInstance):void
+    {
+        super.initInstance(instance);
+        
+        var resizeInstance:ResizeInstance = ResizeInstance(instance);
+
+        if (!isNaN(widthFrom))
+            resizeInstance.widthFrom = widthFrom;
+        if (!isNaN(widthTo))
+            resizeInstance.widthTo = widthTo;
+        if (!isNaN(widthBy))
+            resizeInstance.widthBy = widthBy;
+        if (!isNaN(heightFrom))
+            resizeInstance.heightFrom = heightFrom;
+        if (!isNaN(heightTo))
+            resizeInstance.heightTo = heightTo;
+        if (!isNaN(heightBy))
+            resizeInstance.heightBy = heightBy;
+    }
+    
+    /**
+     * @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 (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;
+    }
+                                                     
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/SetAction.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/SetAction.as
new file mode 100644
index 0000000..0ce2ee4
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/SetAction.as
@@ -0,0 +1,179 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 spark.effects.supportClasses.SetActionInstance;
+
+import mx.effects.Effect;
+import mx.effects.IEffectInstance;
+
+//--------------------------------------
+//  Excluded APIs
+//--------------------------------------
+
+[Exclude(name="duration", kind="property")]
+
+/**
+ *  The SetAction class defines an action effect that sets 
+ *  the value of a named property or style.
+ *  You use a SetAction effect within a transition definition
+ *  to control when the view state change defined by a
+ *  property or style change occurs during the transition.
+ *  
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:SetAction&gt;</code> tag
+ *  inherits all of the tag attributes of its superclass,
+ *  and adds the following tag attributes:</p>
+ * 
+ *  <pre>
+ *  &lt;s:SetAction
+ *    <b>Properties</b>
+ *    id="ID"
+ *    property=""
+ *    value=""
+ *  /&gt;
+ *  </pre>
+ *  
+ *  @see spark.effects.supportClasses.SetActionInstance
+ *
+ *  @includeExample examples/SetActionEffectExample.mxml
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class SetAction 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 SetAction(target:Object = null)
+    {
+        super(target);
+        duration = 0;
+        instanceClass = SetActionInstance;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+
+    //----------------------------------
+    //  property
+    //----------------------------------
+
+    [Inspectable(category="General")]
+    
+    /** 
+     *  The name of the property being changed.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var property:String;
+    
+    //----------------------------------
+    //  value
+    //----------------------------------
+
+    [Inspectable(category="General")]
+    
+    /** 
+     *  The new value for the property.
+     *  When run within a transition and value is not specified, Flex determines 
+     *  the value based on that set by the new view state.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var value:*;
+        
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden properties
+    //
+    //--------------------------------------------------------------------------
+
+    //----------------------------------
+    //  relevantStyles
+    //----------------------------------
+
+    /**
+     *  @private
+     */
+    override public function get relevantStyles():Array /* of String */
+    {
+        return [ property ];
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     */
+    override public function getAffectedProperties():Array /* of String */
+    {
+        return [ property ];
+    }
+
+    /**
+     *  @private
+     */
+    override protected function initInstance(instance:IEffectInstance):void
+    {
+        super.initInstance(instance);
+        
+        var actionInstance:SetActionInstance =
+            SetActionInstance(instance);
+
+        actionInstance.property = property;
+        actionInstance.value = value;
+    }
+}
+
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/IEaser.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/IEaser.as
new file mode 100644
index 0000000..d71089e
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/IEaser.as
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 IEaser interface is implemented by classes that provide time-easing
+ *  functionality for the Animation class. 
+ *  Implementors are responsible for the single
+ *  function, <code>ease()</code>, which takes and returns a fraction according
+ *  to the easing behavior desired. As a simple example, LinearEase simply 
+ *  returns the same input fraction, since there is no easing performed by
+ *  that easer. As another example, a reversing easer could be written which
+ *  returned the inverse fraction, (1 - <code>fraction</code>).
+ * 
+ *  <p>By easing the fractional values of the time elapsed in an animation, 
+ *  these classes are easing the resulting values of the animation, but they
+ *  only have to deal with the fractional value of time instead of any
+ *  specific object types.</p>
+ * 
+ *  @see spark.effects.animation.Animation
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public interface IEaser
+{
+    /**
+     *  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.
+     * 
+     *  @param fraction The elapsed fraction of an animation, from 0.0 to 1.0.
+     * 
+     *  @return The eased value for the elapsed time. Typically, this value
+     *  should be constrained to lie between 0.0 and 1.0, although it is possible
+     *  to return values outside of this range. Note that the results of
+     *  returning such values are undefined, and depend on what kind of 
+     *  effects are using this eased value. For example, an object moving
+     *  in a linear fashion can have positions calculated outside of its start 
+     *  and end point without a problem, but other value types (such as color) 
+     *  may not result in desired effects if they use time values that cause
+     *  them to surpass their endpoint values.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    function ease(fraction:Number):Number;
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/Power.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/Power.as
new file mode 100644
index 0000000..bb0f06f
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/effects/easing/Power.as
@@ -0,0 +1,154 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 Power class defines the easing functionality using a polynomial expression.
+ *  Easing consists of two phases: the acceleration, or ease in phase,
+ *  followed by the deceleration, or ease out phase.
+ *  The rate of acceleration and deceleration is based on
+ *  the <code>exponent</code> property.
+ *  The higher the value of the <code>exponent</code> property,
+ *  the greater the acceleration and deceleration.
+ *  Use the <code>easeInFraction</code> property to specify the percentage
+ *  of an animation accelerating.
+ *
+ *  @mxml
+ *
+ *  <p>The <code>&lt;s:Power&gt;</code> tag
+ *  inherits all of the tag attributes of its of its superclass,
+ *  and adds the following tag attributes:</p>
+ *
+ *  <pre>
+ *  &lt;s:Power
+ *    id="ID"
+ *    exponent="2"
+ *   /&gt;
+ *  </pre>
+ *
+ *  @includeExample examples/SinePowerEffectExample.mxml
+ *
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class Power extends EaseInOutBase
+{
+    /**
+     * Storage for the exponent property
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    private var _exponent:Number;
+    
+    [Inspectable(minValue="1.0")]
+    
+    /**
+     *  The exponent used in the easing calculation.
+     *  The higher the value of the <code>exponent</code> property,
+     *  the greater the acceleration and deceleration.
+     *  For example, to get quadratic behavior, set <code>exponent</code> to 2.
+     *  To get cubic behavior, set <code>exponent</code> to 3.
+     *
+     *  @default 2
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get exponent():Number
+    {
+        return _exponent;
+    }
+    public function set exponent(value:Number):void
+    {
+        _exponent = value;
+    }
+
+    /**
+     * Constructor.
+     *
+     *  @param easeInFraction The fraction of the overall duration
+     *  in the acceleration phase, between 0.0 and 1.0.
+     *
+     *  @param exponent The exponent used in the easing calculation.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function Power(easeInFraction:Number = 0.5, exponent:Number = 2)
+    {
+        super(easeInFraction);
+        this.exponent = exponent;
+    }
+
+    /**
+     *  @private
+     *  Returns a value that represents the eased fraction during the
+     *  ease in phase of the animation.
+     *  The easing calculation for Power is equal to
+     *  <code>fraction^^exponent</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 Math.pow(fraction, _exponent);
+    }
+
+    /**
+     *  @private
+     *  Returns a value that represents the eased fraction during the
+     *  ease out phase of the animation.
+     *  The easing calculation for Power is equal to
+     *  <code>1 - ((1-fraction)^^exponent)</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 1 - Math.pow((1 - fraction), _exponent);
+    }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/events/TitleWindowBoundsEvent.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/events/TitleWindowBoundsEvent.as
new file mode 100644
index 0000000..db3a410
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/events/TitleWindowBoundsEvent.as
@@ -0,0 +1,283 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.events
+{
+    
+import flash.events.Event;
+import flash.geom.Rectangle;
+
+/**
+ *  The TitleWindowBoundsEvent class represents event objects 
+ *  that are dispatched when bounds of a
+ *  Spark TitleWindow changes, either by moving or resizing.
+ *
+ *  @see spark.components.TitleWindow
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class TitleWindowBoundsEvent extends Event
+{
+    include "../core/Version.as";
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Class constants
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  The <code>TitleWindowBoundsEvent.WINDOW_MOVE_START</code> constant defines the value of the
+     *  <code>type</code> property of the event object for a <code>windowMoveStart</code> event.
+     *
+     *  <p>The properties of the event object have the following values:</p>
+     *  <table class="innertable">
+     *     <tr><th>Property</th><th>Value</th></tr>
+     *     <tr><td><code>bubbles</code></td><td>false</td></tr>
+     *     <tr><td><code>cancelable</code></td><td>true</td></tr>
+     *     <tr><td><code>currentTarget</code></td><td>The Object that defines the
+     *       event listener that handles the event. For example, if you use
+     *       <code>myButton.addEventListener()</code> to register an event listener,
+     *       myButton is the value of the <code>currentTarget</code>. </td></tr>
+     *     <tr><td><code>beforeBounds</code></td><td>The starting bounds of the object.</td></tr>
+     *     <tr><td><code>afterBounds</code></td><td>null</td></tr>
+     *     <tr><td><code>target</code></td><td>The Object that dispatched the event;
+     *       it is not always the Object listening for the event.
+     *       Use the <code>currentTarget</code> property to always access the
+     *       Object listening for the event.</td></tr>
+     *  </table>
+     *
+     *  @eventType windowMoveStart
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static const WINDOW_MOVE_START:String = "windowMoveStart";
+    
+    /**
+     *  The <code>TitleWindowBoundsEvent.WINDOW_MOVING</code> constant defines the value of the
+     *  <code>type</code> property of the event object for a <code>windowMoving</code> event.
+     *
+     *  <p>The properties of the event object have the following values:</p>
+     *  <table class="innertable">
+     *     <tr><th>Property</th><th>Value</th></tr>
+     *     <tr><td><code>bubbles</code></td><td>false</td></tr>
+     *     <tr><td><code>cancelable</code></td><td>true</td></tr>
+     *     <tr><td><code>currentTarget</code></td><td>The Object that defines the
+     *       event listener that handles the event. For example, if you use
+     *       <code>myButton.addEventListener()</code> to register an event listener,
+     *       myButton is the value of the <code>currentTarget</code>. </td></tr>
+     *     <tr><td><code>beforeBounds</code></td><td>The current bounds of the object.</td></tr>
+     *     <tr><td><code>afterBounds</code></td><td>The future bounds of the object
+     *       if the object moves to the current cursor position.</td></tr>
+     *     <tr><td><code>target</code></td><td>The Object that dispatched the event;
+     *       it is not always the Object listening for the event.
+     *       Use the <code>currentTarget</code> property to always access the
+     *       Object listening for the event.</td></tr>
+     *  </table>
+     *
+     *  @eventType windowMoving
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static const WINDOW_MOVING:String = "windowMoving";
+
+    /**
+     *  The <code>TitleWindowBoundsEvent.WINDOW_MOVE</code> constant defines the value of the
+     *  <code>type</code> property of the event object for a <code>windowMove</code> event.
+     *
+     *  <p>The properties of the event object have the following values:</p>
+     *  <table class="innertable">
+     *     <tr><th>Property</th><th>Value</th></tr>
+     *     <tr><td><code>bubbles</code></td><td>false</td></tr>
+     *     <tr><td><code>cancelable</code></td><td>false</td></tr>
+     *     <tr><td><code>currentTarget</code></td><td>The Object that defines the
+     *       event listener that handles the event. For example, if you use
+     *       <code>myButton.addEventListener()</code> to register an event listener,
+     *       myButton is the value of the <code>currentTarget</code>. </td></tr>
+     *     <tr><td><code>beforeBounds</code></td><td>The previous bounds of the object.</td></tr>
+     *     <tr><td><code>afterBounds</code></td><td>The current bounds of the object.</td></tr>
+     *     <tr><td><code>target</code></td><td>The Object that dispatched the event;
+     *       it is not always the Object listening for the event.
+     *       Use the <code>currentTarget</code> property to always access the
+     *       Object listening for the event.</td></tr>
+     *  </table>
+     *
+     *  @eventType windowMove
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static const WINDOW_MOVE:String = "windowMove";  
+
+    /**
+     *  The <code>TitleWindowBoundsEvent.WINDOW_MOVE_END</code> constant defines the value of the
+     *  <code>type</code> property of the event object for a <code>windowMoveEnd</code> event.
+     *
+     *  <p>The properties of the event object have the following values:</p>
+     *  <table class="innertable">
+     *     <tr><th>Property</th><th>Value</th></tr>
+     *     <tr><td><code>bubbles</code></td><td>false</td></tr>
+     *     <tr><td><code>cancelable</code></td><td>false</td></tr>
+     *     <tr><td><code>currentTarget</code></td><td>The Object that defines the
+     *       event listener that handles the event. For example, if you use
+     *       <code>myButton.addEventListener()</code> to register an event listener,
+     *       myButton is the value of the <code>currentTarget</code>. </td></tr>
+     *     <tr><td><code>beforeBounds</code></td><td>The starting bounds of the object.</td></tr>
+     *     <tr><td><code>afterBounds</code></td><td>The final bounds of the object.</td></tr>
+     *     <tr><td><code>target</code></td><td>The Object that dispatched the event;
+     *       it is not always the Object listening for the event.
+     *       Use the <code>currentTarget</code> property to always access the
+     *       Object listening for the event.</td></tr>
+     *  </table>
+     *
+     *  @eventType windowMoveEnd
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static const WINDOW_MOVE_END:String = "windowMoveEnd";   
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  Constructor.
+     *
+     *  @param type The event type; indicates the action that caused the event.
+     *
+     *  @param bubbles Specifies whether the event can bubble
+     *  up the display list hierarchy.
+     *
+     *  @param cancelable Specifies whether the behavior
+     *  associated with the event can be prevented.
+     *
+     *  @param beforeBounds The bounds of the window before the action. If
+     *      this event is cancelable, <code>beforeBounds</code> is the current bounds of
+     *      the window. Otherwise, it is the bounds before a change occurred.
+     *
+     *  @param afterBounds The bounds of the window after the action. If
+     *      this event is cancelable, <code>afterBounds</code> is the future bounds of
+     *      the window. Otherwise, it is the current bounds.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function TitleWindowBoundsEvent(type:String, bubbles:Boolean = false,
+                                           cancelable:Boolean = false,
+                                           beforeBounds:Rectangle = null, afterBounds:Rectangle = null)
+    {
+        super(type, bubbles, cancelable);
+        
+        this.beforeBounds = beforeBounds;
+        this.afterBounds = afterBounds;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+    
+    //----------------------------------
+    //  beforeBounds
+    //----------------------------------
+    
+    /**
+     *  The bounds of the object before the action. If this event
+     *  is cancelable, <code>beforeBounds</code> is the current bounds of
+     *  the window. Otherwise, it is the bounds before a change occurred.
+     *
+     *  <p>The following list shows how this property is set for the different 
+     *  event types:</p>
+     *
+     *  <ul>
+     *    <li><code>WINDOW_MOVE</code> - The previous bounds of the window.</li>
+     *    <li><code>WINDOW_MOVE_END</code> - The starting bounds of the window, before the drag.</li>
+     *    <li><code>WINDOW_MOVE_START   </code> - The starting bounds of the window.</li>
+     *    <li><code>WINDOW_MOVING</code> - The current bounds of the window.</li>
+     *  </ul>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var beforeBounds:Rectangle;
+    
+    //----------------------------------
+    //  afterBounds
+    //----------------------------------
+    
+    /**
+     *  The bounds of the object after the action. If this event
+     *  is cancelable, <code>afterBounds</code> is the future bounds of
+     *  the window. Otherwise, it is the current bounds.
+     *
+     *  <p>The following list shows how this property is set for the different 
+     *  event types:</p>
+     *
+     *  <ul>
+     *    <li><code>WINDOW_MOVE</code> - The current bounds of the window.</li>
+     *    <li><code>WINDOW_MOVE_END</code> - The final bounds of the window, before the drag.</li>
+     *    <li><code>WINDOW_MOVE_START   </code> - null.</li>
+     *    <li><code>WINDOW_MOVING</code> - The future bounds of the window 
+     *      if the window moves to the current cursor position.</li>
+     *  </ul>
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public var afterBounds:Rectangle;
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden methods: Event
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @private
+     */
+    override public function clone():Event
+    {
+        return new TitleWindowBoundsEvent(type, bubbles, cancelable, beforeBounds, afterBounds);
+    }
+}
+
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/ColorMatrixFilter.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/ColorMatrixFilter.as
new file mode 100644
index 0000000..233f6d1
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/ColorMatrixFilter.as
@@ -0,0 +1,144 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.filters
+{
+import flash.filters.BitmapFilter;
+import flash.filters.ColorMatrixFilter;
+import mx.filters.BaseFilter;
+import mx.filters.IBitmapFilter;
+/**
+ *  The ColorMatrixFilter class lets you apply a 4 x 5 matrix transformation on the 
+ *  RGBA color and alpha values of every pixel in the input image to produce a result 
+ *  with a new set of RGBA color and alpha values. It allows saturation changes, hue 
+ *  rotation, luminance to alpha, and various other effects. You can apply the filter 
+ *  to any display object (that is, objects that inherit from the DisplayObject class), 
+ *  such as MovieClip, SimpleButton, TextField, and Video objects, as well as to 
+ *  BitmapData objects.
+ * 
+ *  @mxml 
+ *  <p>The <code>&lt;s:ColorMatrixFilter&gt;</code> tag inherits all of the tag 
+ *  attributes of its superclass and adds the following tag attributes:</p>
+ *
+ *  <pre>
+ *  &lt;s:ColorMatrixFilter
+ *    <strong>Properties</strong>
+ *    matrix="[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0]"
+ *  /&gt;
+ *  </pre>
+ * 
+ *  @see flash.filters.ColorMatrixFilter
+ * 
+ *  @includeExample examples/ColorMatrixFilterExample.mxml
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class ColorMatrixFilter extends BaseFilter implements IBitmapFilter
+{
+    /**
+     * Constructor.
+     *
+     * @tiptext Initializes a new ColorMatrixFilter instance.
+     *
+     * @param matrix An array of 20 items arranged as a 4 x 5 matrix.
+     *
+     * @langversion 3.0
+     * @playerversion Flash 10
+     * @playerversion AIR 1.5
+     * @productversion Flex 4
+     */
+    
+    public function ColorMatrixFilter(matrix:Array = null)
+    {
+        super();
+        
+        if (matrix != null)
+        {
+            this.matrix = matrix;
+        } 
+    }
+    
+    //----------------------------------
+    //  matrix
+    //----------------------------------
+    
+    private var _matrix:Array =  [1,0,0,0,0,0,
+                                  1,0,0,0,0,0,
+                                  1,0,0,0,0,0,
+                                  1,0];
+    
+    /**
+     *  A comma delimited list of 20 doubles that comprise a 4x5 matrix applied to the 
+     *  rendered element.  The matrix is in row major order -- that is, the first five 
+     *  elements are multipled by the vector [srcR,srcG,srcB,srcA,1] to determine the 
+     *  output red value, the second five determine the output green value, etc.
+     * 
+     *  <p>The value must either be an array or comma delimited string of 20 numbers. </p>
+     *
+     *  @default [1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0]
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get matrix():Object
+    {
+        return _matrix;
+    }
+    
+    public function set matrix(value:Object):void
+    {
+        if (value != _matrix)
+        {
+            if (value is Array)
+            {
+                _matrix = value as Array;
+            }
+            else if (value is String)
+            {
+                _matrix = String(value).split(',');
+            }
+            
+            notifyFilterChanged();
+        }
+    }
+    
+    /**
+     * Returns a copy of this filter object.
+     *
+     * @return A new ColorMatrixFilter instance with all of the same
+     * properties as the original one.
+     *
+     * @langversion 3.0
+     * @playerversion Flash 10
+     * @playerversion AIR 1.5
+     * @productversion Flex 4
+     */
+    
+    public function clone():BitmapFilter
+    {
+        return new flash.filters.ColorMatrixFilter(_matrix);
+    }
+    
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/DropShadowFilter.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/DropShadowFilter.as
new file mode 100644
index 0000000..fd7177b
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/DropShadowFilter.as
@@ -0,0 +1,361 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.filters
+{
+import flash.filters.BitmapFilter;
+import flash.filters.DropShadowFilter;
+import mx.filters.BaseDimensionFilter;
+import mx.filters.IBitmapFilter;
+
+
+/**
+ * The DropShadowFilter class lets you add a drop shadow to display objects.
+ * The shadow algorithm is based on the same box filter that the blur filter uses. You have 
+ * several options for the style of the drop shadow, including inner or outer shadow and knockout mode.
+ * You can apply the filter to any display object (that is, objects that inherit from the DisplayObject class), 
+ * such as MovieClip, SimpleButton, TextField, and Video objects, as well as to BitmapData objects.
+ * 
+ * <p>The use of filters depends on the object to which you apply the filter:</p>
+ * <ul><li>To apply filters to display objects use the
+ * <code>filters</code> property (inherited from DisplayObject). Setting the <code>filters</code> 
+ * property of an object does not modify the object, and you can remove the filter by clearing the
+ * <code>filters</code> property. </li>
+ * 
+ * <li>To apply filters to BitmapData objects, use the <code>BitmapData.applyFilter()</code> method.
+ * Calling <code>applyFilter()</code> on a BitmapData object takes the source BitmapData object 
+ * and the filter object and generates a filtered image as a result.</li>
+ * </ul>
+ * 
+ * <p>If you apply a filter to a display object, the value of the <code>cacheAsBitmap</code> property of the 
+ * display object is set to <code>true</code>. If you clear all filters, the original value of 
+ * <code>cacheAsBitmap</code> is restored.</p>
+ * <p>This filter supports Stage scaling. However, it does not support general scaling, rotation, and 
+ * skewing. If the object itself is scaled (if <code>scaleX</code> and <code>scaleY</code> are 
+ * set to a value other than 1.0), the filter is not scaled. It is scaled only when 
+ * the user zooms in on the Stage.</p>
+ * 
+ * <p>A filter is not applied if the resulting image exceeds the maximum dimensions.
+ * In  AIR 1.5 and Flash Player 10, the maximum is 8,191 pixels in width or height, 
+ * and the total number of pixels cannot exceed 16,777,215 pixels. (So, if an image is 8,191 pixels 
+ * wide, it can only be 2,048 pixels high.) 
+ * If, for example, you zoom in on a large movie clip with a filter applied, the filter is 
+ * turned off if the resulting image exceeds the maximum dimensions.</p>
+ *
+ *  @mxml 
+ *  <p>The <code>&lt;s:DropShadowFilter&gt;</code> tag inherits all of the tag 
+ *  attributes of its superclass and adds the following tag attributes:</p>
+ *
+ *  <pre>
+ *  &lt;s:DropShadowFilter 
+ *    <strong>Properties</strong>
+ *    alpha="1"
+ *    angle="45"
+ *    color="0xFF0000"
+ *    distance="4"
+ *    hideObject="false"
+ *    inner="false"
+ *  /&gt;
+ *  </pre>
+ *
+ * @includeExample examples/DropShadowFilterExample.mxml
+ *  
+ * @see flash.filters.DropShadowFilter
+ *  
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+ 
+public class DropShadowFilter extends BaseDimensionFilter implements IBitmapFilter
+{
+    /**
+     * Constructor.
+     * 
+     * @param distance Offset distance for the shadow, in pixels. 
+     * @param angle Angle of the shadow, 0 to 360 degrees (floating point). 
+     * @param color Color of the shadow, in hexadecimal format 
+     * <i>0xRRGGBB</i>. The default value is 0x000000.
+     * @param alpha Alpha transparency value for the shadow color. Valid values are 0.0 to 1.0. 
+     * For example, .25 sets a transparency value of 25%. 
+     * @param blurX Amount of horizontal blur. Valid values are 0 to 255.0 (floating point). 
+     * @param blurY Amount of vertical blur. Valid values are 0 to 255.0 (floating point). 
+     * @param strength The strength of the imprint or spread. The higher the value, 
+     * the more color is imprinted and the stronger the contrast between the shadow and the background. 
+     * Valid values are 0 to 255.0. 
+     * @param quality The number of times to apply the filter. Use the BitmapFilterQuality constants:
+     * <ul>
+     * <li><code>BitmapFilterQuality.LOW</code></li>
+     * <li><code>BitmapFilterQuality.MEDIUM</code></li>
+     * <li><code>BitmapFilterQuality.HIGH</code></li>
+     * </ul>
+     * <p>For more information about these values, see the <code>quality</code> property description.</p>
+     *
+     * @param inner Indicates whether or not the shadow is an inner shadow. A value of <code>true</code> specifies
+     * an inner shadow. A value of <code>false</code> specifies an outer shadow (a
+     * shadow around the outer edges of the object).
+     * @param knockout Applies a knockout effect (<code>true</code>), which effectively 
+     * makes the object's fill transparent and reveals the background color of the document. 
+     * @param hideObject Indicates whether or not the object is hidden. A value of <code>true</code> 
+     * indicates that the object itself is not drawn; only the shadow is visible. 
+     * 
+     * @see flash.filters.BitmapFilterQuality
+     * 
+     * @langversion 3.0
+     * @playerversion Flash 10
+     * @playerversion AIR 1.5
+     * @productversion Flex 4
+     */
+    public function DropShadowFilter(distance:Number = 4.0, angle:Number = 45, 
+                                     color:uint = 0, alpha:Number = 1.0, 
+                                     blurX:Number = 4.0, blurY:Number = 4.0, 
+                                     strength:Number = 1.0, quality:int = 1, 
+                                     inner:Boolean = false, 
+                                     knockout:Boolean = false, 
+                                     hideObject:Boolean = false)
+     {
+        super();
+        
+        this.distance = distance;
+        this.angle = angle;
+        this.color = color;
+        this.alpha = alpha;
+        this.blurX = blurX;
+        this.blurY = blurY;
+        this.strength = strength;
+        this.quality = quality;
+        this.inner = inner;
+        this.knockout = knockout;
+        this.hideObject = hideObject;
+     }
+    
+    //----------------------------------
+    //  alpha
+    //----------------------------------
+    
+    private var _alpha:Number = 1.0;
+    
+    [Inspectable(minValue="0.0", maxValue="1.0")]        
+    
+    /**
+     *  The alpha transparency value for the color. Valid values are 0 to 1. 
+     *  For example, .25 sets a transparency value of 25%.
+     * 
+     *  @default 1
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get alpha():Number
+    {
+        return _alpha;
+    }
+    
+    public function set alpha(value:Number):void
+    {
+        if (value != _alpha)
+        {
+            _alpha = value;
+            notifyFilterChanged();
+        }
+    }
+    
+    //----------------------------------
+    //  angle
+    //----------------------------------
+    
+    private var _angle:Number = 45;
+    
+    /**
+     *   The angle of the bevel. Valid values are from 0 to 360°. 
+     *   The angle value represents the angle of the theoretical light source falling on the 
+     *   object and determines the placement of the effect relative to the object. 
+     *   If the distance property is set to 0, the effect is not offset from the object and, 
+     *   therefore, the angle property has no effect.
+     * 
+     *   @default 45
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get angle():Number
+    {
+        return _angle;
+    }
+    
+    public function set angle(value:Number):void
+    {
+        if (value != _angle)
+        {
+            _angle = value;
+            notifyFilterChanged();
+        }
+    }
+    
+    //----------------------------------
+    //  color
+    //----------------------------------
+    
+    private var _color:uint = 0x000000;
+    
+    /**
+     *  The color of the glow. Valid values are in the hexadecimal format 
+     *  0xRRGGBB. 
+     *  @default 0xFF0000
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get color():uint
+    {
+        return _color;
+    }
+    
+    public function set color(value:uint):void
+    {
+        if (value != _color)
+        {
+            _color = value;
+            notifyFilterChanged();
+        }
+    }
+    
+    //----------------------------------
+    //  distance
+    //----------------------------------
+    
+    private var _distance:Number = 4.0;
+    
+    /**
+     *  The offset distance of the bevel. Valid values are in pixels (floating point). 
+     *  @default 4
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get distance():Number
+    {
+        return _distance;
+    }
+    
+    public function set distance(value:Number):void
+    {
+        if (value != _distance)
+        {
+            _distance = value;
+            notifyFilterChanged();
+        }
+    }
+    
+    //----------------------------------
+    //  hideObject
+    //----------------------------------
+    
+    private var _hideObject:Boolean = false;
+    
+    /**
+     *  Indicates whether or not the object is hidden. 
+     *  The value <code>true</code> indicates that the 
+     *  object itself is not drawn; only the shadow is visible. 
+     *  The default is <code>false</code>, meaning that the object is shown.
+     *
+     *  @default false
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get hideObject():Boolean
+    {
+        return _hideObject;
+    }
+    
+    public function set hideObject(value:Boolean):void
+    {
+        if (value != _hideObject)
+        {
+            _hideObject = value;
+            notifyFilterChanged();
+        }
+    }
+    
+    //----------------------------------
+    //  inner
+    //----------------------------------
+    
+    private var _inner:Boolean = false;
+    
+    /**
+     *  Specifies whether the glow is an inner glow. 
+     *  The value <code>true</code> indicates an inner glow. 
+     *  The default is <code>false</code> that creates an outer glow 
+     *  (a glow around the outer edges of the object).
+     *
+     *  @default false
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get inner():Boolean
+    {
+        return _inner;
+    }
+    
+    public function set inner(value:Boolean):void
+    {
+        if (value != _inner)
+        {
+            _inner = value;
+            notifyFilterChanged();
+        }
+    }
+    
+    /**
+     * Returns a copy of this filter object.
+     * @return A new DropShadowFilter instance with all the
+     * properties of the original DropShadowFilter instance.
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4 
+     */
+     
+    public function clone():BitmapFilter
+    {
+        return new flash.filters.DropShadowFilter(distance, angle, color, alpha, blurX, 
+                                                  blurY, strength, quality, inner, 
+                                                  knockout, hideObject);
+    }
+        
+}
+    
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/GlowFilter.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/GlowFilter.as
new file mode 100644
index 0000000..1661876
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/filters/GlowFilter.as
@@ -0,0 +1,266 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.filters
+{
+import flash.filters.BitmapFilter;
+import flash.filters.GlowFilter;
+import mx.filters.BaseDimensionFilter;
+import mx.filters.IBitmapFilter;
+
+/**
+ * The GlowFilter class lets you apply a glow effect to display objects.
+ * You have several options for the style of the 
+ * glow, including inner or outer glow and knockout mode. 
+ * The glow filter is similar to the drop shadow filter with the <code>distance</code>
+ * and <code>angle</code> properties of the drop shadow filter set to 0. 
+ * You can apply the filter to any display object (that is, objects that inherit from the DisplayObject class), 
+ * such as MovieClip, SimpleButton, TextField, and Video objects, as well as to BitmapData objects.
+ *
+ * <p>The use of filters depends on the object to which you apply the filter:</p>
+ * <ul><li>To apply filters to display objects, use the
+ * <code>filters</code> property (inherited from DisplayObject). Setting the <code>filters</code> 
+ * property of an object does not modify the object, and you can remove the filter by clearing the
+ * <code>filters</code> property. </li>
+ * 
+ * <li>To apply filters to BitmapData objects, use the <code>BitmapData.applyFilter()</code> method.
+ * Calling <code>applyFilter()</code> on a BitmapData object takes the source BitmapData object 
+ * and the filter object and generates a filtered image as a result.</li>
+ * </ul>
+ * 
+ * <p>If you apply a filter to a display object, the <code>cacheAsBitmap</code> property of the 
+ * display object is set to <code>true</code>. If you clear all filters, the original value of 
+ * <code>cacheAsBitmap</code> is restored.</p>
+ *
+ * <p>This filter supports Stage scaling. However, it does not support general scaling, rotation, and 
+ * skewing. If the object itself is scaled (if <code>scaleX</code> and <code>scaleY</code> are 
+ * set to a value other than 1.0), the filter is not scaled. It is scaled only when the user zooms
+ * in on the Stage.</p>
+ * 
+ * <p>A filter is not applied if the resulting image exceeds the maximum dimensions.
+ * In  AIR 1.5 and Flash Player 10, the maximum is 8,191 pixels in width or height, 
+ * and the total number of pixels cannot exceed 16,777,215 pixels. (So, if an image is 8,191 pixels 
+ * wide, it can only be 2,048 pixels high.) 
+ * For example, if you zoom in on a large movie clip with a filter applied, the filter is 
+ * turned off if the resulting image exceeds the maximum dimensions.</p>
+ * 
+ *  @mxml 
+ *  <p>The <code>&lt;s:GlowFilter&gt;</code> tag inherits all of the tag 
+ *  attributes of its superclass and adds the following tag attributes:</p>
+ *
+ *  <pre>
+ *  &lt;s:GlowFilter
+ *    <strong>Properties</strong>
+ *    alpha="1"
+ *    color="0xFF0000"
+ *    inner="false"
+ *  /&gt;
+ *  </pre>
+ *
+ * @see flash.filters.GlowFilter
+ * @see flash.display.BitmapData#applyFilter()
+ * @see flash.display.DisplayObject#filters
+ * @see flash.display.DisplayObject#cacheAsBitmap
+ * @see flash.display.DisplayObject#scaleX
+ * @see flash.display.DisplayObject#scaleY
+ * @see flash.filters.DropShadowFilter#distance
+ * @see flash.filters.DropShadowFilter#angle
+ *
+ * @includeExample examples/GlowFilterExample.mxml
+ *  
+ * @langversion 3.0
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @productversion Flex 4
+ */
+
+public class GlowFilter extends BaseDimensionFilter implements IBitmapFilter
+{
+    
+    /**
+     * Constructor.
+     * 
+     * @param color The color of the glow, in the hexadecimal format 
+     * 0x<i>RRGGBB</i>. The default value is 0xFF0000.
+     * @param alpha The alpha transparency value for the color. Valid values are 0 to 1. For example,
+     * .25 sets a transparency value of 25%.
+     * @param blurX The amount of horizontal blur. Valid values are 0 to 255 (floating point). Values
+     * that are a power of 2 (such as 2, 4, 8, 16 and 32) are optimized 
+     * to render more quickly than other values.
+     * @param blurY The amount of vertical blur. Valid values are 0 to 255 (floating point). 
+     * Values that are a power of 2 (such as 2, 4, 8, 16 and 32) are optimized 
+     * to render more quickly than other values.
+     * @param strength The strength of the imprint or spread. The higher the value, 
+     * the more color is imprinted and the stronger the contrast between the glow and the background. 
+     * Valid values are 0 to 255. 
+     * @param quality The number of times to apply the filter. Use the BitmapFilterQuality constants:
+     * <ul>
+     * <li><code>BitmapFilterQuality.LOW</code></li>
+     * <li><code>BitmapFilterQuality.MEDIUM</code></li>
+     * <li><code>BitmapFilterQuality.HIGH</code></li>
+     * </ul>
+     * <p>For more information, see the description of the <code>quality</code> property.</p>
+     * @param inner Specifies whether the glow is an inner glow. The value <code> true</code> specifies
+     * an inner glow. The value <code>false</code> specifies an outer glow (a glow
+     * around the outer edges of the object). 
+     * @param knockout Specifies whether the object has a knockout effect. The value <code>true</code>
+     * makes the object's fill transparent and reveals the background color of the document. 
+     *
+     * @see BitmapFilterQuality
+     *
+     * @langversion 3.0
+     * @playerversion Flash 10
+     * @playerversion AIR 1.5
+     * @productversion Flex 4
+     *
+     */
+    public function GlowFilter(color:uint = 0xFF0000, alpha:Number = 1.0, 
+                               blurX:Number = 4.0, blurY:Number = 4.0, 
+                               strength:Number = 1, quality:int = 1, 
+                               inner:Boolean = false, knockout:Boolean = false)
+    {
+        super();
+        
+        this.color = color;
+        this.alpha = alpha;
+        this.blurX = blurX;
+        this.blurY = blurY;
+        this.strength = strength;
+        this.quality = quality;
+        this.inner = inner;
+        this.knockout = knockout;
+    }
+
+    //----------------------------------
+    //  alpha
+    //----------------------------------
+    
+    private var _alpha:Number = 1.0;
+    
+    [Inspectable(minValue="0.0", maxValue="1.0")]    
+    
+    /**
+     *  The alpha transparency value for the color. Valid values are 0 to 1. 
+     *  For example, .25 sets a transparency value of 25%.
+     * 
+     *  @default 1
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get alpha():Number
+    {
+        return _alpha;
+    }
+    
+    public function set alpha(value:Number):void
+    {
+        if (value != _alpha)
+        {
+            _alpha = value;
+            notifyFilterChanged();
+        }
+    }
+    
+    //----------------------------------
+    //  color
+    //----------------------------------
+    
+    private var _color:uint = 0xFF0000;
+    
+    /**
+     *  The color of the glow. Valid values are in the hexadecimal format 
+     *  0xRRGGBB. 
+     *  @default 0xFF0000
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get color():uint
+    {
+        return _color;
+    }
+    
+    public function set color(value:uint):void
+    {
+        if (value != _color)
+        {
+            _color = value;
+            notifyFilterChanged();
+        }
+    }
+    
+    //----------------------------------
+    //  inner
+    //----------------------------------
+    
+    private var _inner:Boolean = false;
+    
+    /**
+     *  Specifies whether the glow is an inner glow. 
+     *  The value true indicates an inner glow. 
+     *  The default is false, meaning an outer glow 
+     *  (a glow around the outer edges of the object).
+     *
+     *  @default false
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function get inner():Boolean
+    {
+        return _inner;
+    }
+    
+    public function set inner(value:Boolean):void
+    {
+        if (value != _inner)
+        {
+            _inner = value;
+            notifyFilterChanged();
+        }
+    }
+
+    /**
+     * Returns a copy of this filter object.
+     * @return A new GlowFilter instance with all the
+     * properties of the original GlowFilter instance.
+     *
+     * @langversion 3.0
+     * @playerversion Flash 10
+     * @playerversion AIR 1.5
+     * @productversion Flex 4
+     */
+    public function clone():BitmapFilter
+    {
+        return new flash.filters.GlowFilter(color, alpha, blurX, blurY, strength, quality, inner, knockout);
+    }
+
+
+
+    
+}
+
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/primitives/Ellipse.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/primitives/Ellipse.as
new file mode 100644
index 0000000..d1d09aa
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/primitives/Ellipse.as
@@ -0,0 +1,355 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.primitives
+{
+
+import flash.display.Graphics;
+import flash.geom.Matrix;
+import flash.geom.Point;
+import flash.geom.Rectangle;
+
+import mx.core.mx_internal;
+import mx.utils.MatrixUtil;
+
+import spark.primitives.supportClasses.FilledElement;
+
+use namespace mx_internal;
+
+/**
+ *  The Ellipse class is a filled graphic element that draws an ellipse.
+ *  To draw the ellipse, this class calls the <code>Graphics.drawEllipse()</code> 
+ *  method.
+ *  
+ *  @see flash.display.Graphics
+ *  
+ *  @includeExample examples/EllipseExample.mxml
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class Ellipse extends FilledElement
+{
+    include "../core/Version.as";
+
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  Constructor. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public function Ellipse()
+    {
+        super();
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Overridden methods
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override protected function draw(g:Graphics):void
+    {
+        g.drawEllipse(drawX, drawY, width, height);
+    }
+    
+    /**
+     *  @private
+     */
+    override protected function transformWidthForLayout(width:Number,
+                                                        height:Number,
+                                                        postLayoutTransform:Boolean = true):Number
+    {
+        if (postLayoutTransform && hasComplexLayoutMatrix)
+            width = MatrixUtil.getEllipseBoundingBox(width / 2, height / 2, width / 2, height / 2, 
+                                                     layoutFeatures.layoutMatrix).width;    
+
+        // Take stroke into account
+        return width + getStrokeExtents(postLayoutTransform).width;
+    }
+
+    /**
+     *  @private
+     */
+    override protected function transformHeightForLayout(width:Number,
+                                                         height:Number,
+                                                         postLayoutTransform:Boolean = true):Number
+    {
+        if (postLayoutTransform && hasComplexLayoutMatrix)
+            height = MatrixUtil.getEllipseBoundingBox(width / 2, height / 2, width / 2, height / 2, 
+                                                      layoutFeatures.layoutMatrix).height;
+
+        // Take stroke into account
+        return height + getStrokeExtents(postLayoutTransform).height;
+    }
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override public function getBoundsXAtSize(width:Number, height:Number, postLayoutTransform:Boolean = true):Number
+    {
+        var strokeExtents:Rectangle = getStrokeExtents(postLayoutTransform);
+        var m:Matrix = getComplexMatrix(postLayoutTransform);
+        if (!m)
+            return strokeExtents.left + this.x;
+
+        if (!isNaN(width))
+            width -= strokeExtents.width;
+        if (!isNaN(height))
+            height -= strokeExtents.height;
+
+        // Calculate the width and height pre-transform:
+        var newSize:Point = MatrixUtil.fitBounds(width, height, m,
+                                                 explicitWidth, explicitHeight,
+                                                 preferredWidthPreTransform(),
+                                                 preferredHeightPreTransform(),
+                                                 minWidth, minHeight,
+                                                 maxWidth, maxHeight);
+        if (!newSize)
+            newSize = new Point(minWidth, minHeight);
+
+        return strokeExtents.left + 
+            MatrixUtil.getEllipseBoundingBox(newSize.x / 2, newSize.y / 2, newSize.x / 2, newSize.y / 2, m).x;
+    }
+
+    /**
+     *  @inheritDoc
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    override public function getBoundsYAtSize(width:Number, height:Number, postLayoutTransform:Boolean = true):Number
+    {
+        var strokeExtents:Rectangle = getStrokeExtents(postLayoutTransform);
+        var m:Matrix = getComplexMatrix(postLayoutTransform);
+        if (!m)
+            return strokeExtents.top + this.y;
+
+        if (!isNaN(width))
+            width -= strokeExtents.width;
+        if (!isNaN(height))
+            height -= strokeExtents.height;
+
+        // Calculate the width and height pre-transform:
+        var newSize:Point = MatrixUtil.fitBounds(width, height, m,
+                                                 explicitWidth, explicitHeight,
+                                                 preferredWidthPreTransform(),
+                                                 preferredHeightPreTransform(),
+                                                 minWidth, minHeight,
+                                                 maxWidth, maxHeight);
+        if (!newSize)
+            newSize = new Point(minWidth, minHeight);
+
+        return strokeExtents.top + 
+            MatrixUtil.getEllipseBoundingBox(newSize.x / 2, newSize.y / 2, newSize.x / 2, newSize.y / 2, m).y;
+    }
+
+    /**
+     *  @private
+     */
+    override public function getLayoutBoundsX(postLayoutTransform:Boolean = true):Number
+    {
+        var stroke:Number = getStrokeExtents(postLayoutTransform).left;
+        
+        if (postLayoutTransform && hasComplexLayoutMatrix)
+            return stroke + MatrixUtil.getEllipseBoundingBox(width / 2, height / 2, width / 2, height / 2, 
+                                                             layoutFeatures.layoutMatrix).x;
+        
+        return stroke + this.x;
+    }
+
+    /**
+     *  @private
+     */
+    override public function getLayoutBoundsY(postLayoutTransform:Boolean = true):Number
+    {
+        var stroke:Number = getStrokeExtents(postLayoutTransform).top;
+
+        if (postLayoutTransform && hasComplexLayoutMatrix)
+                return stroke + MatrixUtil.getEllipseBoundingBox(width / 2, height / 2, width / 2, height / 2, 
+                                                                 layoutFeatures.layoutMatrix).y;
+
+        return stroke + this.y;
+    }
+    
+    /**
+     *  @private
+     *  Returns the bounding box of the transformed ellipse(width, height) with matrix m.
+     */
+    private function getBoundingBox(width:Number, height:Number, m:Matrix):Rectangle
+    {
+        return MatrixUtil.getEllipseBoundingBox(0, 0, width / 2, height / 2, m);
+    }
+    
+    /**
+     *  @private
+     */
+    override public function setLayoutBoundsSize(width:Number,
+                                                 height:Number,
+                                                 postLayoutTransform:Boolean = true):void
+    {
+        var m:Matrix = getComplexMatrix(postLayoutTransform);
+        if (!m)
+        {
+            super.setLayoutBoundsSize(width, height, postLayoutTransform);
+            return;
+        }
+        
+        setLayoutBoundsTransformed(width, height, m);
+    }
+   
+    /**
+     *  @private
+     */
+    private function setLayoutBoundsTransformed(width:Number, height:Number, m:Matrix):void
+    {
+        var strokeExtents:Rectangle = getStrokeExtents(true);
+        width -= strokeExtents.width;
+        height -= strokeExtents.height;
+        
+        var size:Point = fitLayoutBoundsIterative(width, height, m);
+        
+        // We couldn't find a solution, try to relax the constraints
+        if (!size && !isNaN(width) && !isNaN(height))
+        {
+            // Try without width constraint
+            var size1:Point = fitLayoutBoundsIterative(NaN, height, m);
+            
+            // Try without height constraint
+            var size2:Point = fitLayoutBoundsIterative(width, NaN, m);
+            
+            // Ignore solutions that will exceed the requested size
+            if (size1 && getBoundingBox(size1.x, size1.y, m).width > width)
+                size1 = null;
+            if (size2 && getBoundingBox(size2.x, size2.y, m).height > height)
+                size2 = null;
+            
+            // Which size was better?
+            if (size1 && size2)
+            {
+                var pickSize1:Boolean = size1.x * size1.y > size2.x * size2.y;
+                
+                if (pickSize1)
+                    size = size1;
+                else
+                    size = size2;
+            }
+            else if (size1)
+            {
+                size = size1;
+            }
+            else
+            {
+                size = size2;
+            }
+        }
+        
+        if (size)
+            setActualSize(size.x, size.y);
+        else
+            setActualSize(minWidth, minHeight);
+    }
+    
+    /**
+     *  Iteratively approach a solution. Returns 0 if no exact solution exists.
+     *  NaN values for width/height mean "not constrained" in that dimesion. 
+     * 
+     *  @private
+     */
+    private function fitLayoutBoundsIterative(width:Number, height:Number, m:Matrix):Point
+    {
+        var newWidth:Number = this.preferredWidthPreTransform();
+        var newHeight:Number = this.preferredHeightPreTransform();
+        var fitWidth:Number = MatrixUtil.transformBounds(newWidth, newHeight, m).x;
+        var fitHeight:Number = MatrixUtil.transformBounds(newWidth, newHeight, m).y;
+        
+        if (isNaN(width))
+            fitWidth = NaN;
+        if (isNaN(height))
+            fitHeight = NaN;
+        
+        var i:int = 0;
+        while (i++ < 150)
+        {
+            var postTransformBounds:Rectangle = getBoundingBox(newWidth, newHeight, m);
+            
+            var widthDifference:Number = isNaN(width) ? 0 : width - postTransformBounds.width;
+            var heightDifference:Number = isNaN(height) ? 0 : height - postTransformBounds.height;
+            
+            if (Math.abs(widthDifference) < 0.1 && Math.abs(heightDifference) < 0.1)
+            {
+                return new Point(newWidth, newHeight);
+            }
+            
+            fitWidth += widthDifference * 0.5;
+            fitHeight += heightDifference * 0.5;
+            
+            var newSize:Point = MatrixUtil.fitBounds(fitWidth, 
+                                                     fitHeight, 
+                                                     m,
+                                                     explicitWidth, 
+                                                     explicitHeight,
+                                                     preferredWidthPreTransform(),
+                                                     preferredHeightPreTransform(),
+                                                     minWidth, minHeight,
+                                                     maxWidth, maxHeight);
+
+            if (!newSize)
+                break;
+            
+            newWidth = newSize.x;
+            newHeight = newSize.y;
+        }
+        
+        return null;        
+    }
+}
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/SparkButtonSkin.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/SparkButtonSkin.as
new file mode 100644
index 0000000..a3a8115
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/SparkButtonSkin.as
@@ -0,0 +1,529 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.skins
+{
+    import mx.core.IVisualElement;
+    import mx.styles.IStyleClient;
+    
+    import spark.components.Group;
+    import spark.components.IconPlacement;
+    import spark.components.supportClasses.ButtonBase;
+    import spark.components.supportClasses.LabelAndIconLayout;
+    import spark.core.IDisplayText;
+    import spark.layouts.*;
+    import spark.primitives.BitmapImage;
+    
+    /**
+     *  Base class for Spark button skins. Primarily used for
+     *  pay-as-you-go icon management.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4.5
+     */    
+    public class SparkButtonSkin extends SparkSkin
+    {              
+        
+        //--------------------------------------------------------------------------
+        //
+        //  Constructor
+        //
+        //--------------------------------------------------------------------------
+        
+        /**
+         * Constructor.
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 10
+         *  @playerversion AIR 1.5
+         *  @productversion Flex 4.5
+         */
+        public function SparkButtonSkin()
+        {
+            super();
+        }    
+        
+        //--------------------------------------------------------------------------
+        //
+        //  Variables
+        //
+        //--------------------------------------------------------------------------
+        
+        /**
+         * @private
+         * Internal flag used to determine if we should consider icon construction,
+         * placement, or layout in commitProperties.
+         */  
+        private var iconChanged:Boolean = true;
+        private var iconPlacementChanged:Boolean = false;
+        private var groupPaddingChanged:Boolean = true;
+        
+        /**
+         * @private
+         * Our transient icon and label Group.
+         */ 
+        private var iconGroup:Group;
+        private var _icon:Object;
+        
+        /**
+         * @private
+         * Saves our label's previous textAlign value.
+         */ 
+        private var prevTextAlign:String;
+        
+        //--------------------------------------------------------------------------
+        //
+        //  Properties
+        //
+        //--------------------------------------------------------------------------
+           
+        //----------------------------------
+        //  autoIconManagement
+        //----------------------------------
+        
+        private var _autoIconManagement:Boolean = true;
+        
+        /**
+         *  If enabled will automatically construct the necessary
+         *  constructs to present and layout an iconDisplay
+         *  part.
+         * 
+         *  @default true
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 10
+         *  @playerversion AIR 1.5
+         *  @productversion Flex 4
+         */
+        public function get autoIconManagement():Boolean
+        {
+            return _autoIconManagement;
+        }
+        
+        /**
+         *  @private
+         */
+        public function set autoIconManagement(value:Boolean):void
+        {
+            _autoIconManagement = value;
+            invalidateProperties();
+        }
+        
+        //----------------------------------
+        //  gap
+        //----------------------------------
+        
+        private var _gap:Number = 6;
+        
+        /**
+         *  Number of pixels between the buttons's icon and
+         *  label.
+         * 
+         *  @default 6
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 10
+         *  @playerversion AIR 1.5
+         *  @productversion Flex 4
+         */
+        public function get gap():Number
+        {
+            return _gap;
+        }
+        
+        /**
+         *  @private
+         */
+        public function set gap(value:Number):void
+        {
+            _gap = value;
+            groupPaddingChanged = true;
+            invalidateProperties();
+        }
+                
+        //----------------------------------
+        //  hostComponent
+        //----------------------------------
+        
+        /**
+         *  @private
+         */
+        private var _hostComponent:ButtonBase;
+        
+        /**
+         *  @private 
+         */ 
+        public function set hostComponent(value:ButtonBase):void
+        {
+            if (_hostComponent)
+                _hostComponent.removeEventListener("contentChange", contentChangeHandler);
+            
+            _hostComponent = value;
+            
+            if (value)
+                _hostComponent.addEventListener("contentChange", contentChangeHandler);
+        }
+        
+        /**
+         *  @private 
+         */ 
+        public function get hostComponent():ButtonBase
+        {
+            return _hostComponent;
+        }
+        
+        //----------------------------------
+        //  iconDisplay
+        //----------------------------------
+        
+        /**
+         * @copy spark.components.supportClasses.ButtonBase#iconDisplay
+         */  
+        [Bindable]
+        public var iconDisplay:BitmapImage;
+        
+        //----------------------------------
+        //  labelDisplay
+        //----------------------------------
+        
+        /**
+         * @copy spark.components.supportClasses.ButtonBase#labelDisplay
+         */  
+        [Bindable]
+        public var labelDisplay:IDisplayText;
+
+        
+        //----------------------------------
+        //  paddingLeft
+        //----------------------------------
+        
+        private var _iconGroupPaddingLeft:Number = 10;
+        
+        /**
+         *  The minimum number of pixels between the buttons's left edge and
+         *  the left edge of the icon or label.
+         * 
+         *  @default 0
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 10
+         *  @playerversion AIR 1.5
+         *  @productversion Flex 4.5
+         */
+        public function get iconGroupPaddingLeft():Number
+        {
+            return _iconGroupPaddingLeft;
+        }
+        
+        /**
+         *  @private
+         */
+        public function set iconGroupPaddingLeft(value:Number):void
+        {
+            _iconGroupPaddingLeft = value;
+            groupPaddingChanged = true;
+            invalidateProperties();
+        }    
+        
+        //----------------------------------
+        //  paddingRight
+        //----------------------------------
+        
+        private var _iconGroupPaddingRight:Number = 10;
+        
+        [Inspectable(category="General")]
+        
+        /**
+         *  The minimum number of pixels between the buttons's right edge and
+         *  the right edge of the icon or label.
+         * 
+         *  @default 0
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 10
+         *  @playerversion AIR 1.5
+         *  @productversion Flex 4
+         */
+        public function get iconGroupPaddingRight():Number
+        {
+            return _iconGroupPaddingRight;
+        }
+        
+        /**
+         *  @private
+         */
+        public function set iconGroupPaddingRight(value:Number):void
+        {
+            _iconGroupPaddingRight = value;
+            groupPaddingChanged = true;
+            invalidateProperties();
+        }    
+        
+        //----------------------------------
+        //  paddingTop
+        //----------------------------------
+        
+        private var _iconGroupPaddingTop:Number = 2;
+                
+        /**
+         *  Number of pixels between the buttons's top edge
+         *  and the top edge of the first icon or label.
+         * 
+         *  @default 0
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 10
+         *  @playerversion AIR 1.5
+         *  @productversion Flex 4
+         */
+        public function get iconGroupPaddingTop():Number
+        {
+            return _iconGroupPaddingTop;
+        }
+        
+        /**
+         *  @private
+         */
+        public function set iconGroupPaddingTop(value:Number):void
+        {
+            _iconGroupPaddingTop = value;
+            groupPaddingChanged = true;
+            invalidateProperties();
+        }    
+        
+        //----------------------------------
+        //  paddingBottom
+        //----------------------------------
+        
+        private var _iconGroupPaddingBottom:Number = 2;
+        
+        /**
+         *  Number of pixels between the buttons's bottom edge
+         *  and the bottom edge of the icon or label.
+         * 
+         *  @default 0
+         *  
+         *  @langversion 3.0
+         *  @playerversion Flash 10
+         *  @playerversion AIR 1.5
+         *  @productversion Flex 4
+         */
+        public function get iconGroupPaddingBottom():Number
+        {
+            return _iconGroupPaddingBottom;
+        }
+        
+        /**
+         *  @private
+         */
+        public function set iconGroupPaddingBottom(value:Number):void
+        {
+            _iconGroupPaddingBottom = value;
+            groupPaddingChanged = true;
+            invalidateProperties();
+        }
+        
+        //--------------------------------------------------------------------------
+        //
+        //  Overridden Methods
+        //
+        //--------------------------------------------------------------------------
+        
+        /**
+         *  @private 
+         */ 
+        override protected function commitProperties():void
+        {
+            super.commitProperties();
+            
+            if (!autoIconManagement)
+                return;
+
+            if (iconChanged)
+            {
+                // Make sure the icon has really changed, to avoid doing extra work for 
+                // broad stylesheet changes that don't actually change the "icon" style
+                var icon:Object = getStyle("icon");
+                if (_icon == icon)
+                    iconChanged = false;
+                else
+                    _icon = icon;
+            }
+
+            if (!(iconChanged || iconPlacementChanged || groupPaddingChanged))
+                return;
+
+            // If we have an icon to render we ensure the necessary
+            // parts are created and we configure a helper layout
+            // (LabelAndIconLayout) to manage our display parts.
+            if (_icon)
+            {
+                if (iconChanged)
+                    constructIconParts(true);
+
+                var layout:LabelAndIconLayout = iconGroup.layout as LabelAndIconLayout;
+                
+                if (groupPaddingChanged || iconChanged)
+                {
+                    layout.gap = _gap;
+                    layout.paddingLeft = _iconGroupPaddingLeft;
+                    layout.paddingRight = _iconGroupPaddingRight;
+                    layout.paddingTop = _iconGroupPaddingTop;
+                    layout.paddingBottom = _iconGroupPaddingBottom;
+                }
+                
+                if (iconPlacementChanged || iconChanged)
+                {
+                    layout.iconPlacement = getStyle("iconPlacement");
+                    alignLabelForPlacement();
+                }
+            }
+            else
+            {
+                // If we've previously realized our iconDisplay or iconGroup
+                // remove them from the display list as they are no long required.
+                constructIconParts(false);
+            }
+            
+            iconChanged = false;
+            iconPlacementChanged = false;
+            groupPaddingChanged = false;
+        }
+        
+        /**
+         *  @private 
+         *  Detected changes to iconPlacement and update as necessary.
+         */ 
+        override public function styleChanged(styleProp:String):void 
+        {
+            var allStyles:Boolean = !styleProp || styleProp == "styleName";
+
+            if (allStyles || styleProp == "iconPlacement")
+            {
+                iconPlacementChanged = true;
+                invalidateProperties();
+            }
+
+            if (allStyles || styleProp == "icon")
+            {
+                iconChanged = true;
+                invalidateProperties();
+            }
+
+            super.styleChanged(styleProp);
+        }
+        
+        //--------------------------------------------------------------------------
+        //
+        //  Methods
+        //
+        //--------------------------------------------------------------------------
+        
+        /**
+         *  @private 
+         *  Creates our iconDisplay and containing Group
+         *  or removes them from the display list and restores
+         *  our single labelDisplay.
+         */ 
+        private function constructIconParts(construct:Boolean):void
+        {
+            if (!autoIconManagement)
+                return;
+            
+            if (construct)
+            {
+                if (!iconDisplay)
+                    iconDisplay = new BitmapImage();
+                
+                if (!iconGroup)
+                {
+                    iconGroup = new Group();
+                    iconGroup.left = iconGroup.right = 0;
+                    iconGroup.top = iconGroup.bottom = 0;
+                    iconGroup.layout = new LabelAndIconLayout();
+                }
+                
+                iconGroup.addElement(iconDisplay);
+                
+                iconGroup.clipAndEnableScrolling = true;
+                addElement(iconGroup);
+                
+                if (labelDisplay && IVisualElement(labelDisplay).parent != iconGroup)
+                {
+                    iconGroup.addElement(IVisualElement(labelDisplay));
+                    if (labelDisplay is IStyleClient)
+                        prevTextAlign = IStyleClient(labelDisplay).getStyle("textAlign");
+                }
+            }
+            else
+            {
+                if (iconDisplay && iconDisplay.parent)
+                    iconGroup.removeElement(iconDisplay);
+                
+                if (iconGroup && iconGroup.parent)
+                {
+                    removeElement(iconGroup);
+                    addElement(IVisualElement(labelDisplay));
+                    
+                    if (labelDisplay is IStyleClient)
+                        IStyleClient(labelDisplay).setStyle("textAlign", prevTextAlign);
+                }
+            }
+        }
+        
+        /**
+         *  @private
+         */
+        private function alignLabelForPlacement():void
+        {
+            if (labelDisplay is IStyleClient)
+            {
+                var iconPlacement:String = getStyle("iconPlacement");
+                
+                var alignment:String = 
+                    iconPlacement == IconPlacement.LEFT ||
+                    iconPlacement == IconPlacement.RIGHT ? "start" : "center"
+                
+                IStyleClient(labelDisplay).setStyle("textAlign", alignment);
+            }
+        }
+        
+        //--------------------------------------------------------------------------
+        //
+        //  Event Handlers
+        //
+        //--------------------------------------------------------------------------
+        
+        /**
+         *  @private 
+         */ 
+        protected function contentChangeHandler(event:Event):void
+        {
+            // Ensure empty label is not included in layout else
+            // a gap between icon and label would be applied.
+            if (labelDisplay)
+            {
+                IVisualElement(labelDisplay).includeInLayout = labelDisplay.text != null 
+                    && labelDisplay.text.length;
+            }
+        }
+    }
+}
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/fullScreen/PlayPauseButtonSkin.mxml b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/fullScreen/PlayPauseButtonSkin.mxml
new file mode 100644
index 0000000..4b47d00
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/fullScreen/PlayPauseButtonSkin.mxml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+
+  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.
+
+-->
+
+<!--- The default skin class for the play/pause button of a Spark VideoPlayer 
+      component while in one of the <code>fullScreen</code> skin states.  
+
+      @see spark.components.VideoPlayer
+        
+      @langversion 3.0
+      @playerversion Flash 10
+      @playerversion AIR 1.5
+      @productversion Flex 4
+-->
+<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" 
+      alpha.disabledStates="0.5">
+
+    <!-- host component -->
+    <fx:Metadata>
+        /** 
+         * @copy spark.skins.spark.ApplicationSkin#hostComponent
+         */
+        [HostComponent("spark.components.ToggleButton")]
+    </fx:Metadata>
+    
+    <!-- states -->
+    <s:states>
+        <s:State name="up" />
+        <s:State name="over" stateGroups="overStates" />
+        <s:State name="down" stateGroups="downStates" />
+        <s:State name="disabled" stateGroups="disabledStates" />
+        <s:State name="upAndSelected" stateGroups="selectedStates, selectedUpStates" />
+        <s:State name="overAndSelected" stateGroups="overStates, selectedStates" />
+        <s:State name="downAndSelected" stateGroups="downStates, selectedStates" />
+        <s:State name="disabledAndSelected" stateGroups="selectedUpStates, disabledStates, selectedStates" />
+    </s:states>
+        
+    <!-- layer 1: fill -->
+    <s:Rect left="1" right="1" top="1" bottom="1">
+        <s:fill>
+            <s:LinearGradient rotation="90">
+                <s:GradientEntry color="0x585858"
+                                 color.overStates="0x676767"
+                                 color.downStates="0x2C2C2C"
+                                 alpha="0.55" />
+                <s:GradientEntry color="0x1E1E1E"
+                                 color.overStates="0x363636" 
+                                 color.downStates="0x585858"
+                                 alpha="0.55" />
+            </s:LinearGradient>
+        </s:fill>
+    </s:Rect>
+    
+    <!-- layer 2: One pixel stroke inside border -->
+    <s:Rect left="1" right="1" top="1" bottom="1">
+        <s:stroke>
+            <s:LinearGradientStroke weight="1" rotation="90">
+                <s:GradientEntry color="0xFFFFFF" alpha="0.12" />
+                <s:GradientEntry color="0xFFFFFF" alpha="0.09" />
+            </s:LinearGradientStroke>
+        </s:stroke>
+    </s:Rect>
+    
+    <!-- layer 3: border - put on top of the fill so it doesn't disappear when scale is less than 1 -->
+    <s:Rect left="0" right="0" top="0" bottom="0" width="38" height="24">
+        <s:stroke>
+            <s:SolidColorStroke color="0x222222" alpha="0.66" />
+        </s:stroke>
+    </s:Rect>
+    
+    <!--- Defines the play symbol. -->
+    <s:Group horizontalCenter="0" verticalCenter="0" excludeFrom="selectedStates" id="playSymbol">
+        <!-- triangle -->
+        <s:Path winding="evenOdd" data="M 1 0 L 1 13 L 8 7 L 1 0 Z">
+            <s:fill>
+                <s:SolidColor color="0xFFFFFF" alpha="0.75"/>
+            </s:fill>
+        </s:Path>
+        
+        <!-- triangle drop shadow on bottom/right -->
+        <s:Line xFrom="1" xTo="7" yFrom="13" yTo="7">
+            <s:stroke>
+                <s:SolidColorStroke color="0x000000" alpha="0.33" />
+            </s:stroke>
+        </s:Line>
+        
+        <!-- line on left of triangle -->
+        <s:Line x="0" yFrom="0" yTo="13">
+            <s:stroke>
+                <s:SolidColorStroke color="0x000000" alpha="0.33" />
+            </s:stroke>
+        </s:Line>
+    </s:Group>
+    
+    <!--- Defines the pause symbol. -->
+    <s:Group horizontalCenter="0" verticalCenter="0" includeIn="selectedStates" id="pauseSymbol">
+        
+        <!-- big line on left -->
+        <s:Rect left="0" top="0" height="10" width="3">
+            <s:fill>
+                <s:SolidColor color="0xFFFFFF" alpha="0.75"/>
+            </s:fill>
+        </s:Rect>
+        
+        <!-- big line on right -->
+        <s:Rect left="4" top="0" height="10" width="3">
+            <s:fill>
+                <s:SolidColor color="0xFFFFFF" alpha="0.75"/>
+            </s:fill>
+        </s:Rect>
+        
+        <!-- drop shadows -->
+        <s:Line xFrom="0" xTo="3" y="11">
+            <s:stroke>
+                <s:SolidColorStroke color="0x404040" />
+            </s:stroke>
+        </s:Line>
+        <s:Line xFrom="4" xTo="7" y="11">
+            <s:stroke>
+                <s:SolidColorStroke color="0x404040" />
+            </s:stroke>
+        </s:Line>
+    </s:Group>
+    
+</s:Skin>
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/fullScreen/ScrubBarSkin.mxml b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/fullScreen/ScrubBarSkin.mxml
new file mode 100644
index 0000000..1bc0e87
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/fullScreen/ScrubBarSkin.mxml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+
+  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.
+
+-->
+
+<!--- The default skin class for the scrub bar of a Spark VideoPlayer 
+      component while in one of the <code>fullScreen</code> skin states.  
+
+      @see spark.components.VideoPlayer
+        
+      @langversion 3.0
+      @playerversion Flash 10
+      @playerversion AIR 1.5
+      @productversion Flex 4
+-->
+<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
+      minHeight="14" minWidth="60"
+      alpha.disabled="0.5">
+
+    <fx:Metadata>
+        /** 
+         * @copy spark.skins.spark.ApplicationSkin#hostComponent
+         */
+        [HostComponent("spark.components.mediaClasses.ScrubBar")]
+    </fx:Metadata>
+    
+    <s:states>
+        <s:State name="normal" />
+        <s:State name="disabled" />
+    </s:states>
+    
+    <fx:Declarations>
+        <!--- Defines the appearance of the ScrubBar skin's data tip. To customize the data tip's appearance, create a custom ScrubBarSkin class. -->
+        <fx:Component id="dataTip" className="DataTipClass">     
+            <s:DataRenderer minHeight="24" minWidth="40" y="-34">
+                <s:RectangularDropShadow id="shadow" distance="3" 
+                    angle="90" color="#999999" left="0" top="0" right="0" bottom="0"/>
+                    
+                <s:Rect top="0" left="0" right="0" bottom="0">
+                    <s:fill>
+                        <s:SolidColor color="0x000000" alpha=".9"/>
+                    </s:fill>
+                </s:Rect>
+                
+                <s:Label id="labelDisplay" text="{data}"
+                         horizontalCenter="0" verticalCenter="1"
+                         left="5" right="5" top="5" bottom="5"
+                         textAlign="center" verticalAlign="middle"
+                         fontWeight="normal" color="white" fontSize="11">
+                </s:Label>
+            </s:DataRenderer>
+        </fx:Component>
+    </fx:Declarations>
+    
+    <!--- The skin pat that defines the video timeline. The timeline shows the current playhead location  
+          in the video, the amount of the video previously played, and the loaded in part of the video. -->
+    <s:Button id="track" left="0" right="0" top="0" height="11"
+              skinClass="spark.skins.spark.mediaClasses.fullScreen.ScrubBarTrackSkin" />
+    
+    <!--- @copy spark.components.mediaClasses.ScrubBar#loadedRangeArea -->
+    <s:Group id="loadedRangeArea" x="0" y="0" height="11" includeInLayout="false">
+    
+        <!-- inset 7 and 6 pixels because that's thumbSize/2 -->
+        <s:Group left="7" right="6" top="0" bottom="0" minWidth="0">
+        
+            <!-- fill -->
+            <s:Rect left="0" right="0" top="0" bottom="0">
+                <s:fill>
+                    <s:LinearGradient rotation="90">
+                        <s:GradientEntry color="0xFFFFFF" alpha="0.33"/>
+                        <s:GradientEntry color="0xE1E1E1" alpha="0.33"/>
+                    </s:LinearGradient>
+                </s:fill>
+            </s:Rect>
+            
+            <!-- black line on top -->
+            <!-- set width to 100%, maxWidth=1, minWidth=0 b/c only want this line to show up 
+                 if there's room for it -->
+            <s:Rect right="0" left="0" top="0">
+                <s:fill>
+                    <s:SolidColor color="0x000000" alpha=".18"/>
+                </s:fill>
+            </s:Rect>
+            
+        </s:Group>    
+    </s:Group>
+    
+    <!--- @copy spark.components.mediaClasses.ScrubBar#playedArea -->
+    <s:Group id="playedArea" x="0" y="0" height="11" includeInLayout="false">
+    
+        <!-- inset 7 and 6 pixels because that's thumbSize/2 -->
+        <s:Group left="7" right="6" top="0" bottom="0" minWidth="0">
+        
+            <!-- border -->
+            <s:Rect left="0" right="0" top="0" bottom="0" minWidth="0">
+                <s:fill>
+                    <s:SolidColor color="0x000000" alpha="0.85" />
+                </s:fill>
+            </s:Rect>
+        
+            <!-- fill -->
+            <s:Rect left="1" right="1" top="1" bottom="1">
+                <s:fill>
+                    <s:LinearGradient rotation="90">
+                        <s:GradientEntry color="0xEBECF2"/>
+                        <s:GradientEntry color="0xD2D3D8"/>
+                    </s:LinearGradient>
+                </s:fill>
+            </s:Rect>
+            
+        </s:Group>  
+    </s:Group>
+    
+    <!--- A skin part that defines a button that can be dragged along the track to increase or decrease 
+          the playhead location in the video.   -->
+    <s:Button id="thumb" x="0" y="0" width="14" height="19" includeInLayout="false"
+              skinClass="spark.skins.spark.mediaClasses.fullScreen.ScrubBarThumbSkin" />
+
+</s:Skin>
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/fullScreen/VolumeBarSkin.mxml b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/fullScreen/VolumeBarSkin.mxml
new file mode 100644
index 0000000..6168ae5
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/fullScreen/VolumeBarSkin.mxml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+--> 
+
+<!--- The default skin class for the volume bar of a Spark VideoPlayer 
+      component while in one of the <code>fullScreen</code> skin states.  
+
+      @see spark.components.VideoPlayer
+        
+      @langversion 3.0
+      @playerversion Flash 10
+      @playerversion AIR 1.5
+      @productversion Flex 4
+-->
+<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" alpha.disabled=".5"> 
+
+    <!-- host component -->
+    <fx:Metadata>
+        /** 
+         * @copy spark.skins.spark.ApplicationSkin#hostComponent
+         */
+        [HostComponent("spark.components.mediaClasses.VolumeBar")]
+    </fx:Metadata> 
+
+    <s:states>
+        <s:State name="normal" />
+        <s:State name="open" />
+        <s:State name="disabled" />
+    </s:states>
+    
+    <!--- The PopUpAnchor control that contains the drop-down slider control. -->
+    <s:PopUpAnchor id="popup"  displayPopUp.normal="false" displayPopUp.open="true" includeIn="open"
+        left="0" right="0" top="0" bottom="0" popUpPosition="above">
+        
+        <!--- @copy spark.components.mediaClasses.VolumeBar#dropDown -->
+        <s:Group id="dropDown" width="38" height="84" horizontalCenter="0">
+        
+            <!-- dropshadow for the dropdown -->
+            <s:Rect left="0" top="0" right="0" bottom="0">
+                <s:filters>
+                    <s:DropShadowFilter knockout="true" blurX="20" blurY="20" alpha="0.32" distance="11" angle="90" />   
+                </s:filters>    
+                <s:fill>    
+                    <s:SolidColor color="0x000000" />   
+                </s:fill>   
+            </s:Rect>
+        
+            <!-- background for the popup -->
+            <s:Rect left="0" right="0" top="0" bottom="0">
+                <s:fill>
+                    <s:SolidColor color="0x424242" />
+                </s:fill>
+                <s:stroke>
+                    <s:SolidColorStroke color="0x000000" alpha="0.55" />
+                </s:stroke>
+            </s:Rect>
+            
+            <!--- The skin pat that defines the drop-down slider track.  -->
+            <s:Button id="track" horizontalCenter="0" top="6" bottom="7"
+                      skinClass="spark.skins.spark.mediaClasses.fullScreen.VolumeBarTrackSkin" />
+            
+            <!--- The skin pat that defines the thumb in the drop-down slider track.  -->
+            <s:Button id="thumb" horizontalCenter="0" width="11" height="11" 
+                      skinClass="spark.skins.spark.mediaClasses.fullScreen.VolumeBarThumbSkin" />
+        </s:Group>
+    </s:PopUpAnchor>
+    
+    <!--- @copy spark.components.mediaClasses.VolumeBar#muteButton -->
+    <s:MuteButton id="muteButton" left="0" right="0" top="0" bottom="0" focusEnabled="false"
+              skinClass="spark.skins.spark.mediaClasses.fullScreen.MuteButtonSkin" />
+    
+</s:Skin>
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/normal/PlayPauseButtonSkin.mxml b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/normal/PlayPauseButtonSkin.mxml
new file mode 100644
index 0000000..28e8707
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/normal/PlayPauseButtonSkin.mxml
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+
+  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.
+
+-->
+
+<!--- The default skin class for the play/pause button of a Spark VideoPlayer component  
+      in the normal skin state. The normal skin state means the component is not in 
+      one of the <code>fullScreen</code> skin states.  
+
+      @see spark.components.VideoPlayer
+        
+      @langversion 3.0
+      @playerversion Flash 10
+      @playerversion AIR 1.5
+      @productversion Flex 4
+-->
+<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" 
+    xmlns:fb="http://ns.adobe.com/flashbuilder/2009" alpha.disabledStates="0.5">
+
+    <!-- host component -->
+    <fx:Metadata>
+        /** 
+         * @copy spark.skins.spark.ApplicationSkin#hostComponent
+         */
+        [HostComponent("spark.components.ToggleButton")]
+    </fx:Metadata>
+    
+    <fx:Script fb:purpose="styling">
+        /* Define the skin elements that should not be colorized. */
+        static private const exclusions:Array = ["playSymbol", "pauseSymbol"];
+        
+        /**
+         * @private
+         */
+        override public function get colorizeExclusions():Array {return exclusions;}
+        
+        /* Define the symbol fill items that should be colored by the "symbolColor" style.*/
+        static private const symbols:Array = ["playSymbolFill", "pauseSymbolFill1_1", "pauseSymbolFill1_2", 
+                                              "pauseSymbolFill1_3", "pauseSymbolFill1_4", "pauseSymbolFill1_5", 
+                                              "pauseSymbolFill2_1", "pauseSymbolFill2_2", "pauseSymbolFill2_3", 
+                                              "pauseSymbolFill2_4", "pauseSymbolFill2_5"];
+        
+        /**
+         * @private
+         */
+        override public function get symbolItems():Array {return symbols};
+        
+        /**
+         * @private
+         */
+        override protected function initializationComplete():void
+        {
+            useChromeColor = true;
+            super.initializationComplete();
+        }
+    </fx:Script>
+    
+    <!-- states -->
+    <s:states>
+        <s:State name="up" />
+        <s:State name="over" stateGroups="overStates" />
+        <s:State name="down" stateGroups="downStates" />
+        <s:State name="disabled" stateGroups="disabledStates" />
+        <s:State name="upAndSelected" stateGroups="selectedStates, selectedUpStates" />
+        <s:State name="overAndSelected" stateGroups="overStates, selectedStates" />
+        <s:State name="downAndSelected" stateGroups="downStates, selectedStates" />
+        <s:State name="disabledAndSelected" stateGroups="selectedUpStates, disabledStates, selectedStates" />
+    </s:states>
+        
+    <!-- layer 1: fill -->
+    <s:Rect left="1" right="1" top="1" bottom="1">
+        <s:fill>
+            <s:LinearGradient rotation="90">
+                <s:GradientEntry color="0xFFFFFF"
+                                 color.overStates="0xCACACA"
+                                 color.downStates="0xA8A8A8" />
+                <s:GradientEntry color="0xDCDCDC"
+                                 color.overStates="0x8D8D8D" 
+                                 color.downStates="0x6B6B6B"/>
+            </s:LinearGradient>
+        </s:fill>
+    </s:Rect>
+    
+    <!-- layer 2: One pixel stroke inside border (exclude in downStates) -->
+    <s:Rect left="1" right="1" top="1" bottom="1" excludeFrom="downStates">
+        <s:stroke>
+            <s:LinearGradientStroke weight="1" rotation="90">
+                <s:GradientEntry color="0xFEFEFE" alpha.overStates="0.22" />
+                <s:GradientEntry color="0xEAEAEA" alpha.overStates="0.22" />
+            </s:LinearGradientStroke>
+        </s:stroke>
+    </s:Rect>
+    
+    <!-- layer 3: fill highlight  (exclude in downStates) -->
+    <s:Rect left="1" right="1" top="1" height="11" excludeFrom="downStates">
+        <s:fill>
+            <s:SolidColor color="0xFFFFFF" 
+                        alpha="0.3" 
+                        alpha.overStates="0.12" />
+        </s:fill>
+    </s:Rect>
+    
+    <!-- layer 4: downstate inset border  (include only in downStates) -->
+    <s:Rect left="1" top="1" right="1" height="1" includeIn="downStates">
+        <s:fill>
+            <s:SolidColor color="0x000000" alpha="0.4" />
+        </s:fill>
+    </s:Rect>
+    <s:Rect left="1" top="2" right="1" height="1" includeIn="downStates">
+        <s:fill>
+            <s:SolidColor color="0x000000" alpha="0.12" />
+        </s:fill>
+    </s:Rect>
+    <s:Rect left="1" top="1" bottom="1" width="1" includeIn="downStates">
+        <s:fill>
+            <s:SolidColor color="0x000000" alpha="0.12" />
+        </s:fill>
+    </s:Rect>
+    <s:Rect right="1" top="1" bottom="1" width="1" includeIn="downStates">
+        <s:fill>
+            <s:SolidColor color="0x000000" alpha="0.12" />
+        </s:fill>
+    </s:Rect>
+    
+    <!-- layer 5: border - put on top of the fill so it doesn't disappear when scale is less than 1 -->
+    <s:Rect left="0" right="0" top="0" bottom="0" width="38" height="24">
+        <s:stroke>
+            <s:SolidColorStroke color="0x131313" />
+        </s:stroke>
+    </s:Rect>
+    
+    <!--- Defines the play symbol. -->
+    <s:Group horizontalCenter="0" verticalCenter="0" excludeFrom="selectedStates" id="playSymbol">
+        
+        <!-- triangle -->
+        <s:Path winding="evenOdd" data="M 1 0 L 1 13 L 8 7 L 1 0 Z">
+            <s:fill>
+                <!--- @private -->
+                <s:SolidColor color="0x555555" alpha="0.75" id="playSymbolFill"/>
+            </s:fill>
+        </s:Path>
+        
+        <!-- triangle drop shadow on bottom/right -->
+        <s:Line xFrom="1" xTo="7" yFrom="0" yTo="7">
+            <s:stroke>
+                <s:SolidColorStroke color="0x000000" alpha="0.33" />
+            </s:stroke>
+        </s:Line>
+        
+        <!-- line on left of triangle -->
+        <s:Line x="0" yFrom="0" yTo="13">
+            <s:stroke>
+                <s:SolidColorStroke color="0x000000" alpha="0.5" />
+            </s:stroke>
+        </s:Line>
+    </s:Group>
+    
+    <!--- Defines the pause symbol. -->
+    <s:Group horizontalCenter="0" verticalCenter="0" includeIn="selectedStates" id="pauseSymbol">
+    
+        <!-- big line on left -->
+        <s:Rect left="0" top="0" height="11" width="3">
+            <s:fill>
+                <s:LinearGradient rotation="90">
+                    <!--- @private -->
+                    <s:GradientEntry color="0x252525" alpha="0.75" ratio="0.1" id="pauseSymbolFill1_1"/>
+                    <!--- @private -->
+                    <s:GradientEntry color="0x404040" alpha="0.75" ratio="0.2" id="pauseSymbolFill1_2"/>
+                    <!--- @private -->
+                    <s:GradientEntry color="0x4B4B4B" alpha="0.75" ratio="0.55" id="pauseSymbolFill1_3"/>
+                    <!--- @private -->
+                    <s:GradientEntry color="0x424242" alpha="0.75" ratio="0.9" id="pauseSymbolFill1_4"/>
+                    <!--- @private -->
+                    <s:GradientEntry color="0xC4C4C4" alpha="0.75" ratio="1.0" id="pauseSymbolFill1_5"/>
+                </s:LinearGradient>
+            </s:fill>
+        </s:Rect>
+        
+        <!-- big line on right -->
+        <s:Rect left="4" top="0" height="11" width="3">
+            <s:fill>
+                <s:LinearGradient rotation="90">
+                    <!--- @private -->
+                    <s:GradientEntry color="0x252525" alpha="0.75" ratio="0.1" id="pauseSymbolFill2_1"/>
+                    <!--- @private -->
+                    <s:GradientEntry color="0x404040" alpha="0.75" ratio="0.2" id="pauseSymbolFill2_2"/>
+                    <!--- @private -->
+                    <s:GradientEntry color="0x4B4B4B" alpha="0.75" ratio="0.55" id="pauseSymbolFill2_3"/>
+                    <!--- @private -->
+                    <s:GradientEntry color="0x424242" alpha="0.75" ratio="0.9" id="pauseSymbolFill2_4"/>
+                    <!--- @private -->
+                    <s:GradientEntry color="0xC4C4C4" alpha="0.75" ratio="1.0" id="pauseSymbolFill2_5"/>
+                </s:LinearGradient>
+            </s:fill>
+        </s:Rect>
+        
+    </s:Group>
+</s:SparkSkin>
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/normal/ScrubBarSkin.mxml b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/normal/ScrubBarSkin.mxml
new file mode 100644
index 0000000..36413ce
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/normal/ScrubBarSkin.mxml
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+
+  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.
+
+-->
+
+<!--- The default skin class for the scrub bar of a Spark VideoPlayer component
+      in the normal skin state. The normal skin state means the component is not in 
+      one of the <code>fullScreen</code> skin states.  
+
+      @see spark.components.VideoPlayer
+        
+      @langversion 3.0
+      @playerversion Flash 10
+      @playerversion AIR 1.5
+      @productversion Flex 4
+-->
+<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
+      xmlns:fb="http://ns.adobe.com/flashbuilder/2009" minHeight="14" minWidth="60"
+      alpha.disabled="0.5">
+
+    <fx:Metadata>
+        /** 
+         * @copy spark.skins.spark.ApplicationSkin#hostComponent
+         */
+        [HostComponent("spark.components.mediaClasses.ScrubBar")]
+    </fx:Metadata> 
+    
+    <fx:Script fb:purpose="styling">
+        /* Define the skin elements that should not be colorized. */
+        static private const exclusions:Array = ["track", "thumb"];
+        
+        /**
+         * @private
+         */
+        override public function get colorizeExclusions():Array {return exclusions;}
+        
+        /**
+         * @private
+         */
+        override protected function initializationComplete():void
+        {
+            useChromeColor = true;
+            super.initializationComplete();
+        }
+    </fx:Script>
+    
+    <s:states>
+        <s:State name="normal" />
+        <s:State name="disabled" />
+    </s:states>
+    
+    <fx:Declarations>
+        <!--- Defines the appearance of the ScrubBar skin's data tip. To customize the data tip's appearance, create a custom ScrubBarSkin class. -->
+        <fx:Component id="dataTip">     
+            <s:DataRenderer minHeight="24" minWidth="40" y="-34">
+                <s:RectangularDropShadow id="shadow" distance="3" 
+                    angle="90" color="#999999" left="0" top="0" right="0" bottom="0"/>
+                    
+                <s:Rect top="0" left="0" right="0" bottom="0">
+                    <s:fill>
+                        <s:SolidColor color="0x000000" alpha=".9"/>
+                    </s:fill>
+                </s:Rect>
+                
+                <s:Label id="labelDisplay" text="{data}"
+                         horizontalCenter="0" verticalCenter="1"
+                         left="5" right="5" top="5" bottom="5"
+                         textAlign="center" verticalAlign="middle"
+                         fontWeight="normal" color="white" fontSize="11">
+                </s:Label>
+            </s:DataRenderer>
+        </fx:Component>
+    </fx:Declarations>
+    
+    <!--- The skin pat that defines the video timeline. The timeline shows the current playhead location  
+          in the video, the amount of the video previously played, and the loaded in part of the video. -->
+    <s:Button id="track" left="0" right="0" top="0" height="11"
+              skinClass="spark.skins.spark.mediaClasses.normal.ScrubBarTrackSkin" />
+
+    <!--- @copy spark.components.mediaClasses.ScrubBar#loadedRangeArea -->
+    <s:Group id="loadedRangeArea" x="0" y="0" height="11" includeInLayout="false">
+    
+        <!-- inset 7 and 6 pixels because that's thumbSize/2 -->
+        <s:Group left="7" right="6" top="0" bottom="0" minWidth="0">
+        
+            <!-- fill -->
+            <s:Rect left="1" right="1" top="1" bottom="1">
+                <s:fill>
+                    <s:SolidColor color="0xD7D7D7" />
+                </s:fill>
+            </s:Rect>
+            
+            <!-- inner glow -->
+            <!-- set height to 100%, maxHeight=1, minHeight=0 b/c only want this line to show up 
+                 if there's room for it -->
+            <s:Rect left="1" top="1" bottom="1" width="100%" maxWidth="1" minWidth="0">
+                <s:fill>
+                    <s:SolidColor color="0x000000" alpha="0.12" />
+                </s:fill>
+            </s:Rect>
+            <s:Rect left="2" right="1" top="1" height="100%" maxHeight="1" minHeight="0">
+                <s:fill>
+                    <s:SolidColor color="0x000000" alpha="0.12" />
+                </s:fill>
+            </s:Rect>
+            
+            <!-- black line on right -->
+            <!-- set width to 100%, maxWidth=1, minWidth=0 b/c only want this line to show up 
+                 if there's room for it -->
+            <s:Rect right="0" top="1" bottom="1" width="100%" maxWidth="1" minWidth="0">
+                <s:fill>
+                    <s:SolidColor color="0x000000" alpha=".5"/>
+                </s:fill>
+            </s:Rect>
+            
+        </s:Group>    
+    </s:Group>
+    
+    <!--- @copy spark.components.mediaClasses.ScrubBar#playedArea -->
+    <s:Group id="playedArea" x="0" y="0" height="11" includeInLayout="false">
+    
+        <!-- inset 7 and 6 pixels because that's thumbSize/2 -->
+        <s:Group left="7" right="6" top="0" bottom="0" minWidth="0">
+        
+            <!-- inner glow -->
+            <s:Rect left="1" right="1" top="1" bottom="1">
+                <s:fill>
+                    <s:LinearGradient rotation="90">
+                        <s:GradientEntry color="0xFEFEFE"/>
+                        <s:GradientEntry color="0xECECEC"/>
+                    </s:LinearGradient>
+                </s:fill>
+            </s:Rect>
+            
+            <!-- fill -->
+            <s:Rect left="2" right="2" top="2" bottom="2">
+                <s:fill>
+                    <s:LinearGradient rotation="90">
+                        <s:GradientEntry color="0xFFFFFF" alpha="0.85"/>
+                        <s:GradientEntry color="0xE1E1E1" alpha="0.85"/>
+                    </s:LinearGradient>
+                </s:fill>
+            </s:Rect>
+            
+            <!-- black line on right -->
+            <!-- set width to 100%, maxWidth=1, minWidth=0 b/c only want this line to show up 
+                 if there's room for it -->
+            <s:Rect right="0" top="1" bottom="1" width="100%" maxWidth="1" minWidth="0">
+                <s:fill>
+                    <s:SolidColor color="0x131313"/>
+                </s:fill>
+            </s:Rect>
+            
+        </s:Group>  
+    </s:Group>
+
+    <!--- A skin part that defines a button that can be dragged along the track to increase or decrease 
+          the playhead location in the video.   -->
+    <s:Button id="thumb" x="0" y="0" width="14" height="19" includeInLayout="false"
+              skinClass="spark.skins.spark.mediaClasses.normal.ScrubBarThumbSkin" />
+
+</s:SparkSkin>
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/normal/VolumeBarSkin.mxml b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/normal/VolumeBarSkin.mxml
new file mode 100644
index 0000000..ef46cb4
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/skins/spark/mediaClasses/normal/VolumeBarSkin.mxml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+  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.
+
+-->
+
+<!--- The default skin class for the volume bar of a Spark VideoPlayer component
+      in the normal skin state. The normal skin state means the component is not in 
+      one of the <code>fullScreen</code> skin states.  
+
+      @see spark.components.VideoPlayer
+        
+      @langversion 3.0
+      @playerversion Flash 10
+      @playerversion AIR 1.5
+      @productversion Flex 4
+-->
+<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" 
+    xmlns:fb="http://ns.adobe.com/flashbuilder/2009" alpha.disabled=".5"> 
+
+    <!-- host component -->
+    <fx:Metadata>
+        /** 
+         * @copy spark.skins.spark.ApplicationSkin#hostComponent
+         */
+        [HostComponent("spark.components.mediaClasses.VolumeBar")]
+    </fx:Metadata>
+    
+    <fx:Script fb:purpose="styling">
+        /* Define the skin elements that should not be colorized. */
+        static private const exclusions:Array = ["muteButton", "track", "thumb"];
+        
+        /**
+         * @private
+         */
+        override public function get colorizeExclusions():Array {return exclusions;}
+        
+        /**
+         * @private
+         */
+        override protected function initializationComplete():void
+        {
+            useChromeColor = true;
+            super.initializationComplete();
+        }
+    </fx:Script>
+
+    <s:states>
+        <s:State name="normal" />
+        <s:State name="open" />
+        <s:State name="disabled" />
+    </s:states>
+    
+    <!--- The PopUpAnchor control that contains the drop-down slider control. -->
+    <s:PopUpAnchor id="popup"  displayPopUp.normal="false" displayPopUp.open="true" includeIn="open"
+        left="0" right="0" top="0" bottom="0" popUpPosition="above" itemDestructionPolicy="auto">
+        
+        <!--- @copy spark.components.mediaClasses.VolumeBar#dropDown -->
+        <s:Group id="dropDown" width="38" height="84" horizontalCenter="0">
+        
+            <!-- dropshadow for the dropdown -->
+            <s:Rect left="0" top="0" right="0" bottom="0">
+                <s:filters>
+                    <s:DropShadowFilter knockout="true" blurX="20" blurY="20" alpha="0.32" distance="11" angle="90" />   
+                </s:filters>    
+                <s:fill>    
+                    <s:SolidColor color="0x000000" />   
+                </s:fill>   
+            </s:Rect>
+            
+            <!-- background for the popup -->
+            <s:Rect left="0" right="0" top="0" bottom="0">
+                <s:fill>
+                    <s:LinearGradient rotation="90">
+                        <s:GradientEntry color="0xFFFFFF"/>
+                        <s:GradientEntry color="0xDCDCDC"/>
+                    </s:LinearGradient>
+                </s:fill>
+                <s:stroke>
+                    <s:SolidColorStroke color="0x000000" />
+                </s:stroke>
+            </s:Rect>
+            
+            <!--- The skin pat that defines the drop-down slider track.  -->
+            <s:Button id="track" horizontalCenter="0" top="6" bottom="7"
+                      skinClass="spark.skins.spark.mediaClasses.normal.VolumeBarTrackSkin" />
+            
+            <!--- The skin pat that defines the thumb in the drop-down slider track.  -->
+            <s:Button id="thumb" horizontalCenter="0" width="11" height="11" 
+                      skinClass="spark.skins.spark.mediaClasses.normal.VolumeBarThumbSkin" />
+        </s:Group>
+    </s:PopUpAnchor>
+    
+    <!--- @copy spark.components.mediaClasses.VolumeBar#muteButton -->
+    <s:MuteButton id="muteButton" left="0" right="0" top="0" bottom="0" focusEnabled="false"
+              skinClass="spark.skins.spark.mediaClasses.normal.MuteButtonSkin" />
+    
+</s:SparkSkin>
diff --git a/frameworks/projects/SparkRoyale/src/main/royale/spark/utils/LabelUtil.as b/frameworks/projects/SparkRoyale/src/main/royale/spark/utils/LabelUtil.as
new file mode 100644
index 0000000..43a9d96
--- /dev/null
+++ b/frameworks/projects/SparkRoyale/src/main/royale/spark/utils/LabelUtil.as
@@ -0,0 +1,130 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.utils
+{
+
+import  mx.core.FlexVersion;
+    
+/**
+ *  The LabelUtil class is used by components to determine the correct
+ *  text to display for their renderers or sub-parts. 
+ *  
+ *  @langversion 3.0
+ *  @playerversion Flash 10
+ *  @playerversion AIR 1.5
+ *  @productversion Flex 4
+ */
+public class LabelUtil
+{
+    include "../core/Version.as";
+        
+    //--------------------------------------------------------------------------
+    //
+    //  Class methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  A function used by components that support item renderers 
+     *  to determine the correct text an item renderer should display for a data item. 
+     *  If no <code>labelField</code> or <code>labelFunction</code> parameter 
+     *  is specified, the <code>toString()</code> method of the data item  
+     *  is called to return a String representation of the data item.
+     *  
+     *  <p>The <code>labelFunction</code> property takes a reference to a function. 
+     *  The function takes a single argument which is the item in 
+     *  the data provider and returns a String:</p>
+     *  <pre>
+     *  myLabelFunction(item:Object):String</pre>
+     * 
+     *  @param item The data item. Null items return the empty string. 
+     * 
+     *  @param labelField The field in the data item to return. If labelField is set 
+     *  to an empty string (""), no field will be considered on the data item 
+     *  to represent label.
+     * 
+     *  @param labelFunction A function that takes the data item 
+     *  as a single parameter and returns a String. 
+     * 
+     *  @return A String representation for the data item 
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 1.5
+     *  @productversion Flex 4
+     */
+    public static function itemToLabel(item:Object, labelField:String=null, 
+        labelFunction:Function=null):String
+    {
+        if (labelFunction != null)
+            return labelFunction(item);
+
+        // early check for Strings
+        if (item is String)
+            return String(item);
+
+        if (item is XML)
+        {
+            try
+            {
+                if (item[labelField].length() != 0)
+                    item = item[labelField];
+                //by popular demand, this is a default XML labelField
+                //else if (item.@label.length() != 0)
+                //  item = item.@label;
+            }
+            catch(e:Error)
+            {
+            }
+        }
+        else if (item is Object)
+        {
+            try
+            {
+                if (item[labelField] != null)
+                    item = item[labelField];
+            }
+            catch(e:Error)
+            {
+            }
+        }
+
+        // late check for strings if item[labelField] was valid
+        if (item is String)
+            return String(item);
+
+        // special case for empty labelField
+        if (labelField == "" && FlexVersion.compatibilityVersion >= FlexVersion.VERSION_4_5)
+            return "";
+        
+        try
+        {
+            if (item !== null)
+                return item.toString();
+        }
+        catch(e:Error)
+        {
+        }
+
+        return " ";
+    }
+}
+
+}