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><s:SkinnableDataContainer></code> tag inherits all of the tag
+ * attributes of its superclass and adds the following tag attributes:</p>
+ *
+ * <pre>
+ * <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>"
+ * />
+ * </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><s:VideoDisplay></code> tag inherits all of the tag
+ * attributes of its superclass and adds the following tag attributes:</p>
+ *
+ * <pre>
+ * <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>"
+ *
+ * />
+ * </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><s:AnimateColor></code> tag
+ * inherits all of the tag attributes of its superclass,
+ * and adds the following tag attributes:</p>
+ *
+ * <pre>
+ * <s:AnimateColor
+ * <b>Properties</b>
+ * id="ID"
+ * colorFrom="no default"
+ * colorPropertyName="color"
+ * colorTo="no default"
+ * />
+ * </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><s:Fade></code> tag
+ * inherits the tag attributes of its superclass,
+ * and adds the following tag attributes:</p>
+ *
+ * <pre>
+ * <s:Fade
+ * id="ID"
+ * alphaFrom="val"
+ * alphaTo="val"
+ * />
+ * </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><s:Resize></code> tag
+ * inherits all of the tag attributes of its superclass,
+ * and adds the following tab attributes:</p>
+ *
+ * <pre>
+ * <s:Resize
+ * id="ID"
+ * widthFrom="val"
+ * heightFrom="val"
+ * widthTo="val"
+ * heightTo="val"
+ * widthBy="val"
+ * heightBy="val"
+ * />
+ * </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><s:SetAction></code> tag
+ * inherits all of the tag attributes of its superclass,
+ * and adds the following tag attributes:</p>
+ *
+ * <pre>
+ * <s:SetAction
+ * <b>Properties</b>
+ * id="ID"
+ * property=""
+ * value=""
+ * />
+ * </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><s:Power></code> tag
+ * inherits all of the tag attributes of its of its superclass,
+ * and adds the following tag attributes:</p>
+ *
+ * <pre>
+ * <s:Power
+ * id="ID"
+ * exponent="2"
+ * />
+ * </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><s:ColorMatrixFilter></code> tag inherits all of the tag
+ * attributes of its superclass and adds the following tag attributes:</p>
+ *
+ * <pre>
+ * <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]"
+ * />
+ * </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><s:DropShadowFilter></code> tag inherits all of the tag
+ * attributes of its superclass and adds the following tag attributes:</p>
+ *
+ * <pre>
+ * <s:DropShadowFilter
+ * <strong>Properties</strong>
+ * alpha="1"
+ * angle="45"
+ * color="0xFF0000"
+ * distance="4"
+ * hideObject="false"
+ * inner="false"
+ * />
+ * </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><s:GlowFilter></code> tag inherits all of the tag
+ * attributes of its superclass and adds the following tag attributes:</p>
+ *
+ * <pre>
+ * <s:GlowFilter
+ * <strong>Properties</strong>
+ * alpha="1"
+ * color="0xFF0000"
+ * inner="false"
+ * />
+ * </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 " ";
+ }
+}
+
+}