////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2009 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

package spark.components.supportClasses
{

import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.GestureEvent;
import flash.events.MouseEvent;
import flash.events.PressAndTapGestureEvent;
import flash.events.TouchEvent;
import flash.events.TransformGestureEvent;
import flash.filters.ShaderFilter;
import flash.geom.Point;
import flash.geom.Rectangle;

import mx.core.ILayoutElement;
import mx.core.IVisualElement;
import mx.core.UIComponent;
import mx.core.UIComponentGlobals;
import mx.core.mx_internal;
import mx.events.PropertyChangeEvent;
import mx.graphics.shaderClasses.LuminosityMaskShader;

import spark.components.ResizeMode;
import spark.core.IViewport;
import spark.core.MaskType;
import spark.events.DisplayLayerObjectExistenceEvent;
import spark.layouts.BasicLayout;
import spark.layouts.supportClasses.LayoutBase;
import spark.utils.FTETextUtil;
import spark.utils.MaskUtil;

use namespace mx_internal;

//--------------------------------------
//  Styles
//--------------------------------------

include "../../styles/metadata/BasicInheritingTextStyles.as"
include "../../styles/metadata/AdvancedInheritingTextStyles.as"
include "../../styles/metadata/SelectionFormatTextStyles.as"

/**
 *  Accent color used by component skins. The default button skin uses this color
 *  to tint the background. Slider track highlighting uses this color. 
 * 
 *  @default #0099FF
 * 
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[Style(name="accentColor", type="uint", format="Color", inherit="yes", theme="spark, mobile")]

/**
 *  The colors to use for the backgrounds of the items in the list. 
 *  The value is an array of one or more colors. 
 *  The backgrounds of the list items alternate among the colors in the array. 
 * 
 *  <p>The default value for the Spark theme is <code>undefined</code>.
 *  The default value for the Mobile theme is <code>0xFFFFFF</code>.</p>
 * 
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[Style(name="alternatingItemColors", type="Array", arrayType="uint", format="Color", inherit="yes", theme="spark, mobile")]

/**
 *  The main color for a component. 
 *   
 *  @default 0xCCCCCC
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */ 
[Style(name="chromeColor", type="uint", format="Color", inherit="yes", theme="spark, mobile")]

/**
 *  The alpha of the content background for this component.
 *
 *  @default 1.0
 * 
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[Style(name="contentBackgroundAlpha", type="Number", inherit="yes", theme="spark, mobile", minValue="0.0", maxValue="1.0")]

/**
 *  The color of the content background for this component.
 * 
 *  <p>For a List, changing <code>contentBackgroundColor</code> will 
 *  change the content background color of the List; however, if the item renderer
 *  is opaque, the user may not see any difference.  The item renderer's color is 
 *  affected by <code>alternatingItemColors</code>.  In the Spark theme, by default, item 
 *  renderers are transparent (<code>alternatingItemColors = undefined</code>); however, 
 *  in the Mobile theme, item renderers are opaque by default 
 *  (<code>alternatingItemColors = 0xFFFFFF</code>).</p>
 *  
 *  <p>The default value for the Spark theme is <code>0xFFFFFF</code>.
 *  The default value for the Mobile theme is <code>0xF0F0F0</code>.</p> 
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */ 
[Style(name="contentBackgroundColor", type="uint", format="Color", inherit="yes", theme="spark, mobile")]

/**
 *  The alpha value when the container is disabled.
 *
 *  @default 0.5
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[Style(name="disabledAlpha", type="Number", inherit="no", theme="spark, mobile", minValue="0.0", maxValue="1.0")]

/**
 *  Color of the background of an item renderer when it is being pressed down
 * 
 *  <p>If <code>downColor</code> is set to <code>undefined</code>, 
 *  <code>downColor</code> is not used.</p>
 * 
 *  <p>The default value for the Spark theme is <code>undefined</code>.
 *  The default value for the Mobile theme is <code>0xE0E0E0</code>.</p>
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10.1
 *  @playerversion AIR 2.5
 *  @productversion Flex 4.5
 */
[Style(name="downColor", type="uint", format="Color", inherit="yes", theme="mobile")]

/**
 *  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")]

/**
 *  Color of the highlights when the mouse is over the component.
 * 
 *  <p>This style is only applicable in mouse <code>interactionMode</code>.</p>
 * 
 *  @default 0xCEDBEF
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */ 
[Style(name="rollOverColor", type="uint", format="Color", inherit="yes", theme="spark")]

/**
 *  Color of any symbol of a component. Examples include the check mark of a CheckBox or
 *  the arrow of a scroll button.
 *   
 *  @default 0x000000
 * 
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */ 
[Style(name="symbolColor", type="uint", format="Color", inherit="yes", theme="spark, mobile")]

/**
 *  When in touch interaction mode, the number of milliseconds to wait after the user 
 *  interaction has occured before showing the component in a visually down state.
 * 
 *  <p>The reason for this delay is because when a user initiates a scroll gesture, we don't want 
 *  components to flicker as they touch the screen.  By having a reasonable delay, we make 
 *  sure that the user still gets feedback when they press down on a component, but that the 
 *  feedback doesn't come too quickly that it gets displayed during a scroll gesture 
 *  operation.</p>
 *  
 *  <p>If the mobile theme is applied, the default value for this style is 100 ms for 
 *  components inside of a Scroller and 0 ms for components outside of a Scroller.</p>
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10.1
 *  @playerversion AIR 2.5
 *  @productversion Flex 4.5
 */
[Style(name="touchDelay", type="Number", format="Time", inherit="yes", minValue="0.0")]

//--------------------------------------
//  Excluded APIs
//--------------------------------------

[Exclude(name="focusBlendMode", kind="style")]
[Exclude(name="focusThickness", kind="style")]

/**
 *  The GroupBase class defines the base class for components that display visual elements.
 *  A group component does not control the layout of the visual items that it contains. 
 *  Instead, the layout is handled by a separate layout component.
 *
 *  @mxml
 *
 *  <p>The <code>&lt;s:GroupBase&gt;</code> tag inherits all of the tag 
 *  attributes of its superclass and adds the following tag attributes:</p>
 *
 *  <pre>
 *  &lt;s:GroupBase
 *    <strong>Properties</strong>
 *    autoLayout="true"
 *    clipAndEnableScrolling="false"
 *    horizontalScrollPosition="null"
 *    luminosityClip="false"
 *    luminosityInvert="false"
 *    layout="BasicLayout"
 *    mask=""
 *    maskType="clip"
 *    mouseEnabledWhereTransparent="true"
 *    resizeMode="noScale"
 *    verticalScrollPosition="no default"
 *  
 *    <strong>Styles</strong>
 *    accentColor="0x0099FF"
 *    alignmentBaseline="useDominantBaseline"
 *    alternatingItemColors="undefined"
 *    baselineShift="0"
 *    blockProgression="tb"
 *    breakOpportunity="auto"
 *    cffHinting="horizontalStem"
 *    chromeColor="0xCCCCCC"
 *    color="0x000000"
 *    contentBackgroundAlpha="1.0"
 *    contentBackgroundColor="0xFFFFFF"
 *    clearFloats="none"
 *    digitCase="default"
 *    digitWidth="default"
 *    direction="ltr"
 *    disabledAlpha="0.5"
 *    dominantBaseline="auto"
 *    firstBaselineOffset="auto"
 *    focusColor="0x70B2EE"
 *    focusedTextSelectionColor="A8C6EE"
 *    fontFamily="Arial"
 *    fontLookup="device"
 *    fontSize="12"
 *    fontStyle="normal"
 *    fontWeight="normal"
 *    inactiveTextSelectionColor="E8E8E8"
 *    justificationRule="auto"
 *    justificationStyle="auto"
 *    kerning="auto"
 *    leadingModel="auto"
 *    ligatureLevel="common"
 *    lineHeight="120%"
 *    lineThrough="false"
 *    listAutoPadding="40"
 *    listStylePosition="outside"
 *    listStyleType="disc"
 *    locale="en"
 *    paragraphEndIndent="0"
 *    paragraphSpaceAfter="0"
 *    paragraphSpaceBefore="0"
 *    paragraphStartIndent="0"
 *    renderingMode="cff"
 *    rollOverColor="0xCEDBEF"
 *    symbolColor="0x000000"
 *    tabStops="null"
 *    textAlign="start"
 *    textAlignLast="start"
 *    textAlpha="1"
 *    textDecoration="none"
 *    textIndent="0"
 *    textJustify="interWord"
 *    textRotation="auto"
 *    trackingLeft="0"
 *    trackingRight="0"
 *    typographicCase="default"
 *    unfocusedTextSelectionColor="0xE8E8E8"
 *    whiteSpaceCollapse="collapse"
 *    wordSpacing="100%,50%,150%"
 *  /&gt;
 *  </pre>
 *
 *  @see spark.layouts.supportClasses.LayoutBase
 *  @see spark.components.ResizeMode
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
public class GroupBase extends UIComponent implements IViewport
{

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------
    
    /**
     *  Constructor.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function GroupBase()
    {
        super();
        showInAutomationHierarchy = false;
    }
        
    //--------------------------------------------------------------------------
    //
    //  Overridden properties: UIComponent
    //
    //--------------------------------------------------------------------------
    
    //----------------------------------
    //  alpha
    //----------------------------------
    
    private var _explicitAlpha:Number = 1.0;
    
    /**
     *  @private
     */
    override public function set alpha(value:Number):void
    {
        if (enabled)
            super.alpha = value;
        _explicitAlpha = value;
    }
    
    //----------------------------------
    //  baselinePosition
    //----------------------------------

    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    override public function get baselinePosition():Number
    {
        if (!validateBaselinePosition())
            return NaN;

        // Unless the height is very small, the baselinePosition
        // of a generic UIComponent is calculated as if there was
        // a UITextField using the component's styles
        // whose top coincides with the component's top.
        // If the height is small, the baselinePosition is calculated
        // as if there were text within whose ascent the component
        // is vertically centered.
        // At the crossover height, these two calculations
        // produce the same result.

        return FTETextUtil.calculateFontBaseline(this, height, moduleFactory);
    }

    //----------------------------------
    //  mouseChildren
    //----------------------------------

    private var _explicitMouseChildren:Boolean = true;

    /**
     *  @private
     */
    override public function set mouseChildren(value:Boolean):void
    {
        if (enabled)
            super.mouseChildren = value;
        _explicitMouseChildren = value;
    }

    //----------------------------------
    //  mouseEnabled
    //----------------------------------

    private var _explicitMouseEnabled:Boolean = true;

    /**
     *  @private
     */
    override public function set mouseEnabled(value:Boolean):void
    {
        if (enabled)
            super.mouseEnabled = value;
        _explicitMouseEnabled = value;
    }

    //----------------------------------
    //  enabled
    //----------------------------------

    /**
     *  @private
     */
    override public function set enabled(value:Boolean):void
    {
        super.enabled = value;

        // If enabled, reset the mouseChildren, mouseEnabled to the previously
        // set explicit value, otherwise disable mouse interaction.
        super.mouseChildren = value ? _explicitMouseChildren : false;
        super.mouseEnabled  = value ? _explicitMouseEnabled  : false;
        
        if (value)
            super.alpha = _explicitAlpha;
        else
        {
            var disabledAlpha:Number = getStyle("disabledAlpha");
            
            if (!isNaN(disabledAlpha))
                super.alpha = disabledAlpha;
            // else if it is NaN, it'll get handled in styleChanged()
        }
    }

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------
        
    //----------------------------------
    //  layout
    //----------------------------------    
    
    // layout is initialized in createChildren() if layout 
    // hasn't been set yet by someone else
    private var _layout:LayoutBase;
    private var _layoutProperties:Object = null;
    private var layoutInvalidateSizeFlag:Boolean = false;
    private var layoutInvalidateDisplayListFlag:Boolean = false;
    
    [Inspectable(category="General")]
    
    /**
     *  The layout object for this container.  
     *  This object is responsible for the measurement and layout of 
     *  the visual elements in the container.
     * 
     *  @default spark.layouts.BasicLayout
     *
     *  @see spark.layouts.supportClasses.LayoutBase
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get layout():LayoutBase
    {
        return _layout;
    }
        
    /**
     *  @private
     * 
     *  Three properties are delegated to the layout: clipAndEnableScrolling,
     *  verticalScrollPosition, horizontalScrollPosition.
     *  If the layout is reset, we copy the properties from the old
     *  layout to the new one (we don't copy verticalScrollPosition
     *  and horizontalScrollPosition from an old layout object to a new layout 
     *  object because this information might not translate correctly).   
     *  If the new layout is null, then we
     *  temporarily store the delegated properties in _layoutProperties. 
     */
    public function set layout(value:LayoutBase):void
    {
        if (_layout == value)
            return;
        
        if (_layout)
        {
            _layout.target = null;
            _layout.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, redispatchLayoutEvent);

            if (clipAndEnableScrollingExplicitlySet)
            {
                // when the layout changes, we don't want to transfer over 
                // horizontalScrollPosition and verticalScrollPosition
                _layoutProperties = {clipAndEnableScrolling: _layout.clipAndEnableScrolling};
            }
        }

        _layout = value; 

        if (_layout)
        {
            _layout.target = this;
            _layout.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, redispatchLayoutEvent);

            if (_layoutProperties)
            {
                if (_layoutProperties.clipAndEnableScrolling !== undefined)
                    value.clipAndEnableScrolling = _layoutProperties.clipAndEnableScrolling;
                
                if (_layoutProperties.verticalScrollPosition !== undefined)
                    value.verticalScrollPosition = _layoutProperties.verticalScrollPosition;
                
                if (_layoutProperties.horizontalScrollPosition !== undefined)
                    value.horizontalScrollPosition = _layoutProperties.horizontalScrollPosition;
                
                _layoutProperties = null;
            }
        }

        invalidateSize();
        invalidateDisplayList();
    }
    
    /**
     *  @private
     *  Redispatch the bindable LayoutBase properties that we expose (that we "facade"). 
     */
    private function redispatchLayoutEvent(event:Event):void
    {
        var pce:PropertyChangeEvent = event as PropertyChangeEvent;
        if (pce)
            switch (pce.property)
            {
                case "verticalScrollPosition":
                case "horizontalScrollPosition":
                    dispatchEvent(event);
                    break;
            }
    }    
    
    //----------------------------------
    //  horizontalScrollPosition
    //----------------------------------
        
    [Bindable]
    [Inspectable(minValue="0.0")]     

    /**
     *  @copy spark.core.IViewport#horizontalScrollPosition
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get horizontalScrollPosition():Number 
    {
        if (_layout)
        {
            return _layout.horizontalScrollPosition;
        }
        else if (_layoutProperties && 
                _layoutProperties.horizontalScrollPosition !== undefined)
        {
            return _layoutProperties.horizontalScrollPosition;
        }
        else
        {
            return 0;
        }
    }

    /**
     *  @private
     */
    public function set horizontalScrollPosition(value:Number):void 
    {
        if (_layout)
        {
            _layout.horizontalScrollPosition = value;
        }
        else if (_layoutProperties)
        {
            _layoutProperties.horizontalScrollPosition = value;
        }
        else
        {
            _layoutProperties = {horizontalScrollPosition: value};
        }
    }
    
    //----------------------------------
    //  verticalScrollPosition
    //----------------------------------
    
    [Bindable]
    [Inspectable(minValue="0.0")] 
    
    /**
     *  @copy spark.core.IViewport#verticalScrollPosition
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get verticalScrollPosition():Number 
    {
        if (_layout)
        {
            return _layout.verticalScrollPosition;
        }
        else if (_layoutProperties && 
                _layoutProperties.verticalScrollPosition !== undefined)
        {
            return _layoutProperties.verticalScrollPosition;
        }
        else
        {
            return 0;
        }
    }

    /**
     *  @private
     */
    public function set verticalScrollPosition(value:Number):void 
    {
        if (_layout)
        {
            _layout.verticalScrollPosition = value;
        }
        else if (_layoutProperties)
        {
            _layoutProperties.verticalScrollPosition = value;
        }
        else
        {
            _layoutProperties = {verticalScrollPosition: value};
        }
    }
    
    //----------------------------------
    //  clipAndEnableScrolling
    //----------------------------------

    private var clipAndEnableScrollingExplicitlySet:Boolean = false;
    
    /**
     *  @copy spark.core.IViewport#clipAndEnableScrolling
     *
     *  @default false
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get clipAndEnableScrolling():Boolean 
    {
        if (_layout)
        {
            return _layout.clipAndEnableScrolling;
        }
        else if (_layoutProperties && 
                _layoutProperties.clipAndEnableScrolling !== undefined)
        {
            return _layoutProperties.clipAndEnableScrolling;
        }
        else
        {
            return false;
        }
    }

    /**
     *  @private
     */
    public function set clipAndEnableScrolling(value:Boolean):void 
    {
        clipAndEnableScrollingExplicitlySet = true;
        if (_layout)
        {
            _layout.clipAndEnableScrolling = value;
        }
        else if (_layoutProperties)
        {
            _layoutProperties.clipAndEnableScrolling = value;
        }
        else
        {
            _layoutProperties = {clipAndEnableScrolling: value};
        }

        // clipAndEnableScrolling affects measured minimum size
        invalidateSize();
    }
    
    //----------------------------------
    //  scrollRect
    //----------------------------------
    
    private var _scrollRectSet:Boolean = false;
    
    /**
     * @private
     * GroupBase workaround for a FP bug.  Ignore attempts to reset the
     * DisplayObject scrollRect property to null (its default value) if we've
     * never set it.  Similarly, don't read the DisplayObject scrollRect
     * property if its value has not been set.
     */
     override public function get scrollRect():Rectangle
     {
         return (!_scrollRectSet) ? null : super.scrollRect;
     }

     /**
      * @private 
      */
    override public function set scrollRect(value:Rectangle):void
    {
        if (!_scrollRectSet && !value)
            return;
        _scrollRectSet = true;
        super.scrollRect = value;
    }
    
    //----------------------------------
    //  autoLayout
    //----------------------------------

    /**
     *  @private
     *  Storage for the autoLayout property.
     */
    private var _autoLayout:Boolean = true;

    [Inspectable(defaultValue="true")]

    /**
     *  If <code>true</code>, measurement and layout are done
     *  when the position or size of a child is changed.
     *  If <code>false</code>, measurement and layout are done only once,
     *  when children are added to or removed from the container.
     *
     *  @default true
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get autoLayout():Boolean
    {
        return _autoLayout;
    }

    /**
     *  @private
     */
    public function set autoLayout(value:Boolean):void
    {
        if (_autoLayout == value)
            return;

        _autoLayout = value;

        // If layout is being turned back on, trigger a layout to occur now.
        if (value)
        {
            invalidateSize();
            invalidateDisplayList();
            invalidateParentSizeAndDisplayList();
        }
    }

    //----------------------------------
    //  overlay
    //----------------------------------

    /**
     *  @private
     *  Storage for the overlay property 
     */
    mx_internal var _overlay:DisplayLayer;

    [Inspectable(category="General")]

    /**
     *  The overlay plane for this group.
     *  All objects of the overlay plane render on top of the Group elements.
     *
     *  <p><b>Note: </b>Do not retain this object because the group destroys and creates it on demand.</p>
     *   
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get overlay():DisplayLayer
    {
        if (!_overlay)
        {
            _overlay = new DisplayLayer();
            _overlay.addEventListener(DisplayLayerObjectExistenceEvent.OBJECT_ADD, overlay_objectAdd);
            _overlay.addEventListener(DisplayLayerObjectExistenceEvent.OBJECT_REMOVE, overlay_objectRemove);
            
            // Invalidate properties, so that if nothing was actually added to the
            // overlay object, we'll clean it up:
            invalidateProperties();
        }
        return _overlay;
    }
    
    /**
     *  @private
     *  Event listener to add overlay objects when added to the overlay DisplayLayer.
     */
    private function overlay_objectAdd(event:DisplayLayerObjectExistenceEvent):void
    {
        super.addChildAt(event.object, event.index + numChildren - _overlay.numDisplayObjects + 1);
    }
    
    /**
     *  @private
     *  Event listener to remove overlay objects when removed from the overlay
     *  DisplayLayer.
     */
    private function overlay_objectRemove(event:DisplayLayerObjectExistenceEvent):void
    {
        // Remove the object from the display list
        super.removeChild(event.object);
        
        // Is this the last display object?
        if (_overlay.numDisplayObjects == 1)
            invalidateProperties();
    }
    
    /**
     *  Destroys the overlay object. This method gets called on commitProperties
     *  when the overlay doesn't contain any objects. 
     */
    private function destroyOverlay():void
    {
        _overlay.removeEventListener(DisplayLayerObjectExistenceEvent.OBJECT_ADD, overlay_objectAdd);
        _overlay.removeEventListener(DisplayLayerObjectExistenceEvent.OBJECT_REMOVE, overlay_objectRemove);
        _overlay = null;
    }

    //----------------------------------
    //  resizeMode
    //----------------------------------
    
    private var _resizeMode:String = ResizeMode.NO_SCALE; 
    
    [Inspectable(category="General", enumeration="noScale,scale", defaultValue="noScale")]

    /**
     *  The ResizeMode for this container. If the resize mode
     *  is set to <code>ResizeMode.NO_SCALE</code>, resizing is done by laying 
     *  out the children with the new width and height. If the 
     *  resize mode is set to <code>ResizeMode.SCALE</code>, all of the children 
     *  keep their unscaled width and height and the children 
     *  are scaled to change size.
     * 
     * <p>The default value is <code>ResizeMode.NO_SCALE</code>, corresponding to "noScale".</p>
     * 
     * @see spark.components.ResizeMode
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get resizeMode():String
    {
        return _resizeMode; 
    }
    
    public function set resizeMode(value:String):void
    {
        if (_resizeMode == value)
            return;

        // If old value was scale, reset it            
        if (_resizeMode == ResizeMode.SCALE)
            setStretchXY(1, 1);

        _resizeMode = value;
        
        // We need the measured values and _resizeMode affects
        // our measure (skipMeasure implementation checks resizeMode) so
        // we need to invalidate the size.
        invalidateSize();
        
        // Invalidate the display list so that our validateMatrix() gets called.
        invalidateDisplayList();
    }

    //----------------------------------
    //  mouseOpaque
    //----------------------------------
    
    /**
     *  @private
     *  Storage for the mouseOpaque property
     */
    private var _mouseEnabledWhereTransparent:Boolean = true;
    private var mouseEventReferenceCount:int;

    [Inspectable(category="General", enumeration="true,false", defaultValue="true")]
    
    /**
     *  When <code>true</code>, this property 
     *  ensures that the entire bounds of the Group respond to 
     *  mouse events such as click and roll over.
     * 
     *  This property only goes in to effect if mouse, touch, or
     *  flash player gesture events are added to this instance.  In addition, 
     *  it assumes that the calls to addEventListener()/removeEventListener()
     *  are not superfluous.
     * 
     *  @default true
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get mouseEnabledWhereTransparent():Boolean
    {
        return _mouseEnabledWhereTransparent;
    }
    
    /**
     *  @private
     */
    public function set mouseEnabledWhereTransparent(value:Boolean):void
    {
        if (value == _mouseEnabledWhereTransparent)
            return;
            
        _mouseEnabledWhereTransparent = value;

        if (_hasMouseListeners)
            invalidateDisplayList();
    }
     
    /**
     *  @private
     *  Renders a background for the container, if necessary.  It is used to fill in
     *  a transparent background fill as necessary to support the _mouseEnabledWhereTransparent flag.  It 
     *  is also used in ItemRenderers when handleBackgroundColor is set to true.
     *  We assume for now that we are the first layer to be rendered into the graphics
     *  context.
     */
    mx_internal function drawBackground():void
    {
        if (!_mouseEnabledWhereTransparent || !_hasMouseListeners)
            return;
        
        var w:Number = (_resizeMode == ResizeMode.SCALE) ? measuredWidth : unscaledWidth;
        var h:Number = (_resizeMode == ResizeMode.SCALE) ? measuredHeight : unscaledHeight;

        if (isNaN(w) || isNaN(h))
            return;

        graphics.clear();
        graphics.beginFill(0xFFFFFF, 0);
        
        if (layout && layout.useVirtualLayout)
            graphics.drawRect(horizontalScrollPosition, verticalScrollPosition, w, h);
        else
        {
            const tileSize:int = 4096;
            const maxX:int = Math.round(Math.max(w, contentWidth));
            const maxY:int = Math.round(Math.max(h, contentHeight));
            for (var x:int = 0; x < maxX; x += tileSize)
                for (var y:int = 0; y < maxY; y += tileSize)
                {
                    var tileWidth:int = Math.min(maxX - x, tileSize);
                    var tileHeight:int = Math.min(maxY - y, tileSize);
                    graphics.drawRect(x, y, tileWidth, tileHeight); 
                }
        }

        graphics.endFill();
    }

    //----------------------------------
    //  hasMouseListeners
    //----------------------------------
    
    private var _hasMouseListeners:Boolean = false;
    
    /**
     * @private
     * 
     * This is a protected helper property used when selectively rendering
     * the fill layer for mouseOpaque (we only render when we have active
     * mouse event listeners).
     * 
     */  
    mx_internal function set hasMouseListeners(value:Boolean):void
    {
        if (_mouseEnabledWhereTransparent)
            $invalidateDisplayList();
    _hasMouseListeners = value;
    }
    
    /**
     * @private
     */  
    mx_internal function get hasMouseListeners():Boolean
    {
        return _hasMouseListeners;
    }
        
    /**
     *  @private
     */
    override protected function canSkipMeasurement():Boolean
    {
        // We never want to skip measure, if we resize by scaling
        return _resizeMode == ResizeMode.SCALE ? false : super.canSkipMeasurement();
    }

    /**
     *  @private
     */
    override public function invalidateSize():void
    {
        super.invalidateSize();
        layoutInvalidateSizeFlag = true;
    }

    /**
     *  @private
     */
    override public function invalidateDisplayList():void
    {
        super.invalidateDisplayList();
        layoutInvalidateDisplayListFlag = true;
    }
    
    /**
     *  @private
     *  Called when the child transform changes (currently x and y on UIComponent),
     *  so that the Group has a chance to invalidate the layout. 
     */
    override mx_internal function childXYChanged():void
    {
        if (autoLayout)
        {
            invalidateSize();
            invalidateDisplayList();
        }
    }
    
    /**
     *  @private
     *  Invalidates the size, but doesn't run layout measure pass. This is useful
     *  for subclasses like Group that perform additional work there - like running
     *  the graphic element measure pass.
     */
    mx_internal function $invalidateSize():void
    {
        super.invalidateSize();
    }
    
    /**
     *  @private
     *  Invalidates the display list, but doesn't run layout updateDisplayList pass.
     *  This is useful for subclasses like Group that perform additional work on
     *  updateDisplayList - like redrawing the graphic elements.
     */
    mx_internal function $invalidateDisplayList():void
    {
        super.invalidateDisplayList();
    }
    
    /**
     *  <p>If the layout object has not been set yet, 
     *  createChildren() assigns this container a 
     *  default layout object, BasicLayout.</p>
     *  
     *  @copy mx.core.UIComponent:createChildren()
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     */ 
    override protected function createChildren():void
    {
        super.createChildren();
        
        if (!layout)
            layout = new BasicLayout();
    }

    /**
     *  @private
     */ 
    override protected function commitProperties():void
    {
        super.commitProperties();
        
        // Cleanup the _overlay object when there are no more overlay objects
        if (_overlay && _overlay.numDisplayObjects == 0)
            destroyOverlay();
    }

    /**
     *  @private
     */
    override protected function measure():void
    {
        if (_layout && layoutInvalidateSizeFlag)
        {
            var oldMeasuredWidth:Number = measuredWidth;
            var oldMeasuredHeight:Number = measuredHeight;
            
            super.measure();
        
            layoutInvalidateSizeFlag = false;
            _layout.measure();
            
            // Special case: If the group clips content, or resizeMode is "scale"
            // then measured minimum size is zero
            if (clipAndEnableScrolling || resizeMode == ResizeMode.SCALE)
            {
                measuredMinWidth = 0;
                measuredMinHeight = 0;
            }

            // Make sure that we invalidate the display list if the measured
            // size changes, as we always size our content at the measured size when in scale mode.
            if (_resizeMode == ResizeMode.SCALE && (measuredWidth != oldMeasuredWidth || measuredHeight != oldMeasuredHeight))
                invalidateDisplayList();
        }
    }

    /**
     *  @private
     */
    override protected function validateMatrix():void
    {
        // Update the stretchXY before the matrix gets validates
        if (_resizeMode == ResizeMode.SCALE)
        {
            var stretchX:Number = 1;
            var stretchY:Number = 1;            

            if (measuredWidth != 0)
                stretchX = width / measuredWidth;
            if (measuredHeight != 0)
                stretchY = height / measuredHeight;

            setStretchXY(stretchX, stretchY);
        }
        super.validateMatrix();
    }

    /**
     *  @private
     */
    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
        var shaderFilter:ShaderFilter; 
        
        if (_resizeMode == ResizeMode.SCALE)
        {
            unscaledWidth = measuredWidth;
            unscaledHeight = measuredHeight;
        }

        super.updateDisplayList(unscaledWidth, unscaledHeight);

        if (maskChanged)
        {
            maskChanged = false;
            if (_mask)
            {
                maskTypeChanged = true;
            
                if (!_mask.parent)
                {
                    // TODO (jszeto): Does this need to be attached to a sibling?
                    overlay.addDisplayObject(_mask, OverlayDepth.MASK);
                    
                    MaskUtil.applyMask(_mask, null);
                }
            }
        }
        
        if (luminositySettingsChanged)
        {
            luminositySettingsChanged = false; 
            
            if (_mask && _maskType == MaskType.LUMINOSITY && _mask.filters.length > 0)
            {
                // Grab the shader filter 
                var shaderFilterIndex:int; 
                var len:int = _mask.filters.length; 
                for (shaderFilterIndex = 0; shaderFilterIndex < len; shaderFilterIndex++)
                {
                    if (_mask.filters[shaderFilterIndex] is ShaderFilter && 
                        ShaderFilter(_mask.filters[shaderFilterIndex]).shader is LuminosityMaskShader)
                    {
                        shaderFilter = _mask.filters[shaderFilterIndex];
                        break; 
                    }
                }
                
                if (shaderFilter && (shaderFilter.shader is LuminosityMaskShader))
                {
                    // Reset the mode property  
                    LuminosityMaskShader(shaderFilter.shader).mode = calculateLuminositySettings();
                    
                    // Re-apply the filter to the mask 
                    _mask.filters[shaderFilterIndex] = shaderFilter; 
                    _mask.filters = _mask.filters; 
                }
            }
        }
        
        if (maskTypeChanged)    
        {
            maskTypeChanged = false;
            
            if (_mask)
            {
                if (_maskType == MaskType.CLIP)
                {
                    // Turn off caching on mask
                    _mask.cacheAsBitmap = false; 
                    // Save the original filters and clear the filters property
                    originalMaskFilters = _mask.filters;
                    _mask.filters = []; 
                }
                else if (_maskType == MaskType.ALPHA)
                {
                    _mask.cacheAsBitmap = true;
                    cacheAsBitmap = true;
                }
                else if (_maskType == MaskType.LUMINOSITY)
                {
                    _mask.cacheAsBitmap = true;
                    cacheAsBitmap = true;
                    
                    // Create the shader class which wraps the pixel bender filter 
                    var luminosityMaskShader:LuminosityMaskShader = new LuminosityMaskShader();
                    
                    // Sets up the shader's mode property based on 
                    // whether the luminosityClip and 
                    // luminosityInvert properties are on or off. 
                    luminosityMaskShader.mode = calculateLuminositySettings(); 
                    
                    // Create the shader filter 
                    shaderFilter = new ShaderFilter(luminosityMaskShader);
                    
                    // Apply the shader filter to the mask
                    _mask.filters = [shaderFilter];
                }
            }
        }     

        if (layoutInvalidateDisplayListFlag)
        {
            layoutInvalidateDisplayListFlag = false;
            if (autoLayout && _layout)
                _layout.updateDisplayList(unscaledWidth, unscaledHeight);
                
            if (_layout)
                _layout.updateScrollRect(unscaledWidth, unscaledHeight);
        }
    }
    
    /**
     *  @private
     *  Calculates the luminosity mask shader's mode property which 
     *  determines how the shader is drawn. 
     */
    private function calculateLuminositySettings():int
    {
        var mode:int = 0;
        if (luminosityInvert)
            mode += 1; 
        if (luminosityClip) 
            mode += 2;  
        return mode; 
    }
    
    /**
     *  @private
     */
    override public function styleChanged(styleProp:String):void
    {
        super.styleChanged(styleProp);
        var allStyles:Boolean = (styleProp == null || styleProp == "styleName");
        
        if (!enabled && (allStyles || styleProp == "disabledAlpha"))
        {
            var disabledAlpha:Number = getStyle("disabledAlpha");
            
            if (!isNaN(disabledAlpha))
                super.alpha = disabledAlpha;
        }
    }
    
    /**
     *  @private 
     */ 
    override public function globalToLocal(point:Point):Point
    {
        if (resizeMode == ResizeMode.SCALE && _layoutFeatures != null)
        {
            // If resize mode is scale, then globalToLocal shouldn't account for 
            // stretchX/Y
            var sX:Number = _layoutFeatures.stretchX;
            var sY:Number = _layoutFeatures.stretchY;
            _layoutFeatures.stretchX = 1;
            _layoutFeatures.stretchY = 1;
            applyComputedMatrix();
            
            var p:Point = super.globalToLocal(point);
            
            // Restore stretch
            _layoutFeatures.stretchX = sX;
            _layoutFeatures.stretchY = sY;
            applyComputedMatrix();
            
            return p;
        }
        else
        {
            return super.globalToLocal(point);    
        }
    }
    
    /**
     *  @private 
     */ 
    override public function localToGlobal(point:Point):Point
    {
        if (resizeMode == ResizeMode.SCALE && _layoutFeatures != null)
        {
            // If resize mode is scale, then localToGlobal shouldn't account for 
            // stretchX/Y
            var sX:Number = _layoutFeatures.stretchX;
            var sY:Number = _layoutFeatures.stretchY;
            _layoutFeatures.stretchX = 1;
            _layoutFeatures.stretchY = 1;
            applyComputedMatrix();
            
            var p:Point = super.localToGlobal(point);
            
            // Restore stretch
            _layoutFeatures.stretchX = sX;
            _layoutFeatures.stretchY = sY;
            applyComputedMatrix();
            
            return p;
        }
        else
        {
            return super.localToGlobal(point);    
        }
    }
    
    //----------------------------------
    //  horizontal,verticalScrollPositionDelta
    //----------------------------------

    /**
     *  @copy spark.layouts.supportClasses.LayoutBase#getHorizontalScrollPositionDelta()
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function getHorizontalScrollPositionDelta(navigationUnit:uint):Number
    {
        return (layout) ? layout.getHorizontalScrollPositionDelta(navigationUnit) : 0;     
    }
    
    /**
     *  @copy spark.layouts.supportClasses.LayoutBase#getVerticalScrollPositionDelta()
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function getVerticalScrollPositionDelta(navigationUnit:uint):Number
    {
        return (layout) ? layout.getVerticalScrollPositionDelta(navigationUnit) : 0;     
    }
    
    /**
     *  @private
     *  @copy spark.layouts.supportClasses.LayoutBase#isElementVisible()
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4 
     */
    mx_internal function isElementVisible(elt:ILayoutElement):Boolean
    {
        return layout && layout.isElementVisible(elt);
    }
    
    //--------------------------------------------------------------------------
    //
    //  IViewport properties
    //
    //--------------------------------------------------------------------------        

    //----------------------------------
    //  contentWidth
    //---------------------------------- 
    
    private var _contentWidth:Number = 0;
    
    [Bindable("propertyChange")]
    [Inspectable(category="General")]    

    /**
     *  @copy spark.core.IViewport#contentWidth
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get contentWidth():Number 
    {
        return _contentWidth;
    }
    
    /**
     *  @private
     */
    private function setContentWidth(value:Number):void 
    {
        if (value == _contentWidth)
            return;
        var oldValue:Number = _contentWidth;
        _contentWidth = value;
        dispatchPropertyChangeEvent("contentWidth", oldValue, value);        
    }

    //----------------------------------
    //  contentHeight
    //---------------------------------- 
    
    private var _contentHeight:Number = 0;
    
    [Bindable("propertyChange")]
    [Inspectable(category="General")]    

    /**
     *  @copy spark.core.IViewport#contentHeight
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get contentHeight():Number 
    {
        return _contentHeight;
    }
    
    /**
     *  @private
     */
    private function setContentHeight(value:Number):void 
    {            
        if (value == _contentHeight)
            return;
        var oldValue:Number = _contentHeight;
        _contentHeight = value;
        dispatchPropertyChangeEvent("contentHeight", oldValue, value);        
    }    

    /**
     *  Sets the <code>contentWidth</code> and <code>contentHeight</code>
     *  properties.
     * 
     *  This method is intended for layout class developers who should
     *  call it from <code>updateDisplayList()</code> methods.
     *
     *  @param width The new value of <code>contentWidth</code>.
     * 
     *  @param height The new value of <code>contentHeight</code>.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function setContentSize(width:Number, height:Number):void
    {
        if ((width == _contentWidth) && (height == _contentHeight))
           return;
        setContentWidth(width);
        setContentHeight(height);
    }

    //--------------------------------------------------------------------------
    //
    //  Overridden methods: EventDispatcher
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     *  We render a transparent background fill by default when we have mouse
     *  listeners.
     */
    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);

        // This code below is not really accurate for two reasons:
        // 1) MouseEvents bubble so someone up the chain
        //    may be listening for this event, and we woudln't 
        //    detect it here.
        // 2) addEventListener()/removeEventListener() may be called 
        //    multiple times with the same handler and may have no 
        //    real effect on the component, but we're still incrementing
        //    and decrementing the mouseEventReferenceCount here
        //  Neither of these issues seem worth fixing as there aren't 
        //  really great fixes for them, so it's just something we will 
        //  document and live with for now.
        
        // mouse events, then touch events, then gesture events
        switch (type)
        {
            
            case MouseEvent.CLICK:
            case MouseEvent.DOUBLE_CLICK:
            case MouseEvent.MOUSE_DOWN:
            case MouseEvent.MOUSE_MOVE:
            case MouseEvent.MOUSE_OVER:
            case MouseEvent.MOUSE_OUT:
            case MouseEvent.ROLL_OUT:
            case MouseEvent.ROLL_OVER:
            case MouseEvent.MOUSE_UP:
            case MouseEvent.MOUSE_WHEEL:
            case TouchEvent.TOUCH_BEGIN:
            case TouchEvent.TOUCH_END:
            case TouchEvent.TOUCH_MOVE:
            case TouchEvent.TOUCH_OUT:
            case TouchEvent.TOUCH_OVER:
            case TouchEvent.TOUCH_ROLL_OUT:
            case TouchEvent.TOUCH_ROLL_OVER:
            case TouchEvent.TOUCH_TAP:
            case GestureEvent.GESTURE_TWO_FINGER_TAP:
            case PressAndTapGestureEvent.GESTURE_PRESS_AND_TAP:
            case TransformGestureEvent.GESTURE_PAN:
            case TransformGestureEvent.GESTURE_ROTATE:
            case TransformGestureEvent.GESTURE_SWIPE:
            case TransformGestureEvent.GESTURE_ZOOM:
                if (mouseEventReferenceCount++ == 0)
                    hasMouseListeners = true;
        }
    }

    /**
     *  @private
     *  We no longer render our default transparent background fill when we have 
     *  no mouse listeners.
     */
    override public function removeEventListener( type:String, listener:Function,
        useCapture:Boolean = false):void
    {
        super.removeEventListener(type, listener, useCapture);

        // see comment above in addEventListener()
        // mouse events, then touch events, then gesture events
        switch (type)
        {
            
            case MouseEvent.CLICK:
            case MouseEvent.DOUBLE_CLICK:
            case MouseEvent.MOUSE_DOWN:
            case MouseEvent.MOUSE_MOVE:
            case MouseEvent.MOUSE_OVER:
            case MouseEvent.MOUSE_OUT:
            case MouseEvent.ROLL_OUT:
            case MouseEvent.ROLL_OVER:
            case MouseEvent.MOUSE_UP:
            case MouseEvent.MOUSE_WHEEL:
            case TouchEvent.TOUCH_BEGIN:
            case TouchEvent.TOUCH_END:
            case TouchEvent.TOUCH_MOVE:
            case TouchEvent.TOUCH_OUT:
            case TouchEvent.TOUCH_OVER:
            case TouchEvent.TOUCH_ROLL_OUT:
            case TouchEvent.TOUCH_ROLL_OVER:
            case TouchEvent.TOUCH_TAP:
            case GestureEvent.GESTURE_TWO_FINGER_TAP:
            case PressAndTapGestureEvent.GESTURE_PRESS_AND_TAP:
            case TransformGestureEvent.GESTURE_PAN:
            case TransformGestureEvent.GESTURE_ROTATE:
            case TransformGestureEvent.GESTURE_SWIPE:
            case TransformGestureEvent.GESTURE_ZOOM:
                if (--mouseEventReferenceCount == 0)
                    hasMouseListeners = false;
        }
    }
    
    /**
     *  @private
     *  Automation requires a version of addEventListener that does not
     *  affect behavior of the underlying component.
     */  
    mx_internal function $addEventListener(
                            type:String, listener:Function,
                            useCapture:Boolean = false,
                            priority:int = 0,
                            useWeakReference:Boolean = false):void
    {
        super.addEventListener(type, listener, useCapture,
                               priority, useWeakReference);
    }

    /**
     *  @private
     *  Automation requires a version of removeEventListener that does not
     *  affect behavior of the underlying component.
     */  
    mx_internal function $removeEventListener(
                              type:String, listener:Function,
                              useCapture:Boolean = false):void
    {
        super.removeEventListener(type, listener, useCapture);
    }
           
    //--------------------------------------------------------------------------
    //
    //  Properties: Overridden Focus management
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  focusPane
    //----------------------------------

    /**
     *  @private
     *  Storage for the focusPane property.
     */
    private var _focusPane:Sprite;

    [Inspectable(environment="none")]

    /**
     *  @private
     */
    override public function get focusPane():Sprite
    {
        return _focusPane;
    }

    /**
     *  @private
     */
    override public function set focusPane(value:Sprite):void
    {
        if (value)
        {
            overlay.addDisplayObject(value, OverlayDepth.FOCUS_PANE);

            value.x = 0;
            value.y = 0;
            value.scrollRect = null;

            _focusPane = value;
        }
        else
        {
            overlay.removeDisplayObject(_focusPane);
             
            // TODO (jszeto): remove mask?  SDK-15310
            _focusPane = null;
        }
    }
    
    //--------------------------------------------------------------------------
    //
    //  Layout item iteration
    //
    //  Iterators used by Layout objects. For visual items, the layout item
    //  is the item itself. For data items, the layout item is the item renderer
    //  instance that is associated with the item.
    //
    //  These methods and getters are really abstract methods that are 
    //  implemented in Group and DataGroup.  We need them here in BaseGroup 
    //  so that layouts can use these methods.
    //--------------------------------------------------------------------------
    
    /**
     *  @copy mx.core.IVisualElementContainer#numElements
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get numElements():int
    {
        return -1;
    }
    
    /**
     *  @copy mx.core.IVisualElementContainer#getElementAt()
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function getElementAt(index:int):IVisualElement
    {
        return null;
    }
    
    /**
     *  Layouts that honor the <code>useVirtualLayout</code> flag will use this 
     *  method at updateDisplayList() time to get layout elements that are "in view", 
     *  i.e. that overlap the Group's scrollRect.
     * 
     *  <p>If the element to be returned wasn't already a visible child, i.e. if 
     *  it was created or recycled, and either eltWidth or eltHeight is specified,
     *  then the element's initial size is set with setLayoutBoundsSize() before 
     *  it's validated.  This is important for components, like text, that reflow 
     *  when the layout is justified to the Group's width or height.</p>
     *  
     *  <p>The returned layout element will have been validated.</p>
     * 
     *  <p>This method will lazily create or "recycle" and validate layout
     *  elements as needed.</p>
     * 
     *  <p>This method is not intended to be called directly, layouts that
     *  support virutalization will call it.</p>
     * 
     *  @param index The index of the element to retrieve.
     *  @param eltWidth If specified, the newly created or recycled element's initial width.
     *  @param eltHeight If specified, the newly created or recycled element's initial height.
     *  @return The validated element at the specified index.
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function getVirtualElementAt(index:int, eltWidth:Number=NaN, eltHeight:Number=NaN):IVisualElement
    {
        return getElementAt(index);            
    }
    
    /**
     *  @copy mx.core.IVisualElementContainer#getElementIndex()
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function getElementIndex(element:IVisualElement):int
    {
        return -1;
    }
    
    /**
     *  Determines whether the specified IVisualElement is a 
     *  child of the container instance or the instance
     *  itself. The search is deep, i.e. if the element is
     *  a child, grandchild, great-grandchild, etc., of this
     *  container, this returns true.
     *  
     *  @param element the child object to test
     *  @return true if the element is a descendant of the container
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 2.5
     *  @productversion Flex 4.5
     * 
     */    
    public function containsElement(element:IVisualElement):Boolean
    {
        while (element)
        {
            if (element == this)
                return true;
            
            if (element.parent is IVisualElement)
                element = IVisualElement(element.parent);
            else
                return false;
        }
        
        return false;
    }
    
    //----------------------------------
    //  mask
    //----------------------------------
    
    private var _mask:DisplayObject;
    mx_internal var maskChanged:Boolean;
    
    [Inspectable(category="General")]
    
    /**
     *  Sets the mask. The mask will be added to the display list. The mask must
     *  not already on a display list nor in the elements array.  
     *
     *  @see flash.display.DisplayObject#mask
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */ 
    override public function get mask():DisplayObject
    {
        return _mask;
    }
    
    /**
     *  @private
     */ 
    override public function set mask(value:DisplayObject):void
    {
        if (_mask !== value)
        {
            if (_mask && _mask.parent === this)
            {
                overlay.removeDisplayObject(_mask);
            }     
            
            _mask = value;
            maskChanged = true;
            invalidateDisplayList();            
        }
        super.mask = value;         
    } 
    
    //----------------------------------
    //  maskType
    //----------------------------------
    
    private var _maskType:String = MaskType.CLIP;
    private var maskTypeChanged:Boolean;
    private var originalMaskFilters:Array;
    
    [Bindable("propertyChange")]
    [Inspectable(category="General", enumeration="clip,alpha,luminosity", defaultValue="clip")]
    
    /**
     *  <p>The mask type. Possible values are <code>MaskType.CLIP</code>, <code>MaskType.ALPHA</code> 
     *  or <code>MaskType.LUMINOSITY</code>.</p> 
     *
     *  <p><b>Clip Masking</b></p>
     * 
     *  <p>When masking in clip mode, a clipping masks is reduced to 1-bit.  This means that a mask will 
     *  not affect the opacity of a pixel in the source content; it either leaves the value unmodified, 
     *  if the corresponding pixel in the mask is has a non-zero alpha value, or makes it fully 
     *  transparent, if the mask pixel value has an alpha value of zero.</p>
     * 
     *  <p><b>Alpha Masking</b></p>
     * 
     *  <p>In alpha mode, the opacity of each pixel in the source content is multiplied by the opacity 
     *  of the corresponding region of the mask.  i.e., a pixel in the source content with an opacity of 
     *  1 that is masked by a region of opacity of .5 will have a resulting opacity of .5.  A source pixel 
     *  with an opacity of .8 masked by a region with opacity of .5 will have a resulting opacity of .4.</p>
     * 
     *  <p><b>Luminosity Masking</b></p>
     * 
     *  <p>A luminosity mask, sometimes called a 'soft mask', works very similarly to an alpha mask
     *  except that both the opacity and RGB color value of a pixel in the source content is multiplied
     *  by the opacity and RGB color value of the corresponding region in the mask.</p>
     * 
     *  <p>Luminosity masking is not native to Flash but is common in Adobe Creative Suite tools like Adobe 
     *  Illustrator and Adobe Photoshop. In order to accomplish the visual effect of a luminosity mask in 
     *  Flash-rendered content, a graphic element specifying a luminosity mask actually instantiates a shader
     *  filter that mimics the visual look of a luminosity mask as rendered in Adobe Creative Suite tools.</p>
     * 
     *  <p>Objects being masked by luminosity masks can set properties to control the RGB color value and 
     *  clipping of the mask. See the luminosityInvert and luminosityClip attributes.</p>
     * 
     *  @default MaskType.CLIP 
     *
     *  @see  spark.core.MaskType
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get maskType():String
    {
        return _maskType;
    }
    
    /**
     *  @private
     */
    public function set maskType(value:String):void
    {
        if (_maskType != value)
        {
            _maskType = value;
            maskTypeChanged = true;
            invalidateDisplayList(); 
        }
    }
    
    //----------------------------------
    //  luminosityInvert
    //----------------------------------
    
    /**
     *  @private
     *  Storage for the luminosityInvert property.
     */
    private var _luminosityInvert:Boolean = false; 
    
    /**
     *  @private
     */
    private var luminositySettingsChanged:Boolean;

    [Inspectable(category="General", enumeration="true,false", defaultValue="false")]
    
    /**
     *  A property that controls the calculation of the RGB 
     *  color value of a graphic element being masked by 
     *  a luminosity mask. If true, the RGB color value of a  
     *  pixel in the source content is inverted and multipled  
     *  by the corresponding region in the mask. If false, 
     *  the source content's pixel's RGB color value is used 
     *  directly. 
     * 
     *  @default false 
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get luminosityInvert():Boolean
    {
        return _luminosityInvert;
    }

    /**
     *  @private
     */
    public function set luminosityInvert(value:Boolean):void
    {
        if (_luminosityInvert == value)
            return;

        _luminosityInvert = value;
        luminositySettingsChanged = true;
        invalidateDisplayList(); 
    }

    //----------------------------------
    //  luminosityClip
    //----------------------------------
    
    /**
     *  @private
     *  Storage for the luminosityClip property.
     */
    private var _luminosityClip:Boolean = false; 
    
    [Inspectable(category="General", enumeration="true,false", defaultValue="false")]
    
    /**
     *  A property that controls whether the luminosity 
     *  mask clips the masked content. This property can 
     *  only have an effect if the graphic element has a 
     *  mask applied to it that is of type 
     *  MaskType.LUMINOSITY.  
     * 
     *  @default false 
     *  @see #maskType 
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get luminosityClip():Boolean
    {
        return _luminosityClip;
    }

    /**
     *  @private
     */
    public function set luminosityClip(value:Boolean):void
    {
        if (_luminosityClip == value)
            return;

        _luminosityClip = value;
        luminositySettingsChanged = true;
        invalidateDisplayList();
    }
    
   /**
     *  @private
     * 
     *  A simple insertion sort.  This works well for small lists (under 12 or so), uses
     *  no aditional memory, and most importantly, is stable, meaning items with comparable
     *  values will stay in the same order relative to each other. For layering, we guarantee
     *  first the layer property, and then the item order, so a stable sort is important (and the 
     *  built in flash sort is not stable).
     */
    mx_internal static function sortOnLayer(a:Vector.<IVisualElement>):void
    {
        var len:Number = a.length;
        var tmp:IVisualElement;
        if (len<= 1)
            return;
        for (var i:int = 1;i<len;i++)
        {
            for (var j:int = i;j > 0;j--)
            {
                if ( a[j].depth < a[j-1].depth )
                {
                    tmp = a[j];
                    a[j] = a[j-1];
                    a[j-1] = tmp;
                }
                else
                    break;
            }
        }
    }
    
}

}
