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

package spark.components.supportClasses
{

import flash.accessibility.Accessibility;
import flash.accessibility.AccessibilityProperties;
import flash.display.DisplayObject;
import flash.display.InteractiveObject;
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.MouseEvent;
import flash.events.SoftKeyboardEvent;
import flash.system.Capabilities;

import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.events.SelectionEvent;

import mx.core.FlexGlobals;
import mx.core.IIMESupport;
import mx.core.IVisualElement;
import mx.core.InteractionMode;
import mx.core.mx_internal;
import mx.events.FlexEvent;
import mx.events.SandboxMouseEvent;
import mx.events.TouchInteractionEvent;
import mx.managers.IFocusManagerComponent;
import mx.utils.BitFlagUtil;

import spark.components.Application;
import spark.components.RichEditableText;
import spark.components.TextSelectionHighlighting;
import spark.core.IDisplayText;
import spark.core.IEditableText;
import spark.core.ISoftKeyboardHintClient;
import spark.events.TextOperationEvent;

use namespace mx_internal;

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

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

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

/**
 *  The color of the border for this component.
 * 
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[Style(name="borderColor", type="uint", format="Color", inherit="no", theme="spark")]

/**
 *  Controls the visibility of the border for this component.
 * 
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[Style(name="borderVisible", type="Boolean", inherit="no", theme="spark, mobile")]

/**
 *  The alpha of the content background for this component.
 * 
 *  @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")]

/**
 *  @copy spark.components.supportClasses.GroupBase#style:contentBackgroundColor
 *  
 *  @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 of the focus ring for this component.
 * 
 *  @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")]

/**
 *  @copy spark.components.supportClasses.GroupBase#style:focusColor
 *  
 *  @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")]

/**
 *  Controls the visibility of prompt text for this component when it is empty
 *  and focused.
 * 
 *  @langversion 3.0
 *  @playerversion Flash 10.2
 *  @playerversion AIR 2.5
 *  @productversion Flex 4.6
 */
[Style(name="showPromptWhenFocused", type="Boolean", inherit="yes", theme="mobile")]

//--------------------------------------
//  Events
//--------------------------------------

/**
 *  Dispached after the <code>selectionAnchorPosition</code> and/or
 *  <code>selectionActivePosition</code> properties have changed
 *  for any reason.
 *
 *  <p><b>For the Mobile theme, this is not dispatched.</b></p>
 * 
 *  @eventType mx.events.FlexEvent.SELECTION_CHANGE
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[Event(name="selectionChange", type="mx.events.FlexEvent")]

/**
 *  Dispatched before a user editing operation occurs.
 *  You can alter the operation, or cancel the event
 *  to prevent the operation from being processed.
 *
 *  <p><b>For the Mobile theme, this is not dispatched.</b></p>
 * 
 *  @eventType spark.events.TextOperationEvent.CHANGING
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[Event(name="changing", type="spark.events.TextOperationEvent")]

/**
 *  Dispatched after a user editing operation is complete.
 *
 *  @eventType spark.events.TextOperationEvent.CHANGE
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[Event(name="change", type="spark.events.TextOperationEvent")]

/**
 *  Dispatched when a keystroke is about to be input to
 *  the component.
 *
 *  <p><b>For the Mobile theme, this is not dispatched.</b></p>
 * 
 *  @eventType flash.events.TextEvent.TEXT_INPUT
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[Event(name="textInput", type="flash.events.TextEvent")]

//--------------------------------------
//  Skin states
//--------------------------------------

/**
 *  Normal state.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[SkinState("normal")]

/**
 *  Disabled state.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[SkinState("disabled")]

/**
 *  Normal state with prompt.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10.2
 *  @playerversion AIR 2.5
 *  @productversion Flex 4.5
 */
[SkinState("normalWithPrompt")]

/**
 *  Disabled state with prompt.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10.2
 *  @playerversion AIR 2.5
 *  @productversion Flex 4.5
 */
[SkinState("disabledWithPrompt")]

/**
 *  The base class for skinnable components, such as the Spark TextInput
 *  and TextArea, that include an instance of IEditableText in their skin
 *  to provide text display, scrolling, selection, and editing.
 *  By default, IEditableText is RichEditableText for the Spark theme and StyleableStageText
 *  for the Mobile theme. StyleableTextField is also available for the Mobile theme.
 *  
 *  @mxml
 *
 *  <p>The <code>&lt;s:SkinnableTextBase&gt;</code> tag inherits all of the tag 
 *  attributes of its superclass and adds the following tag attributes:</p>
 *
 *  <pre>
 *  &lt;s:SkinnableTextBase
 *    autoCapitalize="none"  <b>[Mobile theme only]</b>
 *    autoCorrect="true"  <b>[Mobile theme only]</b>
 *    displayAsPassword="false"
 *    editable="true"
 *    imeMode="null"  <b>[Spark theme only]</b>
 *    maxChars="0"
 *    prompt="null"
 *    restrict="null"
 *    returnLabelKey="default"  <b>[Mobile theme only]</b>
 *    selectable="true"
 *    selectionHighlighting="whenFocused"  <b>[Spark theme only]</b>
 *    softKeyBoardType="default"  <b>[Mobile theme only]</b>
 *    text=""
 *    typicalText="null"  <b>[Spark theme only]</b>
 *  
 *    <strong>Styles</strong>
 *    borderAlpha="1.0"
 *    borderColor="0x696969"
 *    borderVisible="true"
 *    contentBackgroundAlpha="1.0" 
 *    contentBackgroundColor="0xFFFFFF"
 *    focusAlpha="0.55"
 *    focusColor="0x70B2EE"
 *    showPromptWhenFocused="true"
 * 
 *    <strong>Events</strong>
 *    change="<i>No default</i>"
 *    changing="<i>No default</i>"  <b>[Spark theme only]</b>
 *    selectionChange="<i>No default</i>"  <b>[Spark theme only]</b>
 *    textInput="<i>No default</i>"  <b>[Spark theme only]</b>
 *  /&gt;
 *  </pre>
 *
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
public class SkinnableTextBase extends SkinnableComponent 
    implements IFocusManagerComponent, IIMESupport, ISoftKeyboardHintClient
{
    include "../../core/Version.as";
    
    //--------------------------------------------------------------------------
    //
    //  Class constants
    //
    //--------------------------------------------------------------------------
    
    /**
     *  @private
     */
    private static const CONTENT_PROPERTY_FLAG:uint = 1 << 0;

    /**
     *  @private
     */
    private static const DISPLAY_AS_PASSWORD_PROPERTY_FLAG:uint = 1 << 1;
    
    /**
     *  @private
     */
    private static const EDITABLE_PROPERTY_FLAG:uint = 1 << 2;
        
    /**
     *  @private
     */
    private static const HEIGHT_IN_LINES_PROPERTY_FLAG:uint = 1 << 3;
    
    /**
     *  @private
     */
    private static const IME_MODE_PROPERTY_FLAG:uint = 1 << 4;
    
    /**
     *  @private
     */
    private static const MAX_CHARS_PROPERTY_FLAG:uint = 1 << 5;
       
    /**
     *  @private
     */
    private static const MAX_WIDTH_PROPERTY_FLAG:uint = 1 << 7;
    
    /**
     *  @private
     */
    private static const RESTRICT_PROPERTY_FLAG:uint = 1 << 8;

    /**
     *  @private
     */
    private static const SELECTABLE_PROPERTY_FLAG:uint = 1 << 9;

    /**
     *  @private
     */
    private static const SELECTION_HIGHLIGHTING_FLAG:uint = 1 << 10;

    /**
     *  @private
     */
    private static const TEXT_PROPERTY_FLAG:uint = 1 << 11;

    /**
     *  @private
     */
    private static const TEXT_FLOW_PROPERTY_FLAG:uint = 1 << 12;
    
    /**
     *  @private
     */
    private static const TYPICAL_TEXT_PROPERTY_FLAG:uint = 1 << 13;
    
    /**
     *  @private
     */
    private static const WIDTH_IN_CHARS_PROPERTY_FLAG:uint = 1 << 14;
    
    /**
     *  @private
     */
    private static const AUTO_CAPITALIZE_FLAG:uint = 1 << 15;
    
    /**
     *  @private
     */
    private static const AUTO_CORRECT_FLAG:uint = 1 << 16;
    
    /**
     *  @private
     */
    private static const RETURN_KEY_LABEL_FLAG:uint = 1 << 17;
    
    /**
     *  @private
     */
    private static const SOFT_KEYBOARD_TYPE_FLAG:uint = 1 << 18;

    /**
     *  @private
     */
    private static const PROMPT_TEXT_PROPERTY_FLAG:uint = 1;
    
    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    /**
     *  Constructor. 
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */    
    public function SkinnableTextBase()
    {
        super();
    }
    
    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //-------------------------------------------------------------------------- 

    /**
     *  @private
     */  
    private var touchHandlersAdded:Boolean = false;
    
    /**
     *  @private
     *  True if we received a mouseDown and we haven't receieved a mouseUp yet
     */
    private var isMouseDown:Boolean = false;
    
    /**
     *  @private
     *  True if setFocus is called while isMouseDown is true
     */
    private var delaySetFocus:Boolean = false;
    
    /**
     *  @private
     *  The target from the current mouseDown event
     */
    private var mouseDownTarget:InteractiveObject;
    
    /**
     *  @private
     *  Variable that determines whether this application is running on iOS.
     */
    private static var isIOS:Boolean = (Capabilities.version.indexOf("IOS") == 0);
    
    //--------------------------------------------------------------------------
    //
    //  Skin parts
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  promptDisplay
    //----------------------------------
    
    [SkinPart(required="false")]
    
    /**
     *  The Label or other IDisplayText that might be present
     *  in any skin assigned to this component.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public var promptDisplay:IDisplayText;
    
    /**
     *  @private
     *  Properties are proxied to promptDisplay.  However, when 
     *  promptDisplay is not around, we need to store values set on 
     *  SkinnableTextBase.  This object stores those values.  If promptDisplay is 
     *  around, the values are  stored  on the promptDisplay directly.  However, 
     *  we need to know what values have been set by the developer on 
     *  TextInput/TextArea (versus set on the promptDisplay or defaults of the 
     *  promptDisplay) as those are values we want to carry around if the 
     *  promptDisplay changes (via a new skin). In order to store this info 
     *  efficiently, promptDisplayProperties becomes a uint to store a series of 
     *  BitFlags.  These bits represent whether a property has been explicitly 
     *  set on this SkinnableTextBase.  When the promptDisplay is not around, 
     *  promptDisplayProperties is a typeless object to store these proxied 
     *  properties.  When promptDisplay is around, promptDisplayProperties stores 
     *  booleans as to whether these properties have been explicitly set or not.
     */
    private var promptDisplayProperties:Object = {};
    
    //----------------------------------
    //  textDisplay
    //----------------------------------

    [Bindable]
    [SkinPart(required="false")]

    /**
     *  The IEditableText that may be present
     *  in any skin assigned to this component.
     *  This is RichEditableText for the Spark theme
     *  and StyleableStageText for the Mobile theme.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public var textDisplay:IEditableText;

    /**
     *  @private
     *  Several properties are proxied to textDisplay.  However, when 
     *  textDisplay is not around, we need to store values set on 
     *  SkinnableTextBase.  This object stores those values.  If textDisplay is 
     *  around, the values are  stored  on the textDisplay directly.  However, 
     *  we need to know what values have been set by the developer on 
     *  TextInput/TextArea (versus set on the textDisplay or defaults of the 
     *  textDisplay) as those are values we want to carry around if the 
     *  textDisplay changes (via a new skin). In order to store this info 
     *  efficiently, textDisplayProperties becomes a uint to store a series of 
     *  BitFlags.  These bits represent whether a property has been explicitly 
     *  set on this SkinnableTextBase.  When the  textDisplay is not around, 
     *  textDisplayProperties is a typeless object to store these proxied 
     *  properties.  When textDisplay is around, textDisplayProperties stores 
     *  booleans as to whether these properties have been explicitly set or not.
     */
    private var textDisplayProperties:Object = {};
   
    //--------------------------------------------------------------------------
    //
    //  Overridden properties: UIComponent
    //
    //--------------------------------------------------------------------------
    
    /*
    
    Note:
    
    SkinnableTextBase does not have an accessibilityImplementation
    because, if it does, the Flash Player doesn't support text-selection
    accessibility. The selectionAnchorIndex and selectionActiveIndex
    getters of the acc impl don't get called, probably because Player 10.1
    doesn't like the fact that stage.focus is the texDisplay:RichEditableText
    part instead of the SinnableTextBase component (i.e., the TextInput
    or TextArea).
    
    Instead, we rely on the RichEditableTextAccImpl of the textDisplay
    to provide accessibility.
    
    However, developers expect to be able to control accessibility by setting
    accessibilityProperties, accessibilityName, accessibilityDescription,
    tabIndex, etc. on the component itself.
    
    In order to make these settings usable by RichEditableTextAccImpl,
    we push them down into the accessibilityProperties of the RichEditableText,
    using the invalidateProperties() / commitProperties() pattern.
    
    */

    //----------------------------------
    //  accessibilityEnabled
    //----------------------------------
    
    /**
     *  @private
     */
    override public function set accessibilityEnabled(value:Boolean):void
    {
        if (!Capabilities.hasAccessibility)
            return;
            
        if (!accessibilityProperties)
            accessibilityProperties = new AccessibilityProperties();
        
        accessibilityProperties.silent = !value;
        accessibilityPropertiesChanged = true;
        
        invalidateProperties();
    }
    
    //----------------------------------
    //  accessibilityDescription
    //----------------------------------
    
    /**
     *  @private
     */
    override public function set accessibilityDescription(value:String):void
    {
        if (!Capabilities.hasAccessibility)
            return;
        
        if (!accessibilityProperties)
            accessibilityProperties = new AccessibilityProperties();
        
        accessibilityProperties.description = value;
        accessibilityPropertiesChanged = true;
        
        invalidateProperties();
    }
    
    //----------------------------------
    //  accessibilityName
    //----------------------------------
    
    /**
     *  @private
     */
    override public function set accessibilityName(value:String):void 
    {
        if (!Capabilities.hasAccessibility)
            return;
        
        if (!accessibilityProperties)
            accessibilityProperties = new AccessibilityProperties();
        
        accessibilityProperties.name = value;
        accessibilityPropertiesChanged = true;
        
        invalidateProperties();
    }
    
    //----------------------------------
    //  accessibilityProperties
    //----------------------------------
    
    /**
     *  @private
     *  Storage for the accessibilityProperties property.
     */
    private var _accessibilityProperties:AccessibilityProperties = null;

    /**
     *  @private
     */
    private var accessibilityPropertiesChanged:Boolean = false;
    
    /**
     *  @private
     */
    override public function get accessibilityProperties():AccessibilityProperties
    {
        return _accessibilityProperties;
    }
    
    /**
     *  @private
     */
    override public function set accessibilityProperties(
                                    value:AccessibilityProperties):void
    {
        _accessibilityProperties = value;
        accessibilityPropertiesChanged = true;
        
        invalidateProperties();
    }
    
    //----------------------------------
    //  accessibilityShortcut
    //----------------------------------
    
    /**
     *  @private
     */
    override public function set accessibilityShortcut(value:String):void
    {
        if (!Capabilities.hasAccessibility)
            return;     
        
        if (!accessibilityProperties)
            accessibilityProperties = new AccessibilityProperties();
        
        accessibilityProperties.shortcut = value;
        accessibilityPropertiesChanged = true;
        
        invalidateProperties();
    }

    //----------------------------------
    //  baselinePosition
    //----------------------------------
    
    /**
     *  @private
     */
    override public function get baselinePosition():Number
    {
        return getBaselinePositionForPart(textDisplay as IVisualElement);
    }
    
    //----------------------------------
    //  enabled
    //----------------------------------
    
    /**
     *  @private
     */
    override public function set enabled(value:Boolean):void
    {
        if (textDisplay)
            textDisplay.enabled = value;
        
        if (value == super.enabled)
            return;
        
        super.enabled = value;
    }

    //----------------------------------
    //  maxWidth
    //----------------------------------

    /**
     *  @private
     */
    override public function get maxWidth():Number
    {
        var richEditableText:RichEditableText = textDisplay as RichEditableText;

        if (richEditableText)
            return richEditableText.maxWidth;
            
        // want the default to be default max width for UIComponent
        var v:* = textDisplay ? undefined : textDisplayProperties.maxWidth;
        return (v === undefined) ? super.maxWidth : v;        
    }

    /**
     *  @private
     */
    override public function set maxWidth(value:Number):void
    {
        if (textDisplay)
        {
            var richEditableText:RichEditableText = textDisplay as RichEditableText;
            
            if (richEditableText)
                richEditableText.maxWidth = value;
            else
                super.maxWidth = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), MAX_WIDTH_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.maxWidth = value;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }

    //----------------------------------
    //  prompt
    //----------------------------------
    
    [Inspectable(category="General", defaultValue="")]

    /**
     *  Text to be displayed if/when the actual text property is an empty string.
     * 
     *  <p>Prompt text appears when the text control is first created. 
     *  Prompt text disappears when the control gets focus or when the control’s 
     *  <code>text</code> property is a non-empty string. 
     *  Prompt text reappears when the control loses focus, but only if no text was entered 
     *  (if the value of the text field is the empty string).</p>
     *  
     *  <p>For text controls, if the user enters text, but later deletes it, the prompt text 
     *  reappears when the control loses focus. 
     *  You can also cause the prompt text to reappear programmatically by setting the 
     *  text control’s text property to the empty string.</p>
     *  
     *  <p>You can change the style of the prompt text with CSS. 
     *  If the control has prompt text and is not disabled, the style is defined by the 
     *  <code>normalWithPrompt</code> pseudo selector. 
     *  If the control is disabled, then the styles defined by the <code>disabledWithPrompt</code> 
     *  pseudo selector are used.</p>
     *  
     *  <p>The following example CSS changes the color of the prompt text in controls that 
     *  sub-class SkinnableTextBase (this includes the Spark TextInput and TextArea controls):
     *  <pre>
     *  &#64;namespace s "library://ns.adobe.com/flex/spark";
     *  s|SkinnableTextBase:normalWithPrompt {
     *      color: #CCCCFF;
     *  }
     *  </pre>
     *  </p>
     *
     *  @default null
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.0
     *  @productversion Flex 4.5
     */
    public function get prompt():String
    {
        return getPrompt();
    }
    
    /**
     *  @private
     */
    public function set prompt(value:String):void
    {
        setPrompt(value);
    }
    
    //----------------------------------
    //  tabIndex
    //----------------------------------

    /**
     *  @private
     *  Storage for the tabIndex property.
     */
    private var _tabIndex:int = -1;
    
    /**
     *  @private
     */
    override public function get tabIndex():int
    {
        return _tabIndex;
    }
    
    /**
     *  @private
     */
    override public function set tabIndex(
                                    value:int):void
    {
        _tabIndex = value;
        accessibilityPropertiesChanged = true;
        
        invalidateProperties();
    }
    
    //----------------------------------
    //  typicalText
    //----------------------------------
    
    [Inspectable(category="General", defaultValue="null")]

    /**
     *  Text that is used to determine
     *  the default width and height of the control, 
     *  without actually being displayed.
     * 
     *  <p><b>For the Spark theme, see
     *  spark.components.RichEditableText.typicalText</b></p>
     *
     *  <p><b>For the Mobile theme, this is not supported.</b></p>
     * 
     *  @see spark.components.RichEditableText#typicalText
     * 
     *  @default null
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get typicalText():String
    {
        return getTypicalText();
    }
    
    /**
     *  @private
     */
    public function set typicalText(value:String):void
    {
        setTypicalText(value);
    }
    
    //--------------------------------------------------------------------------
    //
    //  Properties proxied to textDisplay
    //
    //--------------------------------------------------------------------------
    
    //----------------------------------
    //  displayAsPassword
    //----------------------------------
    
    [Inspectable(category="General", defaultValue="false")]

    /**
     *  @copy flash.text.TextField#displayAsPassword
     * 
     *  @default false
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get displayAsPassword():Boolean
    {
        if (textDisplay)
            return textDisplay.displayAsPassword;

        // want the default to be false
        var v:* = textDisplayProperties.displayAsPassword
        return (v === undefined) ? false : v;
    }

    /**
     *  @private
     */
    public function set displayAsPassword(value:Boolean):void
    {
        if (textDisplay)
        {
            textDisplay.displayAsPassword = value;
            textDisplayProperties = BitFlagUtil.update(
                                    uint(textDisplayProperties), 
                                    DISPLAY_AS_PASSWORD_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.displayAsPassword = value;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }

    //----------------------------------
    //  editable
    //----------------------------------

    [Inspectable(category="General", defaultValue="true")]

    /**
     *  Specifies whether the text is editable.
     * 
     *  <p><b>For the Spark theme, see
     *  spark.components.RichEditableText.editable</b></p>
     *
     *  <p><b>For the Mobile theme, see
     *  spark.components.supportClasses.StyleableStageText.editable</b></p>
     * 
     *  @default true
     * 
     *  @see spark.components.RichEditableText#editable
     *  @see spark.components.supportClasses.StyleableStageText#editable
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get editable():Boolean
    {
        if (textDisplay)
            return textDisplay.editable;
            
        // want the default to be true
        var v:* = textDisplayProperties.editable;
        return (v === undefined) ? true : v;
    }

    /**
     *  @private
     */
    public function set editable(value:Boolean):void
    {
        if (textDisplay)
        {
            textDisplay.editable = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), EDITABLE_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.editable = value;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }

    //----------------------------------
    //  enableIME
    //----------------------------------

    /**
     *  A flag that indicates whether the IME should
     *  be enabled when the component receives focus.
     * 
     *  <p><b>For the Mobile theme, this is not supported.</b></p>
     * 
     *  @see spark.components.RichEditableText#enableIME
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get enableIME():Boolean
    {
        var richEditableText:RichEditableText = textDisplay as RichEditableText;
        
        if (richEditableText)        
            return richEditableText.enableIME;
        
        return false;
    }

    //----------------------------------
    //  imeMode
    //----------------------------------

    /**
     *  Specifies the IME (input method editor) mode.
     * 
     *  <p><b>For the Spark theme, see
     *  spark.components.RichEditableText.imeMode</b></p>
     *
     *  <p><b>For the Mobile theme, this is not supported.</b></p>
     * 
     *  @see spark.components.RichEditableText#imeMode
     * 
     *  @default null
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get imeMode():String
    {
        var richEditableText:RichEditableText = textDisplay as RichEditableText;
                
        if (richEditableText)        
            return richEditableText.imeMode;
            
        // want the default to be null
        var v:* = textDisplay ? undefined : textDisplayProperties.imeMode;
        return (v === undefined) ? null : v;
    }

    /**
     *  @private
     */
    public function set imeMode(value:String):void
    {
        var richEditableText:RichEditableText = textDisplay as RichEditableText;

        if (textDisplay)
        {
            if (richEditableText)
                richEditableText.imeMode = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), IME_MODE_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.imeMode = value;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }

    //----------------------------------
    //  maxChars
    //----------------------------------
    
    [Inspectable(category="General", defaultValue="0")]    

    /**
     *  @copy flash.text.TextField#maxChars
     * 
     *  @default 0
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get maxChars():int 
    {
        if (textDisplay)
            return textDisplay.maxChars;
            
        // want the default to be 0
        var v:* = textDisplayProperties.maxChars;
        return (v === undefined) ? 0 : v;
    }
    
    /**
     *  @private
     */
    public function set maxChars(value:int):void
    {
        if (textDisplay)
        {
            textDisplay.maxChars = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), MAX_CHARS_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.maxChars = value;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }

    //----------------------------------
    //  restrict
    //----------------------------------

    [Inspectable(category="General", defaultValue="null")]

    /**
     *  @copy flash.text.TextField#restrict
     * 
     *  @default null
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get restrict():String 
    {
        if (textDisplay)
            return textDisplay.restrict;
            
        // want the default to be null
        var v:* = textDisplayProperties.restrict;
        return (v === undefined) ? null : v;
    }
    
    /**
     *  @private
     */
    public function set restrict(value:String):void
    {
        if (textDisplay)
        {
            textDisplay.restrict = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), RESTRICT_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.restrict = value;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }

    //----------------------------------
    //  selectable
    //----------------------------------

    [Inspectable(category="General", defaultValue="true")]

    /**
     *  A flag indicating whether the content is selectable.  On a Desktop, a user can 
     *  select content with the mouse or with the keyboard when the control has 
     *  keyboard focus.  On a touch interaction device, a user can select text with 
     *  their fingers once they've entered into selection mode for the text component.
     * 
     *  <p><b>For the Spark theme, see
     *  spark.components.RichEditableText.selectable</b></p>
     *
     *  <p><b>For the Mobile theme, see
     *  spark.components.supportClasses.StyleableStageText.selectable</b></p>
     * 
     *  @see spark.components.RichEditableText#selectable
     *  @see spark.components.supportClasses.StyleableStageText#selectable
     *  
     *  @default true
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get selectable():Boolean
    {
        if (textDisplay)
            return textDisplay.selectable;
            
        // want the default to be true
        var v:* = textDisplayProperties.selectable;
        return (v === undefined) ? true : v;
    }

    /**
     *  @private
     */
    public function set selectable(value:Boolean):void
    {
        if (textDisplay)
        {
            textDisplay.selectable = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), SELECTABLE_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.selectable = value;
        }
        
        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }

    //----------------------------------
    //  selectionActivePosition
    //----------------------------------

    /**
     *  @private
     */
    [Bindable("selectionChange")]
    
    /**
     *  A character position, relative to the beginning of the
     *  <code>text</code> String, specifying the end of the selection
     *  that moves when the selection is extended with the arrow keys.
     * 
     *  <p><b>For the Spark theme, see
     *  spark.components.RichEditableText.selectionActivePosition</b></p>
     *
     *  <p><b>For the Mobile theme, see
     *  spark.components.supportClasses.StyleableStageText.selectionActivePosition</b></p>
     * 
     *  @see spark.components.RichEditableText#selectionActivePosition
     *  @see spark.components.supportClasses.StyleableStageText#selectionActivePosition
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get selectionActivePosition():int
    {
        return textDisplay ? textDisplay.selectionActivePosition : -1;
    }

    //----------------------------------
    //  selectionAnchorPosition
    //----------------------------------

    /**
     *  @private
     */
    [Bindable("selectionChange")]
    
    /**
     *  A character position, relative to the beginning of the
     *  <code>text</code> String, specifying the end of the selection
     *  that stays fixed when the selection is extended with the arrow keys.
     * 
     *  <p><b>For the Spark theme, see
     *  spark.components.RichEditableText.selectionAnchorPosition</b></p>
     *
     *  <p><b>For the Mobile theme, see
     *  spark.components.supportClasses.StyleableStageText.selectionAnchorPosition</b></p>
     * 
     *  @see spark.components.RichEditableText#selectionAnchorPosition
     *  @see spark.components.supportClasses.StyleableStageText#selectionAnchorPosition
     *   
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get selectionAnchorPosition():int
    {
        return textDisplay ? textDisplay.selectionAnchorPosition : -1;
    }

    //----------------------------------
    //  selectionHighlighting
    //----------------------------------

    [Inspectable(category="General", enumeration="always,whenActive,whenFocused", defaultValue="whenFocused")]
    
    /**
     *  Determines when the text selection is highlighted.
     * 
     *  <p><b>For the Spark theme, see
     *  spark.components.RichEditableText.selectionHighlighting</b></p>
     *
     *  <p><b>For the Mobile theme, this is not supported.</b></p>
     * 
     *  @see spark.components.RichEditableText#selectionHighlighting
     * 
     *  @default TextSelectionHighlighting.WHEN_FOCUSED
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get selectionHighlighting():String 
    {
        var richEditableText:RichEditableText = textDisplay as RichEditableText;
        
        if (richEditableText)
            return richEditableText.selectionHighlighting;
            
        // want the default to be "when focused"
        var v:* = textDisplay ? undefined : textDisplayProperties.selectionHighlighting;
        return (v === undefined) ? TextSelectionHighlighting.WHEN_FOCUSED : v;
    }
    
    /**
     *  @private
     */
    public function set selectionHighlighting(value:String):void
    {
        if (textDisplay)
        {
            var richEditableText:RichEditableText = textDisplay as RichEditableText;
            
            if (richEditableText)
                richEditableText.selectionHighlighting = value;
            textDisplayProperties = BitFlagUtil.update(
                                    uint(textDisplayProperties), 
                                    SELECTION_HIGHLIGHTING_FLAG, true);
        }
        else
        {
            textDisplayProperties.selectionHighlighting = value;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }

    //----------------------------------
    //  text
    //----------------------------------
    
    [Inspectable(category="General", defaultValue="")]
    
    /**
     *  The text displayed by this text component.
     * 
     *  <p><b>For the Spark theme, see
     *  spark.components.RichEditableText.text</b></p>
     *
     *  <p><b>For the Mobile theme, see
     *  spark.components.supportClasses.StyleableStageText#text</b></p>
     * 
     *  @see spark.components.RichEditableText#text
     *  @see spark.components.supportClasses.StyleableStageText#text
     * 
     *  @default ""
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get text():String
    {
        if (textDisplay)
            return textDisplay.text;
            
        // If there is no textDisplay, it isn't possible to set one of
        // text, textFlow or content and then get it in another form.
                    
        // want the default to be the empty string
        var v:* = textDisplayProperties.text;
        return (v === undefined) ? "" : v;
    }

    /**
     *  @private
     */
    public function set text(value:String):void
    {
        // text should never be null.  Convert null to the empty string.
        
        if (textDisplay)
        {
            textDisplay.text = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), TEXT_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.text = value ? value : "";

            // Of 'text', 'textFlow', and 'content', the last one set wins.  So
            // if we're holding onto the properties until the skin is loaded
            // make sure only the last one set is defined.
            textDisplayProperties.content = undefined;
            textDisplayProperties.textFlow = undefined;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();
        invalidateSkinState();
     }

    //----------------------------------
    //  Mobile soft-keyboard hints
    //----------------------------------
    
    //----------------------------------
    //  autoCapitalize
    //----------------------------------
    
    [Inspectable(category="General",enumeration="none,word,sentence,all",defaultValue="none")]

    /**
     *  Hint indicating what captialization behavior soft keyboards should
     *  use. 
     *
     *  <p>Supported values are defined in flash.text.AutoCapitalize:</p>
     * 
     *  <ul>
     *      <li><code>"none"</code> - no automatic capitalization</li>
     *      <li><code>"word"</code> - capitalize the first letter following any 
     *          space or punctuation</li>
     *      <li><code>"sentence"</code> - captitalize the first letter following 
     *          any period</li>
     *      <li><code>"all"</code> - capitalize every letter</li>
     *  </ul>
     *
     *  <p><b>For the Spark theme, this is not supported.</b></p>
     * 
     *  @default "none"
     * 
     *  @see flash.text.AutoCapitalize
     * 
     *  @langversion 3.0
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function get autoCapitalize():String
    {
        var softKeyboardClient:ISoftKeyboardHintClient = textDisplay as ISoftKeyboardHintClient;
        
        if (softKeyboardClient)
            return softKeyboardClient.autoCapitalize; 
        
        var v:* = textDisplay ? undefined : textDisplayProperties.autoCapitalize;
        return (v === undefined) ? "none" : v;
    }
    
    public function set autoCapitalize(value:String):void
    {
        if (textDisplay)
        {
            var softKeyboardClient:ISoftKeyboardHintClient = textDisplay as ISoftKeyboardHintClient;
            
            if (softKeyboardClient)
                softKeyboardClient.autoCapitalize = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), 
                AUTO_CAPITALIZE_FLAG, true);
        }
        else
        {
            textDisplayProperties.autoCapitalize = value;
        }
        
        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }
    
    //----------------------------------
    //  autoCorrect
    //----------------------------------
    
    [Inspectable(category="General",defaultValue="true")]

    /**
     *  Hint indicating whether a soft keyboard should use its auto-correct
     *  behavior, if supported.
     *  <p><b>For the Spark theme, this is not supported.</b></p>
     * 
     *  @default true
     *  
     *  @langversion 3.0
     *  @playerversion AIR 3.0
     *  @productversion Flex 4.6
     */
    public function get autoCorrect():Boolean
    {
        var softKeyboardClient:ISoftKeyboardHintClient = textDisplay as ISoftKeyboardHintClient;
        
        if (softKeyboardClient)
            return softKeyboardClient.autoCorrect; 
        
        var v:* = textDisplay ? undefined : textDisplayProperties.autoCorrect;
        return (v === undefined) ? true : v;
    }
    
    public function set autoCorrect(value:Boolean):void
    {
        if (textDisplay)
        {
            var softKeyboardClient:ISoftKeyboardHintClient = textDisplay as ISoftKeyboardHintClient;
            
            if (softKeyboardClient)
                softKeyboardClient.autoCorrect = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), 
                AUTO_CORRECT_FLAG, true);
        }
        else
        {
            textDisplayProperties.autoCorrect = value;
        }
        
        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }
    
    //----------------------------------
    //  returnKeyLabel
    //----------------------------------
    
    [Inspectable(category="General",enumeration="default,done,go,next,search",defaultValue="default")]

    /**
     *  Hint indicating what label should be displayed for the return key on
     *  soft keyboards.
     *
     *  <p>Supported values are defined in flash.text.ReturnKeyLabel:</p>
     * 
     *  <ul>
     *      <li><code>"default"</code> - default icon or label text</li>
     *      <li><code>"done"</code> - icon or label text indicating completed 
     *          text entry</li>
     *      <li><code>"go"</code> - icon or label text indicating that an action
     *          should start</li>
     *      <li><code>"next"</code> - icon or label text indicating a move to 
     *          the next field</li>
     *      <li><code>"search"</code> - icon or label text indicating that the 
     *          entered text should be searched for</li>
     *  </ul>
     *
     *  <p><b>For the Spark theme, this is not supported.</b></p>
     * 
     *  @default "default"
     *  
     *  @see flash.text.ReturnKeyLabel
     * 
     *  @langversion 3.0
     *  @playerversion AIR 3.0
     *  @productversion Flex 4.6
     */
    public function get returnKeyLabel():String
    {
        var softKeyboardClient:ISoftKeyboardHintClient = textDisplay as ISoftKeyboardHintClient;
        
        if (softKeyboardClient)
            return softKeyboardClient.returnKeyLabel; 
        
        var v:* = textDisplay ? undefined : textDisplayProperties.returnKeyLabel;
        return (v === undefined) ? "default" : v;
    }
    
    public function set returnKeyLabel(value:String):void
    {
        if (textDisplay)
        {
            var softKeyboardClient:ISoftKeyboardHintClient = textDisplay as ISoftKeyboardHintClient;
            
            if (softKeyboardClient)
                softKeyboardClient.returnKeyLabel = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), 
                RETURN_KEY_LABEL_FLAG, true);
        }
        else
        {
            textDisplayProperties.returnKeyLabel = value;
        }
        
        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }
    
    //----------------------------------
    //  softKeyboardType
    //----------------------------------
    
    [Inspectable(category="General",enumeration="default,punctuation,url,number,contact,email",defaultValue="default")]

    /**
     *  Hint indicating what kind of soft keyboard should be displayed for
     *  this component.
     *
     *  <p>Supported values are defined in flash.text.SoftKeyboardType:</p>
     * 
     *  <ul>
     *      <li><code>"default"</code> - the default keyboard</li>
     *      <li><code>"punctuation"</code> - puts the keyboard into 
     *          punctuation/symbol entry mode</li>
     *      <li><code>"url"</code> - present soft keys appropriate for URL 
     *          entry, such as a specialized key that inserts '.com'</li>
     *      <li><code>"number"</code> - puts the keyboard into numeric keypad
     *          mode</li>
     *      <li><code>"contact"</code> - puts the keyboard into a mode 
     *          appropriate for entering contact information</li>
     *      <li><code>"email"</code> - puts the keyboard into e-mail addres 
     *          entry mode, which may make it easier to enter the at sign or
     *          '.com'</li>
     *  </ul>
     *
     *  <p><b>For the Spark theme, this is not supported.</b></p>
     * 
     *  @default "default" 
     * 
     *  @see flash.text.SoftKeyboardType
     * 
     *  @langversion 3.0
     *  @playerversion AIR 3.0
     *  @productversion Flex 4.6
     */
    public function get softKeyboardType():String
    {
        var softKeyboardClient:ISoftKeyboardHintClient = textDisplay as ISoftKeyboardHintClient;
        
        if (softKeyboardClient)
            return softKeyboardClient.softKeyboardType; 
        
        var v:* = textDisplay ? undefined : textDisplayProperties.softKeyboardType;
        return (v === undefined) ? "default" : v;
    }
    
    public function set softKeyboardType(value:String):void
    {
        if (textDisplay)
        {
            var softKeyboardClient:ISoftKeyboardHintClient = textDisplay as ISoftKeyboardHintClient;
            
            if (softKeyboardClient)
                softKeyboardClient.softKeyboardType = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), 
                SOFT_KEYBOARD_TYPE_FLAG, true);
        }
        else
        {
            textDisplayProperties.softKeyboardType = value;
        }
        
        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }
    
    //--------------------------------------------------------------------------
    //
    //  Overridden methods
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override protected function commitProperties():void
    {
        super.commitProperties();
        
        if (accessibilityPropertiesChanged)
        {
            if (textDisplay)
            {
                textDisplay.accessibilityProperties = _accessibilityProperties;
                textDisplay.tabIndex = _tabIndex;             
                
                // Note: Calling updateProperties() on players that don't
                // support accessibility will throw an RTE.
                if (Capabilities.hasAccessibility)
                    Accessibility.updateProperties();
            }
            
            accessibilityPropertiesChanged = false;
        }
    }
    
    /**
     *  @private
     */
    override public function styleChanged(styleProp:String):void
    {
        super.styleChanged(styleProp);
        
        if (!styleProp ||
            styleProp == "styleName" || styleProp == "interactionMode")
        {
            if (getStyle("interactionMode") == InteractionMode.TOUCH && !touchHandlersAdded)
            {
                addEventListener(MouseEvent.MOUSE_DOWN, touchMouseDownHandler);
                addEventListener(TouchInteractionEvent.TOUCH_INTERACTION_START,
                    touchInteractionStartHandler);
                touchHandlersAdded = true;
            }
            else if (getStyle("interactionMode") == InteractionMode.MOUSE && touchHandlersAdded)
            {
                removeEventListener(MouseEvent.MOUSE_DOWN, touchMouseDownHandler);
                removeEventListener(TouchInteractionEvent.TOUCH_INTERACTION_START,
                    touchInteractionStartHandler);
                touchHandlersAdded = false;
            }
        }
    }

    /**
     *  @private
     */
    override protected function partAdded(partName:String, instance:Object):void
    {
        super.partAdded(partName, instance);

        if (instance == promptDisplay)
        {
            var newPromptDisplayProperties:uint = 0;
            if (promptDisplayProperties.prompt !== undefined)
            {
                promptDisplay.text = promptDisplayProperties.prompt;
                newPromptDisplayProperties = BitFlagUtil.update(
                    uint(newPromptDisplayProperties), PROMPT_TEXT_PROPERTY_FLAG, true);
            }
            promptDisplayProperties = newPromptDisplayProperties;
        }
        
        if (instance == textDisplay)
        {
            // Copy proxied values from textDisplayProperties (if set) to 
            //textDisplay.
            textDisplayAdded();            

            // Focus on this, rather than the inner RET component.
            textDisplay.focusEnabled = false;

            // Start listening for various events from the IEditableText.

            textDisplay.addEventListener(SelectionEvent.SELECTION_CHANGE,
                                         textDisplay_selectionChangeHandler);

            textDisplay.addEventListener(TextOperationEvent.CHANGING, 
                                         textDisplay_changingHandler);

            textDisplay.addEventListener(TextOperationEvent.CHANGE,
                                         textDisplay_changeHandler);

            textDisplay.addEventListener(FlexEvent.ENTER,
                                         textDisplay_enterHandler);

            textDisplay.addEventListener(FlexEvent.VALUE_COMMIT,
                                         textDisplay_valueCommitHandler);
        }
    }

    /**
     *  @private
     */
    override protected function partRemoved(partName:String, 
                                            instance:Object):void
    {
        super.partRemoved(partName, instance);

        if (instance == textDisplay)
        {
            // Copy proxied values from textDisplay (if explicitly set) to 
            // textDisplayProperties.                        
            textDisplayRemoved();            
            
            // Stop listening for various events from the IEditableText.

            textDisplay.removeEventListener(SelectionEvent.SELECTION_CHANGE,
                                            textDisplay_selectionChangeHandler);

            textDisplay.removeEventListener(TextOperationEvent.CHANGING,
                                            textDisplay_changingHandler);

            textDisplay.removeEventListener(TextOperationEvent.CHANGE,
                                            textDisplay_changeHandler);

            textDisplay.removeEventListener(FlexEvent.ENTER,
                                            textDisplay_enterHandler);

            textDisplay.removeEventListener(FlexEvent.VALUE_COMMIT,
                                            textDisplay_valueCommitHandler);
        }
        
        if (instance == promptDisplay)
        {
            var newPromptDisplayProperties:Object = {};
            
            if (BitFlagUtil.isSet(uint(promptDisplayProperties), 
                PROMPT_TEXT_PROPERTY_FLAG))
            {
                newPromptDisplayProperties.prompt = 
                    promptDisplay.text;
            }
            promptDisplayProperties = newPromptDisplayProperties;
        }
    }
    
    /**
     *  @private
     */
    override protected function getCurrentSkinState():String
    {
        var showPromptWhenFocused:Boolean = getStyle("showPromptWhenFocused");
        
        if ((showPromptWhenFocused || 
            focusManager && focusManager.getFocus() != focusManager.findFocusManagerComponent(this)) && 
            prompt != null && prompt != "")
        {
            if (text.length == 0)
            {
                if (enabled && skin && skin.hasState("normalWithPrompt"))
                    return "normalWithPrompt";
                if (!enabled && skin && skin.hasState("disabledWithPrompt"))
                    return "disabledWithPrompt";
            }
        }
        return enabled ? "normal" : "disabled";
    }

    /**
     *  @private
     *  Focus should always be on the internal textDisplay.
     */
    override public function setFocus():void
    {
        // If the mouse is down, then we don't want the TextField to open the soft keyboard until mouse up. 
        // Otherwise, this was called programmatically and we want the soft keyboard to appear immediately.
        // Note that isMouseDown can only be true when we are in InteractionMode == TOUCH. 
        if (textDisplay)
        {
            if (isMouseDown)
            {
                delaySetFocus = true;
                
                // Cancelling an ACTIVATING event will close the softKeyboard if it is 
                // currently active on iOS only. Add a check to only cancel the event
                // if the softKeyboard is not active. Otherwise, the softKeyboard will
                // close if you press on the skin of a text component.
                var topLevelApp:Application = FlexGlobals.topLevelApplication as Application;
                var cancelEvent:Boolean = !(topLevelApp && topLevelApp.isSoftKeyboardActive);
                if (cancelEvent)
                    addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATING, softKeyboardActivatingHandler);
                
                textDisplay.setFocus();
                
                if (cancelEvent)
                    removeEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATING, softKeyboardActivatingHandler);
            }
            else
            {
                textDisplay.setFocus();
            }
        }
    }

    /**
     *  @private
     */
    override protected function isOurFocus(target:DisplayObject):Boolean
    {
        return target == textDisplay || super.isOurFocus(target);
    }
    
    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------
    
    /**
     *  @copy spark.core.IEditableText#insertText()
     *   
     *  @see spark.components.RichEditableText#insertText()
     *  @see spark.components.supportClasses.StyleableStageText#insertText()
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function insertText(text:String):void
    {
        if (!textDisplay)
            return;

        textDisplay.insertText(text);
        
        // This changes text so generate an UPDATE_COMPLETE event.
        invalidateProperties();
    }

    /**
     *  @copy spark.core.IEditableText#appendText()
     *  
     *  @see spark.components.RichEditableText#appendText()
     *  @see spark.components.supportClasses.StyleableStageText#appendText()
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function appendText(text:String):void
    {
        if (!textDisplay)
            return;

        textDisplay.appendText(text);
        
        // This changes text so generate an UPDATE_COMPLETE event.
        invalidateProperties();
    }
    
    /**
     *  @copy spark.core.IEditableText#selectRange()
     *  
     *  @see spark.components.RichEditableText#selectRange()
     *  @see spark.components.supportClasses.StyleableStageText#selectRange()
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function selectRange(anchorIndex:int, activeIndex:int):void
    {
        if (!textDisplay)
            return;

        textDisplay.selectRange(anchorIndex, activeIndex);

        // This changes the selection so generate an UPDATE_COMPLETE event.
        invalidateProperties();
    }

    /**
     *  @copy spark.core.IEditableText#selectAll()
     * 
     *  @see spark.components.RichEditableText#selectAll()
     *  @see spark.components.supportClasses.StyleableStageText#selectAll()
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public function selectAll():void
    {
        if (!textDisplay)
            return;

        textDisplay.selectAll();

        // This changes the selection so generate an UPDATE_COMPLETE event.
        invalidateProperties();
    }

    /**
     *  @private
     */
    mx_internal function setContent(value:Object):void
    {        
        if (textDisplay)
        {
            var richEditableText:RichEditableText = textDisplay as RichEditableText;
            
            if (richEditableText)
            {
                richEditableText.content = value;
                textDisplayProperties = BitFlagUtil.update(
                    uint(textDisplayProperties), CONTENT_PROPERTY_FLAG, true);
            }
        }
        else
        {
            textDisplayProperties.content = value;
            
            // Of 'text', 'textFlow', and 'content', the last one set wins.  So
            // if we're holding onto the properties until the skin is loaded
            // make sure only the last one set is defined.
            textDisplayProperties.text = undefined;
            textDisplayProperties.textFlow = undefined;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
     }

    /**
     *  @private
     */
    mx_internal function getHeightInLines():Number
    {
        var richEditableText:RichEditableText = textDisplay as RichEditableText;

        if (richEditableText)
            return richEditableText.heightInLines;
            
        // want the default to be NaN
        var v:* = textDisplay ? undefined : textDisplayProperties.heightInLines;        
        return (v === undefined) ? NaN : v;
    }

    /**
     *  @private
     */
    mx_internal function setHeightInLines(value:Number):void
    {
        if (textDisplay)
        {
            var richEditableText:RichEditableText = textDisplay as RichEditableText;

            if (richEditableText)
                richEditableText.heightInLines = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), 
                HEIGHT_IN_LINES_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.heightInLines = value;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }

    /**
     *  @see #prompt
     *
     *  @private
     */
    mx_internal function getPrompt():String
    {
        if (promptDisplay)
        {
            if (BitFlagUtil.isSet(uint(promptDisplayProperties), 
                PROMPT_TEXT_PROPERTY_FLAG))
                return promptDisplay.text;
            return null;
        }
        
        // want the default to be null
        var v:* = promptDisplay ? undefined : promptDisplayProperties.prompt;
        return (v === undefined) ? null : v;
    }
    
    /**
     *  @private
     */
    mx_internal function setPrompt(value:String):void
    {
        if (promptDisplay)
        {
            promptDisplay.text = value;
            promptDisplayProperties = BitFlagUtil.update(
                uint(promptDisplayProperties), 
                PROMPT_TEXT_PROPERTY_FLAG, true);
        }
        else
            promptDisplayProperties.prompt = value;
        
        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();       
        invalidateSkinState();
    }
    
    /**
     *  @private  
     */
    mx_internal function getTextFlow():TextFlow 
    {
        var richEditableText:RichEditableText = textDisplay as RichEditableText;
        
        if (richEditableText)
            return richEditableText.textFlow;
            
        // If there is no textDisplay, it isn't possible to set one of
        // text, textFlow or content and then get it in another form.

        // want the default to be null
        var v:* = textDisplay ? undefined : textDisplayProperties.textFlow;
        return (v === undefined) ? null : v;
    }
    
    /**
     *  @private
     */
    mx_internal function setTextFlow(value:TextFlow):void
    {
        if (textDisplay)
        {
            var richEditableText:RichEditableText = textDisplay as RichEditableText;
            
            if (richEditableText)
                richEditableText.textFlow = value;
            textDisplayProperties = BitFlagUtil.update(
                                    uint(textDisplayProperties), 
                                    TEXT_FLOW_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.textFlow = value;

            // Of 'text', 'textFlow', and 'content', the last one set wins.  So
            // if we're holding onto the properties until the skin is loaded
            // make sure only the last one set is defined.
            textDisplayProperties.text = undefined;
            textDisplayProperties.content = undefined;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();
        invalidateSkinState();
    }

    /**
     *  @see RichEditableText#typicalText
     *
     *  @private
     */
    mx_internal function getTypicalText():String
    {
        var richEditableText:RichEditableText = textDisplay as RichEditableText;
        
        if (richEditableText)
            return richEditableText.typicalText;
        
        // want the default to be null
        var v:* = textDisplay ? undefined : textDisplayProperties.typicalText;
        return (v === undefined) ? null : v;
    }
    
    /**
     *  @private
     */
    mx_internal function setTypicalText(value:String):void
    {
        if (textDisplay)
        {
            var richEditableText:RichEditableText = textDisplay as RichEditableText;
            
            if (richEditableText)
                richEditableText.typicalText = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), 
                TYPICAL_TEXT_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.typicalText = value;
        }
        
        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }

    /**
     *  The default width for the Text components, measured in characters.
     *  The width of the "M" character is used for the calculation.
     *  So if you set this property to 5, it will be wide enough
     *  to let the user enter 5 ems.
     *
     *  @private
     */
    mx_internal function getWidthInChars():Number
    {
        var richEditableText:RichEditableText = textDisplay as RichEditableText;

        if (richEditableText)
            return richEditableText.widthInChars
            
        // want the default to be NaN
        var v:* = textDisplay ? undefined : textDisplayProperties.widthInChars;
        return (v === undefined) ? NaN : v;
    }

    /**
     *  @private
     */
    mx_internal function setWidthInChars(value:Number):void
    {
        if (textDisplay)
        {
            var richEditableText:RichEditableText = textDisplay as RichEditableText;

            if (richEditableText)
                richEditableText.widthInChars = value;
            textDisplayProperties = BitFlagUtil.update(
                uint(textDisplayProperties), 
                WIDTH_IN_CHARS_PROPERTY_FLAG, true);
        }
        else
        {
            textDisplayProperties.widthInChars = value;
        }

        // Generate an UPDATE_COMPLETE event.
        invalidateProperties();                    
    }
    
    /**
     *  @private
     *  Copy values stored locally into textDisplay now that textDisplay 
     *  has been added.
     */
    private function textDisplayAdded():void
    {        
        var newTextDisplayProperties:uint = 0;
        var richEditableText:RichEditableText = textDisplay as RichEditableText;
        
        if (textDisplayProperties.content !== undefined && richEditableText)
        {
            richEditableText.content = textDisplayProperties.content;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), CONTENT_PROPERTY_FLAG, true);
        }
 
        if (textDisplayProperties.displayAsPassword !== undefined)
        {
            textDisplay.displayAsPassword = 
                textDisplayProperties.displayAsPassword
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), 
                DISPLAY_AS_PASSWORD_PROPERTY_FLAG, true);
        }

        if (textDisplayProperties.editable !== undefined)
        {
            textDisplay.editable = textDisplayProperties.editable;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), EDITABLE_PROPERTY_FLAG, true);
        }

        if (textDisplayProperties.heightInLines !== undefined && richEditableText)
        {
            richEditableText.heightInLines = textDisplayProperties.heightInLines;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), 
                HEIGHT_IN_LINES_PROPERTY_FLAG, true);
        }

        if (textDisplayProperties.imeMode !== undefined && richEditableText)
        {
            richEditableText.imeMode = textDisplayProperties.imeMode;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), IME_MODE_PROPERTY_FLAG, true);
        }

        if (textDisplayProperties.maxChars !== undefined)
        {
            textDisplay.maxChars = textDisplayProperties.maxChars;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), MAX_CHARS_PROPERTY_FLAG, true);
        }
        
        if (textDisplayProperties.maxWidth !== undefined)
        {
            if (richEditableText)
                richEditableText.maxWidth = textDisplayProperties.maxWidth;
            else
                super.maxWidth = textDisplayProperties.maxWidth;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), MAX_WIDTH_PROPERTY_FLAG, true);
        }

        if (textDisplayProperties.restrict !== undefined)
        {
            textDisplay.restrict = textDisplayProperties.restrict;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), RESTRICT_PROPERTY_FLAG, true);
        }

        if (textDisplayProperties.selectable !== undefined)
        {
            textDisplay.selectable = textDisplayProperties.selectable;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), SELECTABLE_PROPERTY_FLAG, true);
        }

        if (textDisplayProperties.selectionHighlighting !== undefined && richEditableText)
        {
            richEditableText.selectionHighlighting = 
                textDisplayProperties.selectionHighlighting;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), 
                SELECTION_HIGHLIGHTING_FLAG, true);
        }
            
        if (textDisplayProperties.text != null)
        {
            textDisplay.text = textDisplayProperties.text;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), TEXT_PROPERTY_FLAG, true);
        }

        if (textDisplayProperties.textFlow !== undefined && richEditableText)
        {
            richEditableText.textFlow = textDisplayProperties.textFlow;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), TEXT_FLOW_PROPERTY_FLAG, true);
        }

        if (textDisplayProperties.typicalText !== undefined && richEditableText)
        {
            richEditableText.typicalText = textDisplayProperties.typicalText;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), 
                TYPICAL_TEXT_PROPERTY_FLAG, true);
        }
        
        if (textDisplayProperties.widthInChars !== undefined && richEditableText)
        {
            richEditableText.widthInChars = textDisplayProperties.widthInChars;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties), 
                WIDTH_IN_CHARS_PROPERTY_FLAG, true);
        }
        
        var softKeyboardClient:ISoftKeyboardHintClient = textDisplay as ISoftKeyboardHintClient;
        
        if (textDisplayProperties.autoCapitalize !== undefined && softKeyboardClient)
        {
            softKeyboardClient.autoCapitalize = textDisplayProperties.autoCapitalize;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties),
                AUTO_CAPITALIZE_FLAG, true);
        }
        
        if (textDisplayProperties.autoCorrect !== undefined && softKeyboardClient)
        {
            softKeyboardClient.autoCorrect = textDisplayProperties.autoCorrect;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties),
                AUTO_CORRECT_FLAG, true);
        }
        
        if (textDisplayProperties.returnKeyLabel !== undefined && softKeyboardClient)
        {
            softKeyboardClient.returnKeyLabel = textDisplayProperties.returnKeyLabel;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties),
                RETURN_KEY_LABEL_FLAG, true);
        }
        
        if (textDisplayProperties.softKeyboardType !== undefined && softKeyboardClient)
        {
            softKeyboardClient.softKeyboardType = textDisplayProperties.softKeyboardType;
            newTextDisplayProperties = BitFlagUtil.update(
                uint(newTextDisplayProperties),
                SOFT_KEYBOARD_TYPE_FLAG, true);
        }
            
        // Switch from storing properties to bit mask of stored properties.
        textDisplayProperties = newTextDisplayProperties;    
    }
    
    /**
     *  @private
     *  Copy values stored in textDisplay back to local storage since 
     *  textDisplay is about to be removed.
     */
    private function textDisplayRemoved():void
    {        
        var newTextDisplayProperties:Object = {};
        var richEditableText:RichEditableText = textDisplay as RichEditableText;
        
        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
                              DISPLAY_AS_PASSWORD_PROPERTY_FLAG))
        {
            newTextDisplayProperties.displayAsPassword = 
                textDisplay.displayAsPassword;
        }

        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
                              EDITABLE_PROPERTY_FLAG))
        {
            newTextDisplayProperties.editable = textDisplay.editable;
        }
        
        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
                              HEIGHT_IN_LINES_PROPERTY_FLAG) && richEditableText)
        {
            newTextDisplayProperties.heightInLines = richEditableText.heightInLines;
        }

        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
                              IME_MODE_PROPERTY_FLAG) && richEditableText)
        {
            newTextDisplayProperties.imeMode = richEditableText.imeMode;
        }
        
        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
                              MAX_CHARS_PROPERTY_FLAG))
        {
            newTextDisplayProperties.maxChars = textDisplay.maxChars;
        }

        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
                              MAX_WIDTH_PROPERTY_FLAG))
        {
            newTextDisplayProperties.maxWidth = richEditableText ? 
                richEditableText.maxWidth : super.maxWidth;
        }

        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
                              RESTRICT_PROPERTY_FLAG))
        {
            newTextDisplayProperties.restrict = textDisplay.restrict;
        }
        
        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
                              SELECTABLE_PROPERTY_FLAG))
        {
            newTextDisplayProperties.selectable = textDisplay.selectable;
        }

        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
                              SELECTION_HIGHLIGHTING_FLAG) && richEditableText)
        {
            newTextDisplayProperties.selectionHighlighting = 
                richEditableText.selectionHighlighting;
        }
            
        // Text is special.            
        if (BitFlagUtil.isSet(uint(textDisplayProperties), TEXT_PROPERTY_FLAG))
            newTextDisplayProperties.text = textDisplay.text;

        // Content is just a setter.  So if it was set, get the textFlow
        // instead.        
        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
                TEXT_FLOW_PROPERTY_FLAG) || 
            BitFlagUtil.isSet(uint(textDisplayProperties), 
                CONTENT_PROPERTY_FLAG) && richEditableText)
        {
            newTextDisplayProperties.textFlow = richEditableText.textFlow;
        }

        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
                              TYPICAL_TEXT_PROPERTY_FLAG) && richEditableText)
        {
            newTextDisplayProperties.typicalText = richEditableText.typicalText;
        }
            
        if (BitFlagUtil.isSet(uint(textDisplayProperties), 
            WIDTH_IN_CHARS_PROPERTY_FLAG) && richEditableText)
        {
            newTextDisplayProperties.widthInChars = richEditableText.widthInChars;
        }
        
        var softKeyboardClient:ISoftKeyboardHintClient = textDisplay as ISoftKeyboardHintClient;
        
        if (BitFlagUtil.isSet(uint(textDisplayProperties),
            AUTO_CAPITALIZE_FLAG) && softKeyboardClient)
        {
            newTextDisplayProperties.autoCapitalize = softKeyboardClient.autoCapitalize;
        }
        
        if (BitFlagUtil.isSet(uint(textDisplayProperties),
            AUTO_CORRECT_FLAG) && softKeyboardClient)
        {
            newTextDisplayProperties.autoCorrect = softKeyboardClient.autoCorrect;
        }
        
        if (BitFlagUtil.isSet(uint(textDisplayProperties),
            RETURN_KEY_LABEL_FLAG) && softKeyboardClient)
        {
            newTextDisplayProperties.returnKeyLabel = softKeyboardClient.returnKeyLabel;
        }
        
        if (BitFlagUtil.isSet(uint(textDisplayProperties),
            SOFT_KEYBOARD_TYPE_FLAG) && softKeyboardClient)
        {
            newTextDisplayProperties.softKeyboardType = softKeyboardClient.softKeyboardType;
        }
        
        // Switch from storing bit mask to storing properties.
        textDisplayProperties = newTextDisplayProperties;
    }
    
    //--------------------------------------------------------------------------
    //
    //  Overridden event handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override protected function focusInHandler(event:FocusEvent):void
    {
        if (event.target == this)
        {
            // call setFocus on ourselves to pass focus to the
            // textDisplay.  This situation occurs when the
            // player occasionally takes over the first TAB
            // on a newly activated Window with nothing currently
            // in focus
            setFocus();
            return;
        }

        // On mobile, 2 problems interfere with FocusManager's
        // normal resetting of the focus indicator. First, the
        // mouse down and focus in events on mobile happen out of
        // order, so the normal resetting of showFocusIndicator
        // in the mouse down capture handler doesn't happen until
        // after the focus event. Second, StageText doesn't send
        // mouse events at all. So, to make the focus indicator
        // more robust, we store the flag's old value, set it, let
        // the superclass draw the focus indicator, then restore
        // the flag's old value.
        var oldShowFocusIndicator:Boolean;
        
        // Only editable text should have a focus ring.
        if (enabled && editable && focusManager)
        {
            oldShowFocusIndicator = focusManager.showFocusIndicator;
            focusManager.showFocusIndicator = true;
        }

        invalidateSkinState();
        
        super.focusInHandler(event);
        
        if (enabled && editable && focusManager)
            focusManager.showFocusIndicator = oldShowFocusIndicator;
    }
 
    /**
     *  @private
     */
    override protected function focusOutHandler(event:FocusEvent):void
    {
        if (event.target == this)
            return;
        
        invalidateSkinState();
        
        super.focusOutHandler(event);
    }

    //--------------------------------------------------------------------------
    //
    //  Event handlers
    //
    //--------------------------------------------------------------------------


    /**
     * @private
     * Called if we are in touch interaction mode and we receive a mouseDown
     */  
    private function touchMouseDownHandler(event:MouseEvent):void
    {
        isMouseDown = true;
        mouseDownTarget = event.target as InteractiveObject;
        
        // If we already have focus, make sure to open soft keyboard
        // on mouse up
        if (focusManager && focusManager.getFocus() == this)
            delaySetFocus = true;
        
        // Wait for a mouseUp somewhere
        systemManager.getSandboxRoot().addEventListener(
            MouseEvent.MOUSE_UP, touchMouseUpHandler, false, 0, true);
        systemManager.getSandboxRoot().addEventListener(
            SandboxMouseEvent.MOUSE_UP_SOMEWHERE, touchMouseUpHandler, false, 0, true);
    }
    
    /**
     * @private
     * Called if we are in touch interaction mode and a mouseUp occurs on the stage while isMouseDown is true
     */ 
    private function touchMouseUpHandler(event:Event):void
    {        
        /* 
         We set the focus on the component on mouseUp to activate the softKeyboard   
         We only set focus if the following conditions are met:
         1. mouseUp occurs on this component
         2. mouseDown occured on any subcomponent besides the textDisplay OR
         mouseDown occurred on textDisplay and mouseUp did not occur on textDisplay
        
         The mouseDown and mouseUp on textDisplay case is handled by the Player
        */        
        if ((event.target is DisplayObject && contains(DisplayObject(event.target))) 
            && (delaySetFocus ||
             (mouseDownTarget == textDisplay && event.target != textDisplay)))
        {
            if (textDisplay)
                textDisplay.setFocus();
        }
        
        clearMouseDownState();
    }
       
    /**
     * @private
     * Called if we are inside of a Scroller and the user has started a scroll gesture
     */
    private function touchInteractionStartHandler(event:TouchInteractionEvent):void
    {
        // if in iOS and keyboard is up and scrolling is occurring, drop the keyboard
        var topLevelApp:Application = FlexGlobals.topLevelApplication as Application;
        if (isIOS && topLevelApp && topLevelApp.isSoftKeyboardActive && editable)
        {
            // set focus
            stage.focus = null;
        }
        
        // Clear out the state because starting a scroll gesture should never 
        // open the soft keyboard
        clearMouseDownState();
    }    
    
    /**
     * @private
     * Helper function to clear the state if the mouse is up or we started as scroll gesture
     */
    private function clearMouseDownState():void
    {
        if (isMouseDown)
        {
            systemManager.getSandboxRoot().removeEventListener(
                MouseEvent.MOUSE_UP, touchMouseUpHandler, false);
            systemManager.getSandboxRoot().removeEventListener(
                SandboxMouseEvent.MOUSE_UP_SOMEWHERE, touchMouseUpHandler, false);
            isMouseDown = false;
            delaySetFocus = false;
            mouseDownTarget = null;
        }
    }
    
    /**
     * @private
     */
    private function softKeyboardActivatingHandler(event:SoftKeyboardEvent):void
    {
        event.preventDefault();
    }
    
    /**
     *  @private
     *  Called when the RichEditableText dispatches a 'selectionChange' event.
     */
    private function textDisplay_selectionChangeHandler(event:Event):void
    {
        // Redispatch the event that came from the RichEditableText.
        dispatchEvent(event);
    }

    /**
     *  Called when the RichEditableText dispatches a 'change' event
     *  after an editing operation.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    private function textDisplay_changeHandler(event:Event):void
    {        
        //trace(id, "textDisplay_changeHandler", textDisplay.text);
        
        // The text component has changed.  Generate an UPDATE_COMPLETE event.
        invalidateDisplayList();
        
        // We may have gone from empty to non-empty or vice-versa. This should
        // cause the prompt to show or hide.
        if (prompt != null && prompt != "" && skin && 
            (skin.currentState.indexOf("WithPrompt") != -1 && text.length != 0 ||
            skin.currentState.indexOf("WithPrompt") == -1 && text.length == 0))
            invalidateSkinState();
                
        // Redispatch the event that came from the RichEditableText.
        dispatchEvent(event);
    }

    /**
     *  @private
     *  Called when the RichEditableText dispatches a 'changing' event
     *  before an editing operation.
     */
    private function textDisplay_changingHandler(event:TextOperationEvent):void
    {
        // Redispatch the event that came from the RichEditableText.
        var newEvent:Event = event.clone();
        dispatchEvent(newEvent);
        
        // If the event dispatched from this component is canceled,
        // cancel the one from the RichEditableText, which will prevent
        // the editing operation from being processed.
        if (newEvent.isDefaultPrevented())
            event.preventDefault();
    }

    /**
     *  @private
     *  Called when the RichEditableText dispatches an 'enter' event
     *  in response to the Enter key.
     */
    private function textDisplay_enterHandler(event:Event):void
    {
        // Redispatch the event that came from the RichEditableText.
        dispatchEvent(event);
    }

    /**
     *  @private
     *  Called when the RichEditableText dispatches an 'valueCommit' event
     *  when values are changed programmatically or by user interaction.
     *  Before the textDisplay part is loaded, any properties set are held
     *  in textDisplayProperties.  We don't want to dispatch 'valueCommit' when 
     *  there isn't a textDisplay since since the event will be dispatched by 
     *  RET when the textDisplay part is added.
     */
    private function textDisplay_valueCommitHandler(event:Event):void
    {
        // Redispatch the event that came from the RichEditableText.
        dispatchEvent(event);
    }
}

}
