////////////////////////////////////////////////////////////////////////////////
//
//  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
{
    
    import flash.display.DisplayObject;
    import flash.display.DisplayObjectContainer;
    import flash.events.Event;
    import flash.events.FocusEvent;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;
    import flash.system.IME;
    import flash.system.IMEConversionMode;
    import flash.text.engine.ElementFormat;
    import flash.text.engine.FontDescription;
    import flash.text.engine.FontLookup;
    import flash.text.engine.TextBlock;
    import flash.text.engine.TextElement;
    import flash.text.engine.TextLine;
    import flash.ui.Keyboard;
    
    import flashx.textLayout.compose.ISWFContext;
    import flashx.textLayout.container.TextContainerManager;
    import flashx.textLayout.conversion.ConversionType;
    import flashx.textLayout.conversion.ITextExporter;
    import flashx.textLayout.conversion.ITextImporter;
    import flashx.textLayout.conversion.TextConverter;
    import flashx.textLayout.edit.EditManager;
    import flashx.textLayout.edit.EditingMode;
    import flashx.textLayout.edit.IEditManager;
    import flashx.textLayout.edit.ISelectionManager;
    import flashx.textLayout.edit.SelectionState;
    import flashx.textLayout.elements.Configuration;
    import flashx.textLayout.elements.GlobalSettings;
    import flashx.textLayout.elements.InlineGraphicElement;
    import flashx.textLayout.elements.InlineGraphicElementStatus;
    import flashx.textLayout.elements.TextFlow;
    import flashx.textLayout.events.CompositionCompleteEvent;
    import flashx.textLayout.events.DamageEvent;
    import flashx.textLayout.events.FlowOperationEvent;
    import flashx.textLayout.events.SelectionEvent;
    import flashx.textLayout.events.StatusChangeEvent;
    import flashx.textLayout.factory.StringTextLineFactory;
    import flashx.textLayout.factory.TextFlowTextLineFactory;
    import flashx.textLayout.formats.BlockProgression;
    import flashx.textLayout.formats.Category;
    import flashx.textLayout.formats.ITextLayoutFormat;
    import flashx.textLayout.formats.TextLayoutFormat;
    import flashx.textLayout.operations.CopyOperation;
    import flashx.textLayout.operations.CutOperation;
    import flashx.textLayout.operations.DeleteTextOperation;
    import flashx.textLayout.operations.FlowOperation;
    import flashx.textLayout.operations.FlowTextOperation;
    import flashx.textLayout.operations.InsertTextOperation;
    import flashx.textLayout.operations.PasteOperation;
    import flashx.textLayout.tlf_internal;
    import flashx.undo.IUndoManager;
    
    import mx.core.FlexVersion;
    import mx.core.IFlexModuleFactory;
    import mx.core.IIMESupport;
    import mx.core.ISystemCursorClient;
    import mx.core.UIComponent;
    import mx.core.mx_internal;
    import mx.events.FlexEvent;
    import mx.managers.IFocusManager;
    import mx.managers.IFocusManagerComponent;
    import mx.resources.ResourceManager;
    import mx.utils.StringUtil;
    
    import spark.components.supportClasses.RichEditableTextContainerManager;
    import spark.core.CSSTextLayoutFormat;
    import spark.core.IEditableText;
    import spark.core.IViewport;
    import spark.core.NavigationUnit;
    import spark.events.TextOperationEvent;
    import spark.utils.TextUtil;
    
    use namespace mx_internal;
    use namespace tlf_internal;
    
    //--------------------------------------
    //  Events
    //--------------------------------------
    
    /**
     *  Dispached after the <code>selectionAnchorPosition</code> and/or
     *  <code>selectionActivePosition</code> properties have changed
     *  for any reason.
     *
     *  @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.
     *
     *  @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 the user presses the Enter key,
     *  if the <code>multiline</code> property is false.
     *
     *  @eventType mx.events.FlexEvent.ENTER
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    [Event(name="enter", type="mx.events.FlexEvent")]
    
    //--------------------------------------
    //  Styles
    //--------------------------------------
    
    include "../styles/metadata/BasicInheritingTextStyles.as"
    include "../styles/metadata/BasicNonInheritingTextStyles.as"
    include "../styles/metadata/AdvancedInheritingTextStyles.as"
    include "../styles/metadata/AdvancedNonInheritingTextStyles.as"
    include "../styles/metadata/SelectionFormatTextStyles.as"
    
    /**
     *  The alpha level of the color defined by
     *  the <code>backgroundColor</code> style.
     *  Valid values range from 0.0 to 1.0.
     * 
     *  @default 1.0
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    [Style(name="backgroundAlpha", type="Number", inherit="no")]
    
    /**
     *  The color of the background of the entire
     *  bounding rectangle of this component.
     *  If this style is <code>undefined</code>,
     *  no background is drawn.
     *  Otherwise, this RGB color is drawn with an alpha level
     *  determined by the <code>backgroundAlpha</code> style.
     * 
     *  @default undefined
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    [Style(name="backgroundColor", type="uint", format="Color", inherit="no")]
    
    //--------------------------------------
    //  Excluded APIs
    //--------------------------------------
    
    [Exclude(name="chromeColor", kind="style")]
    
    //--------------------------------------
    //  Other metadata
    //--------------------------------------
    
    [AccessibilityClass(implementation="spark.accessibility.RichEditableTextAccImpl")]
    
    [DefaultProperty("content")]
    
    [IconFile("RichEditableText.png")]
    
    [DefaultTriggerEvent("change")]
    
    [DiscouragedForProfile("mobileDevice")]
    
    /**
     *  RichEditableText is a low-level UIComponent for displaying,
     *  scrolling, selecting, and editing richly-formatted text.
     *
     *  <p>The rich text can contain clickable hyperlinks and inline graphics
     *  that are either embedded or loaded from URLs.</p>
     *
     *  <p>RichEditableText does not have scrollbars, but it implements
     *  the IViewport interface for programmatic scrolling so that it
     *  can be controlled by a Scroller, which does provide scrollbars.
     *  It also supports vertical scrolling with the mouse wheel.</p>
     *
     *  <p>It does not include any user interface for changing
     *  the formatting of the text.
     *  But it offers APIs which can do this programmatically;
     *  these make it possible, for example, for you to create 
     *  a Bold button that makes the selected text bold.</p>
     *
     *  <p>This class is used in the skins of the Spark versions
     *  of TextInput and TextArea.
     *  (TextInput does not expose its ability to handle rich text,
     *  but TextArea does.)
     *  By default, RichEditableText has a transparent background,
     *  and it does not support drawing a border.</p>
     *
     *  <p>RichEditableText uses the Text Layout Framework (TLF) library,
     *  which in turn builds on the new Flash Text Engine (FTE)
     *  in Flash Player 10. In combination, these layers provide text editing with
     *  high-quality international typography and layout.</p>
     *
     *  <p>The Spark architecture provides three text "primitives" -- 
     *  Label, RichText, and RichEditableText.
     *  Label is the fastest and most lightweight
     *  because it uses only FTE, not TLF,
     *  but it is limited in its capabilities: no rich text,
     *  no scrolling, no selection, and no editing.
     *  RichText adds the ability to display rich text
     *  with complex layout, but is still completely non-interactive.
     *  RichEditableText is the heaviest-weight,
     *  but offers most of what TLF can do.
     *  In general, use the fastest text primitive that meets your needs.</p>
     *
     *  <p>RichEditableText is similar to the UITextField class
     *  used in MX components. This class did not use FTE or TLF
     *  but rather extended the older TextField class.</p>
     *
     *  <p>The most important differences between UITextField and RichEditableText are:
     *  <ul>
     *    <li>RichEditableText offers better typography, better support
     *        for international languages, and better text layout.</li>
     *    <li>RichEditableText has an object-oriented model of rich text,
     *        while UITextField does not.</li>
     *    <li>RichEditableText has better support for displaying
     *        large amounts of text.</li>
     *    <li>RichEditableText requires that fonts be embedded
     *        differently than UITextField.
     *        Consult the documentation regarding how to use the
     *        <code>embedAsCFF</code> attribute when you embed a font.</li>
     *  </ul></p>
     *
     *  <p>RichEditableText uses TLF's object-oriented model of rich text,
     *  in which text layout elements such as divisions, paragraphs, spans,
     *  hyperlinks, and images are represented at runtime by ActionScript
     *  objects which can be programmatically accessed and manipulated.
     *  The central object in TLF for representing rich text is a
     *  TextFlow, so you specify rich text for a RichEditableText control to display
     *  by setting its <code>textFlow</code> property to a TextFlow instance.
     *  See the description of the <code>textFlow</code>
     *  property for information about how to create one,
     *  such as by importing TLF markup.
     *  If you don't need to display text that has multiple formats,
     *  you can use the <code>text</code> property to set a "plain text" String.
     *  See the description of the <code>text</code> and <code>textFlow</code>
     *  properties for information about how they interact;
     *  for example, you can set one and get the other.</p>
     *
     *  <p>At compile time, you can put TLF markup tags inside
     *  the RichEditableText tag, as the following example shows:
     *  <pre>
     *  &lt;s:RichEditableText&gt;Hello &lt;s:span fontWeight="bold"&gt;World!&lt;/s:span&gt;&lt;/s:RichEditableText&gt;
     *  </pre>
     *  In this case, the MXML compiler sets the <code>content</code>
     *  property, causing a TextFlow to be automatically created
     *  from the FlowElements that you specify.</p>
     *
     *  <p>The default text formatting is determined by CSS styles
     *  such as <code>fontFamily</code>, <code>fontSize</code>.
     *  Any formatting information in the TextFlow overrides
     *  the default formatting provided by the CSS styles.</p>
     *
     *  <p>You can control the spacing between lines with the
     *  <code>lineHeight</code> style and the spacing between
     *  paragraphs with the <code>paragraphSpaceBefore</code>
     *  and <code>paragraphSpaceAfter</code> styles.
     *  You can align or justify the text using the <code>textAlign</code>
     *  and <code>textAlignLast</code> styles.
     *  You can inset the text from the component's edges using the
     *  <code>paddingLeft</code>, <code>paddingTop</code>, 
     *  <code>paddingRight</code>, and <code>paddingBottom</code> styles.</p>
     *
     *  <p>By default, a RichEditableText "autosizes": it starts out very
     *  small if it has no text, and grows in width up to
     *  <code>maxWidth</code> as you type. It grows in height when you 
     *  press the Enter key to start a new line.</p>
     *
     *  <p>The <code>widthInChars</code> and <code>heightInLines</code>
     *  properties provide a convenient way to specify the width and height
     *  in a way that scales with the font size.
     *  You can use the <code>typicalText</code> property as well.
     *  Note that if you use <code>typicalText</code>, the
     *  <code>widthInChars</code> and <code>heightInLines</code>
     *  are ignored.
     *  You can also specify an explicit width or height in pixels,
     *  or use a percent width and height, or use constraints such as
     *  <code>left</code> and <code>right</code>
     *  or <code>top</code> and <code>bottom</code>.</p>
     *
     *  <p>When you specify some kind of width -- whether an explicit or
     *  percent width, a <code>maxWidth</code> or <code>left</code>
     *  and <code>right</code> constraints -- the text wraps at the right
     *  edge of the component and the text becomes vertically scrollable
     *  when there is more text than fits.
     *  If you set the <code>lineBreak</code> style to <code>explicit</code>,
     *  new lines will start only at explicit lines breaks, such as
     *  if you use CR (<code>\r</code>), LF (<code>\n</code>),
     *  or CR+LF (<code>\r\n</code>) in <code>text</code>
     *  or if you use <code>&lt;p&gt;</code> and <code>&lt;br/&gt;</code>
     *  in TLF markup.
     *  In that case, the text becomes horizontally scrollable
     *  if any lines are wider than the control.</p>
     *
     *  <p>You can use the <code>maxChars</code> property to limit the number
     *  of character that the user can enter, and the <code>restrict</code>
     *  to limit which characters the user can enter.</p>
     *
     *  <p>The <code>multiline</code> property determines what happens
     *  when you press the Enter key.
     *  If it is <code>true</code>, the Enter key starts a new paragraph.
     *  If it is <code>false</code>, it causes a <code>FlexEvent.ENTER</code>
     *  event to be dispatched.</p>
     *
     *  <p>If you don't want the text to be editable,
     *  set the <code>editable</code> property to <code>false</code>.
     *  If you don't even want the text to be selectable,
     *  set the <code>selectable</code> property to <code>false</code>.</p>
     *
     *  <p>Because RichEditableText uses TLF,
     *  it supports displaying left-to-right (LTR) text such as French,
     *  right-to-left (RTL) text such as Arabic, and bidirectional text
     *  such as a French phrase inside of an Arabic one.
     *  If the predominant text direction is right-to-left,
     *  set the <code>direction</code> style to <code>rtl</code>.
     *  The <code>textAlign</code> style defaults to <code>"start"</code>,
     *  which makes the text left-aligned when <code>direction</code>
     *  is <code>ltr</code> and right-aligned when <code>direction</code>
     *  is <code>rtl</code>.
     *  To get the opposite alignment, set <code>textAlign</code> to <code>end</code>.</p>
     *
     *  <p>As a result of using TLF, the RichEditableText supports
     *  unlimited undo/redo within one editing session.
     *  An editing session starts when the component gets keyboard focus
     *  and ends when it loses focus.</p>
     *
     *  <p>RichEditableText uses TLF's TextContainerManager class
     *  to handle its text display, scrolling, selection, editing and context menu.</p>
     *
     *  <p>To use this component in a list-based component, such as a List or DataGrid, 
     *  create an item renderer.
     *  For information about creating an item renderer, see 
     *  <a href="http://help.adobe.com/en_US/flex/using/WS4bebcd66a74275c3-fc6548e124e49b51c4-8000.html">
     *  Custom Spark item renderers</a>. </p>
     *
     *  @mxml
     *
     *  <p>The <code>&lt;s:RichEditableText&gt;</code> tag inherits all of the tag 
     *  attributes of its superclass and adds the following tag attributes:</p>
     *
     *  <pre>
     *  &lt;s:RichEditableText
     *    <strong>Properties</strong>
     *    clipAndEnableScrolling="false|true"
     *    contentHeight="<i>Calculated default</i>"
     *    contentWidth="<i>Calculated default</i>"
     *    displayAsPassword="false"
     *    editable="true"
     *    heightInLines="NaN"
     *    horizontalScrollPosition="0"
     *    imeMode="null"
     *    maxChars="0"
     *    multiline="true"
     *    restrict="null"
     *    selectable="true"
     *    selectionActivePosition="-1"
     *    selectionAnchorPosition="-1"
     *    selectionHighlighting="TextSelectionHighlighting.WHEN_FOCUSED"
     *    text=""
     *    textFlow="<i>TextFlow</i>"
     *    typicalText=null
     *    verticalScrollPosition="0"
     *    widthInChars="NaN"
     *  
     *    <strong>Events</strong>
     *    change="<i>No default</i>"
     *    changing="<i>No default</i>"
     *    enter="<i>No default</i>"
     *    selectionChange="<i>No default</i>"
     *  /&gt;
     *  </pre>
     *  
     *  @includeExample examples/RichEditableTextExample.mxml
     *  @includeExample examples/externalTextFlow.xml -noswf
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     *
     *  @see spark.components.Label
     *  @see spark.components.RichText
     *  @see spark.utils.TextFlowUtil
     *  @see flashx.textLayout.container.TextContainerManager
     */
    public class RichEditableText extends UIComponent
        implements IFocusManagerComponent, IIMESupport, ISystemCursorClient, 
                   IViewport, IEditableText
    {
        include "../core/Version.as";
        
        //--------------------------------------------------------------------------
        //
        //  Class mixins
        //
        //--------------------------------------------------------------------------
        
        /**
         *  @private
         *  Placeholder for mixin by RichEditableTextAccImpl.
         */
        mx_internal static var createAccessibilityImplementation:Function;
        
        //--------------------------------------------------------------------------
        //
        //  Class initialization
        //
        //--------------------------------------------------------------------------
        
        /**
         *  @private
         *  This method initializes the static vars of this class.
         *  Rather than calling it at static initialization time,
         *  we call it in the constructor to do the class initialization
         *  when the first instance is created.
         *  (It does an immediate return if it has already run.)
         *  By doing so, we avoid any static initialization issues
         *  related to whether this class or the TLF classes
         *  that it uses are initialized first.
         */
        private static function initClass():void
        {
            if (classInitialized)
                return;
            
            // Set the TLF hook used for localizing runtime error messages.
            // TLF itself has English-only messages,
            // but higher layers like Flex can provide localized versions.
            GlobalSettings.resourceStringFunction = TextUtil.getResourceString;
            
            // Set the TLF hook used to specify the callback used for changing 
            // the FontLookup based on SWFContext.  
            GlobalSettings.resolveFontLookupFunction = TextUtil.resolveFontLookup;
            
            // Pre-FP10.1, set default tab stops in TLF.  Without this, if there
            // is a tab and TLF is measuring width, the tab will
            // measure as the rest of the remaining width up to 10000.
            GlobalSettings.enableDefaultTabStops = 
                !Configuration.playerEnablesArgoFeatures;
            
            staticPlainTextImporter =
                TextConverter.getImporter(TextConverter.PLAIN_TEXT_FORMAT);
            
            // Throw import errors rather than return a null textFlow.
            // Alternatively, the error strings are in the Vector, importer.errors.
            staticPlainTextImporter.throwOnError = true;
            
            staticPlainTextExporter =
                TextConverter.getExporter(TextConverter.PLAIN_TEXT_FORMAT);
            
            classInitialized = true;
        } 
        
        //--------------------------------------------------------------------------
        //
        //  Class variables
        //
        //--------------------------------------------------------------------------
        
        /**
         *  @private
         */
        private static var classInitialized:Boolean = false;
                
        /**
         *  @private
         *  This TLF object composes TextLines from a text String.
         *  We use it when the 'typicalText' property is set to a String
         *  that doesn't contain linebreaks.
         */
        private static var staticStringFactory:StringTextLineFactory;
        
        /**
         *  @private
         *  This TLF object composes TextLines from a TextFlow.
         *  We use it when the 'typicalText is set to a String
         *  that contains linebreaks (and therefore is interpreted
         *  as multiple paragraphs).
         */
        private static var staticTextFlowFactory:TextFlowTextLineFactory;
        
        /**
         *  @private
         *  This TLF object is used to import a 'text' String
         *  containing linebreaks to create a multiparagraph TextFlow.
         */
        private static var staticPlainTextImporter:ITextImporter;
        
        /**
         *  @private
         *  This TLF object is used to export a TextFlow as plain 'text',
         *  by walking the leaf FlowElements in the TextFlow.
         */
        private static var staticPlainTextExporter:ITextExporter;
        
        /**
         *  @private
         *  Regular expression which matches all newlines in the text.  Used
         *  to strip newlines when pasting text when multiline is false.
         */
        private static const ALL_NEWLINES_REGEXP:RegExp = /\n/g;
        
        //--------------------------------------------------------------------------
        //
        //  Class methods
        //
        //--------------------------------------------------------------------------
        /**
         *  @private
         */
        public static function getNumberOrPercentOf(value:Object,
                                                    n:Number):Number
        {
            // If 'value' is a Number like 10.5, return it.
            if (value is Number)
                return Number(value);
            
            // If 'value' is a percentage String like "10.5%",
            // return that percentage of 'n'.
            if (value is String)
            {
                var len:int = String(value).length;
                if (len >= 1 && value.charAt(len - 1) == "%")
                {
                    var percent:Number = Number(value.substring(0, len - 1));
                    return percent / 100 * n;
                }
            }
            
            // Otherwise, return NaN.
            return NaN;
        }        
        
        /**
         *  @private
         */
        private static function splice(str:String, start:int, end:int,
                                       strToInsert:String):String
        {
            return str.substring(0, start) +
                strToInsert +
                str.substring(end, str.length);
        }

        //--------------------------------------------------------------------------
        //
        //  Constructor
        //
        //--------------------------------------------------------------------------
        
        /**
         *  Constructor. 
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function RichEditableText()
        {
            super();
            
            initClass();
            
            // Use the setter.
            text = "";
            
            // Create the TLF TextContainerManager, using this component
            // as the DisplayObjectContainer for its TextLines.
            // This TextContainerManager instance persists for the lifetime
            // of the component.
            _textContainerManager = createTextContainerManager();
            
            // Add event listeners on this component.
            addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
            
            // The focusInHandler is called by the TCMContainer focusInHandler.
            // The focusOutHandler is called by the TCMContainer focusOutHandler.
            // The keyDownHandler is called by the TCMContainer keyDownHandler.
            
            // Add event listeners on its TextContainerManager.
            
            _textContainerManager.addEventListener(
                CompositionCompleteEvent.COMPOSITION_COMPLETE,
                textContainerManager_compositionCompleteHandler);
            
            _textContainerManager.addEventListener(
                DamageEvent.DAMAGE, textContainerManager_damageHandler);
            
            _textContainerManager.addEventListener(
                Event.SCROLL, textContainerManager_scrollHandler);
            
            _textContainerManager.addEventListener(
                SelectionEvent.SELECTION_CHANGE,
                textContainerManager_selectionChangeHandler);
            
            _textContainerManager.addEventListener(
                FlowOperationEvent.FLOW_OPERATION_BEGIN,
                textContainerManager_flowOperationBeginHandler);
            
            _textContainerManager.addEventListener(
                FlowOperationEvent.FLOW_OPERATION_END,
                textContainerManager_flowOperationEndHandler);
            
            _textContainerManager.addEventListener(
                FlowOperationEvent.FLOW_OPERATION_COMPLETE,
                textContainerManager_flowOperationCompleteHandler);
            
            _textContainerManager.addEventListener(
                StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE, 
                textContainerManager_inlineGraphicStatusChangeHandler);
        }
        
        //--------------------------------------------------------------------------
        //
        //  Variables
        //
        //--------------------------------------------------------------------------
        
        /**
         *  @private
         *  The composition bounds used when creating the TextLines.
         */
        mx_internal var unbounded:Rectangle = new Rectangle(0, 0, NaN, NaN);
        
        /**
         *  @private
         *  The hostFormat object in the _textContainerManager determines the 
         *  default text formatting used by this component, based on its CSS styles. 
         *  This flag is used by updateStylesIfChanged() determine when the object 
         *  must be reinitialized.  It is set in stylesInitialized() and 
         *  styleChanged(), and cleared in commitProperties().  After 
         *  initialization, the hostFormat object should never be null.
         */
        private var hostFormatChanged:Boolean;
        
        /**
         *  @private
         *  It is set to NaN by stylesInitialized() and styleChanged(),
         *  and recreated whenever necessary in calculateFontMetrics().
         */
        private var ascent:Number = NaN;
        
        /**
         *  @private
         *  It is set to NaN by stylesInitialized() and styleChanged(),
         *  and recreated whenever necessary in calculateFontMetrics().
         */
        private var descent:Number = NaN;
        
        /**
         *  @private
         *  Source of text: one of "text", "textFlow" or "content".
         */
        private var source:String = "text";
        
        /**
         *  @private
         *  Holds the last recorded value of the textFlow generation.  Used to
         *  determine whether to return immediately from damage event if there 
         *  have been no changes.
         */
        private var lastGeneration:uint = 0;    // 0 means not set
        
        /**
         *  @private
         *  The generation of the text flow that last reported its content
         *  bounds. 
         */
        private var lastContentBoundsGeneration:int = 0;  // 0 means not set
        
        /**
         *  @private
         *  True if TextOperationEvent.CHANGING and TextOperationEvent.CHANGE 
         *  events should be dispatched.
         */
        private var dispatchChangeAndChangingEvents:Boolean = true;
        
        /**
         *  @private
         */
        private var inMeasureMethod:Boolean = false;
        
        /**
         *  @private
         */
        private var inUpdateDLMethod:Boolean = false;
        
        /**
         *  @private
         */
        private var remeasuringText:Boolean = false;

        /**
         *  @private
         */
        mx_internal var preserveSelectionOnSetText:Boolean = false;

        /**
         *  @private
         */
        mx_internal var passwordChar:String = "*";
        
        /**
         *  @private
         */
        mx_internal var undoManager:IUndoManager;
        
        /**
         *  @private
         */
        mx_internal var clearUndoOnFocusOut:Boolean = true;
        
        /**
         *  @private
         *  Holds the last recorded value of the module factory used to create the 
         *  font.
         */
        mx_internal var embeddedFontContext:IFlexModuleFactory;
        
        /**
         *  @private
         *  The TLF edit manager will batch all inserted text until the next
         *  enter frame event.  This includes text inserted via the GUI as well
         *  as api calls to EditManager.insertText().  Set this to false if you
         *  want every keystroke to be inserted into the text immediately which will
         *  result in a TextOperationEvent.CHANGE event for each character.  One
         *  place this is needed is for the type-ahead feature of the editable combo
         *  box.
         */
        mx_internal var batchTextInput:Boolean = true;
        
        /**
         *  @private
         *  True if we've seen a MOUSE_DOWN event and haven't seen the 
         *  corresponding MOUSE_UP event.
         */
        private var mouseDown:Boolean = false;
        
        /**
         *  @private
         *  Hold the previous editingMode while using a specific instance manager
         *  so that the editingMode can be restored when the instance manager is
         *  released.
         */
        private var priorEditingMode:String;
        
        /**
         *  @private
         *  Cache the width constraint as set by the layout in setLayoutBoundsSize()
         *  so that text reflow can be calculated during a subsequent measure pass.
         */
        private var widthConstraint:Number = NaN;
        
        /**
         *  @private
         *  Cache the height constraint as set by the layout in setLayoutBoundsSize()
         *  so that text reflow can be calculated during a subsequent measure pass.
         */
        private var heightConstraint:Number = NaN;
        
        /**
         *  @private
         *  If the selection was via the selectRange() or selectAll() api, remember
         *  that until the next selection is set, either interactively or via the
         *  API.
         */
        private var hasProgrammaticSelectionRange:Boolean = false;
        
        /**
         *  @private
         *  True if this component sizes itself based on its actual
         *  contents.
         */
        mx_internal var autoSize:Boolean = false;
        
        /**
         *  @private
         */
        private var lastMeasuredWidth:Number = NaN;
        
        /**
         *  @private
         */
        private var lastMeasuredHeight:Number = NaN;
        
        /**
         *  @private
         */
        private var lastUnscaledWidth:Number;
        /**
         *  @private
         */
        private var lastUnscaledHeight:Number;
        
        //--------------------------------------------------------------------------
        //
        //  Overridden properties: UIComponent
        //
        //--------------------------------------------------------------------------
        
        //----------------------------------
        //  baselinePosition
        //----------------------------------
        
        /**
         *  @private
         */
        override public function get baselinePosition():Number
        {
            return getStyle("paddingTop") + ascent;
        }
        
        //----------------------------------
        //  enabled
        //----------------------------------
        
        /**
         *  @private
         */
        private var enabledChanged:Boolean = false;
        
        /**
         *  @private
         */
        override public function set enabled(value:Boolean):void
        {
            if (value == super.enabled)
                return;
            
            super.enabled = value;
            enabledChanged = true;
            
            invalidateProperties();
            invalidateDisplayList();
        }
        
        //----------------------------------
        // explicitHeight
        //----------------------------------
        
        /**
         *  @private
         */
        override public function set explicitHeight(value:Number):void
        {
            super.explicitHeight = value;
            
            heightConstraint = NaN;
            
            // Because of autoSizing, the size and display might be impacted.
            invalidateSize();
            invalidateDisplayList();
        }
        
        //----------------------------------
        // explicitWidth
        //----------------------------------
        
        /**
         *  @private
         */
        override public function set explicitWidth(value:Number):void
        {
            super.explicitWidth = value;
            
            widthConstraint = NaN;
            
            // Because of autoSizing, the size and display might be impacted.
            invalidateSize();
            invalidateDisplayList();
        }
        
        //----------------------------------
        // isTruncated
        //----------------------------------
        
        /**
         *  @private
         */
        public function get isTruncated():Boolean
        {
            // This class does not support truncation
            return false;
        }
               
        //----------------------------------
        // percentHeight
        //----------------------------------
        
        /**
         *  @private
         */
        override public function set percentHeight(value:Number):void
        {
            super.percentHeight = value;
            
            heightConstraint = NaN;
            
            // If we were autoSizing and now we are not we need to remeasure.
            invalidateSize();
            invalidateDisplayList();
        }
        
        //----------------------------------
        // percentWidth
        //----------------------------------
        
        /**
         *  @private
         */
        override public function set percentWidth(value:Number):void
        {
            super.percentWidth = value;
            
            widthConstraint = NaN;
            
            // If we were autoSizing and now we are not we need to remeasure.
            invalidateSize();
            invalidateDisplayList();
        }
        
        //--------------------------------------------------------------------------
        //
        //  Properties: ISystemCursorClient
        //
        //--------------------------------------------------------------------------
        
        /**
         *  True if the system cursor should always be shown when the mouse 
         *  moves over the component.  If false, the custom cursor will be shown.
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get showSystemCursor():Boolean
        {
            return editable;        
        }
        
        //--------------------------------------------------------------------------
        //
        //  Properties: IViewport
        //
        //--------------------------------------------------------------------------
        
        //----------------------------------
        //  clipAndEnableScrolling
        //----------------------------------
        
        /**
         *  @private
         */
        private var _clipAndEnableScrolling:Boolean = false;
        
        /**
         *  @private
         */
        private var clipAndEnableScrollingChanged: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 
        {
            return _clipAndEnableScrolling;
        }
        
        /**
         *  @private
         *  Set to true by a scroller when it installs this as a viewport.
         *  Set to false by a scroller when it uninstalls this as a viewport.
         */
        public function set clipAndEnableScrolling(value:Boolean):void 
        {
            if (value == _clipAndEnableScrolling) 
                return;
            
            _clipAndEnableScrolling = value;
            clipAndEnableScrollingChanged = true;
            
            invalidateProperties();
        }
        
        //----------------------------------
        //  contentHeight
        //----------------------------------
        
        /**
         *  @private
         */
        private var _contentHeight:Number = 0;
        
        [Bindable("propertyChange")]
        
        /**
         *  The height of the text.
         *
         *  <p>Due to the fact that the Text Layout Framework
         *  virtualizes TextLines for performance,
         *  this height will initially be an estimate
         *  if the component cannot display all of the text.
         *  If you scroll to the end of the text,
         *  all the TextLines will get composed
         *  and the <code>contentHeight</code> will be exact.</p>
         *
         *  <p>To scroll over the text vertically, vary the 
         *  <code>verticalScrollPosition</code> between 0 and
         *  <code>contentHeight - height</code>.</p>
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get contentHeight():Number
        {
            return _contentHeight;
        }
        
        //----------------------------------
        //  contentWidth
        //----------------------------------
        
        /**
         *  @private
         */
        private var _contentWidth:Number = 0;
        
        [Bindable("propertyChange")]
        
        /**
         *  The width of the text.
         *
         *  <p>Due to the fact that the Text Layout Framework
         *  virtualizes TextLines for performance,
         *  this width will initially be an estimate
         *  if the component cannot display all of the text.
         *  If you scroll to the end of the text,
         *  all the TextLines will get composed
         *  and the <code>contentWidth</code> will be exact.</p>
         *
         *  <p>To scroll over the text horizontally, vary the 
         *  <code>horizontalScrollPosition</code> between 0 and
         *  <code>contentWidth - width</code>.</p>  
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get contentWidth():Number
        {
            return _contentWidth;
        }
        
        //----------------------------------
        //  horizontalScrollPosition
        //----------------------------------

        /**
         *  @private
         */
        private var _horizontalScrollPosition:Number = 0;
        
        /**
         *  @private
         */
        private var horizontalScrollPositionChanged:Boolean = false;
        
        [Bindable("propertyChange")]
        [Inspectable(defaultValue="0", minValue="0.0")]
                
        /**
         *  The number of pixels by which the text is scrolled horizontally.
         *
         *  <p>To scroll over the text horizontally, vary the 
         *  <code>horizontalScrollPosition</code> between 0 and
         *  <code>contentWidth - width</code>.</p>
         *
         *  @default 0
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get horizontalScrollPosition():Number
        {
            return _horizontalScrollPosition;
        }
        
        /**
         *  @private
         */
        public function set horizontalScrollPosition(value:Number):void
        {
            // Convert NaN to 0 to keep TCM happy.
            if (isNaN(value))
                value = 0;
            
            if (value == _horizontalScrollPosition)
                return;
            
            _horizontalScrollPosition = value;
            horizontalScrollPositionChanged = true;
            
            invalidateProperties();
            
            // Note:  TLF takes care of updating the container when the scroll
            // position is set so there is no need for us to invalidate the 
            // display list.
        }
        
        //----------------------------------
        //  verticalScrollPosition
        //----------------------------------
        
        /**
         *  @private
         */
        private var _verticalScrollPosition:Number = 0;
        
        /**
         *  @private
         */
        private var verticalScrollPositionChanged:Boolean = false;
        
        [Bindable("propertyChange")]
        [Inspectable(defaultValue="0", minValue="0.0")]
                
        /**
         *  The number of pixels by which the text is scrolled vertically.
         *
         *  <p>To scroll over the text vertically, vary the 
         *  <code>verticalScrollPosition</code> between 0 and
         *  <code>contentHeight - height</code>.</p>
         *
         *  @default 0
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get verticalScrollPosition():Number
        {
            return _verticalScrollPosition;
        }
        
        /**
         *  @private
         */
        public function set verticalScrollPosition(value:Number):void
        {
            // Convert NaN to 0 to keep TCM happy.
            if (isNaN(value))
                value = 0;
            
            if (value == _verticalScrollPosition)
                return;
            
            _verticalScrollPosition = value;
            verticalScrollPositionChanged = true;
            
            invalidateProperties();
            
            // Note:  TLF takes care of updating the container when the scroll
            // position is set so there is no need for us to invalidate the 
            // display list.
        }
        
        //--------------------------------------------------------------------------
        //
        //  Properties
        //
        //--------------------------------------------------------------------------
        
        //----------------------------------
        //  content
        //----------------------------------
        
        /**
         *  @private
         */
        private var _content:Object;
        
        /**
         *  @private
         */
        private var contentChanged:Boolean = false;
        
        /**
         *  @private
         *  This metadata tells the MXML compiler to disable some of its default
         *  interpretation of the value specified for the 'content' property.
         *  Normally, for properties of type Object, it assumes that things
         *  looking like numbers are numbers and things looking like arrays
         *  are arrays. But <content>1</content> should generate code to set the
         *  content to  the String "1", not the int 1, and <content>[1]</content>
         *  should set it to the String "[1]", not the Array [ 1 ].
         *  However, {...} continues to be interpreted as a databinding
         *  expression, and @Resource(...), @Embed(...), etc.
         *  as compiler directives.
         *  Similar metadata on TLF classes causes the same rules to apply
         *  within <p>, <span>, etc.
         */
        [RichTextContent]
                
        /**
         *  This property is intended for use in MXML at compile time;
         *  to get or set rich text content at runtime,
         *  please use the <code>textFlow</code> property instead.
         *
         *  <p>The <code>content</code> property is the default property
         *  for RichEditableText, so that you can write MXML such as
         *  <pre>
         *  &lt;s:RichEditableText&gt;Hello &lt;s:span fontWeight="bold"/&gt;World&lt;/s:span&gt;&lt;/s:RichEditableText&gt;
         *  </pre>
         *  and have the String and SpanElement that you specify
         *  as the content be used to create a TextFlow.</p>
         *
         *  <p>This property is typed as Object because you can set it to
         *  to a String, a FlowElement, or an Array of Strings and FlowElements.
         *  In the example above, you are specifying the content
         *  to be a 2-element Array whose first element is the String
         *  "Hello" and whose second element is a SpanElement with the text
         *  "World" in boldface.</p>
         * 
         *  <p>No matter how you specify the content, it gets converted
         *  into a TextFlow, and when you get this property, you will get
         *  the resulting TextFlow.</p>
         * 
         *  <p>Adobe recommends using <code>textFlow</code> property
         *  to get and set rich text content at runtime,
         *  because it is strongly typed as a TextFlow
         *  rather than as an Object.
         *  A TextFlow is the canonical representation
         *  for rich text content in the Text Layout Framework.</p>
         * 
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get content():Object
        {
            return textFlow;
        }
        
        /**
         *  @private
         */   
        public function set content(value:Object):void
        {
            // Treat setting the 'content' to null
            // as if 'text' were being set to the empty String
            // (which is the default state).
            if (value == null)
            {
                text = "";
                return;
            }
            
            if (value == _content)
                return;
            
            _content = value;
            contentChanged = true;
            source = "content";
            
            // Of 'text', 'textFlow', and 'content', the last one set wins.
            textChanged = false;
            textFlowChanged = false;
            
            // The other two are now invalid and must be recalculated when needed.
            _text = null;
            _textFlow = null;
            
            invalidateProperties();
            invalidateSize();
            invalidateDisplayList();
            
            dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));                                   
        }
        
        //----------------------------------
        //  displayAsPassword
        //----------------------------------
        
        /**
         *  @private
         */
        private var _displayAsPassword:Boolean = false;
        
        /**
         *  @private
         */
        private var displayAsPasswordChanged:Boolean = false;
        
        [Inspectable(category="General", defaultValue="false")]
        
        /**
         *  @copy flash.text.TextField#displayAsPassword
         *
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get displayAsPassword():Boolean
        {
            return _displayAsPassword;
        }
        
        /**
         *  @private
         */
        public function set displayAsPassword(value:Boolean):void
        {
            if (value == _displayAsPassword)
                return;
            
            _displayAsPassword = value;
            displayAsPasswordChanged = true;
            
            invalidateProperties();
            invalidateSize();
            invalidateDisplayList();
        }
        
        //----------------------------------
        //  editable
        //----------------------------------
        
        /**
         *  @private
         */
        private var _editable:Boolean = true;
        
        /**
         *  @private
         */
        private var editableChanged:Boolean = false;
        
        [Inspectable(category="General", defaultValue="true")]
        
        /**
         *  A flag indicating whether the user is allowed
         *  to edit the text in this control.
         *
         *  <p>If <code>true</code>, the mouse cursor will change to an i-beam
         *  when over the bounds of this control.
         *  If <code>false</code>, the mouse cursor will remain an arrow.</p>
         *
         *  <p>If this property is <code>true</code>,
         *  the <code>selectable</code> property is ignored.</p>
         *
         *  @default true
         *
         *  @see spark.components.RichEditableText#selectable
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get editable():Boolean
        {
            return _editable;
        }
        
        /**
         *  @private
         */
        public function set editable(value:Boolean):void
        {
            if (value == _editable)
                return;
            
            _editable = value;
            editableChanged = true;
            
            invalidateProperties();
            invalidateDisplayList();
        }
        
        //----------------------------------
        //  editingMode
        //----------------------------------
        
        [Inspectable(category="General", defaultValue="readWrite", enumeration="readOnly,readWrite,readSelect")]

        /**
         *  @private
         *  The editingMode of this component's TextContainerManager.
         *  Note that this is not a public property
         *  and does not use the invalidation mechanism.
         */
        private function get editingMode():String
        {
            // Note: this could be called before all properties are committed.
            
            if (enabledChanged || editableChanged || selectableChanged)
            {
                updateEditingMode();
                
                enabledChanged = false;
                editableChanged = false;
                selectableChanged = false;
            }
            
            return _textContainerManager.editingMode;
        }
        
        /**
         *  @private
         */
        private function set editingMode(value:String):void
        {
            var lastEditingMode:String = _textContainerManager.editingMode;
            
            if (lastEditingMode == value)
                return;
            
            _textContainerManager.editingMode = value;
            
            // Make sure the selection manager selection is in sync with the
            // current selection.
            if (value != EditingMode.READ_ONLY && 
                _selectionAnchorPosition != -1 && _selectionActivePosition != -1)
            {
                var selectionManager:ISelectionManager = 
                    _textContainerManager.beginInteraction();
                
                selectionManager.selectRange(
                    _selectionAnchorPosition, _selectionActivePosition);
                
                _textContainerManager.endInteraction();
            }
        }
        
        //----------------------------------
        //  enableIME
        //----------------------------------
        
        /**
         *  A flag that indicates whether the IME should
         *  be enabled when the component receives focus.
         *
         *  @returns true if the component is editable and it is not displaying a password.
         * 
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get enableIME():Boolean
        {
            return editable && !displayAsPassword;
        }
        
        //----------------------------------
        //  heightInLines
        //----------------------------------
        
        /**
         *  @private
         */
        private var _heightInLines:Number = NaN;
        
        /**
         *  @private
         */
        private var heightInLinesChanged:Boolean = false;
        
        [Inspectable(category="General", minValue="0.0")]
        
        /**
         *  The default height of the control, measured in lines.
         *
         *  <p>The control's formatting styles, such as <code>fontSize</code>
         *  and <code>lineHeight</code>, are used to calculate the line height
         *  in pixels.</p>
         *
         *  <p>You would, for example, set this property to 5 if you want
         *  the height of the RichEditableText to be sufficient
         *  to display five lines of text.</p>
         *
         *  <p>If this property is <code>NaN</code> (the default),
         *  then the component's default height will be determined
         *  from the text to be displayed.</p>
         *  
         *  <p>This property will be ignored if you specify an explicit height,
         *  a percent height, or both <code>top</code> and <code>bottom</code>
         *  constraints.</p>
         *
         *  <p>This property will also be ignored if the <code>typicalText</code> 
         *  property is specified.</p>
         * 
         *  <p>RichEditableText's <code>measure()</code> method uses
         *  <code>widthInChars</code> and <code>heightInLines</code>
         *  to determine the <code>measuredWidth</code>
         *  and <code>measuredHeight</code>.
         *  These are similar to the <code>cols</code> and <code>rows</code>
         *  of an HTML TextArea.</p>
         *
         *  <p>Since both <code>widthInChars</code> and <code>heightInLines</code>
         *  default to <code>NaN</code>, RichTextEditable "autosizes" by default:
         *  it starts out very small if it has no text, grows in width as you
         *  type, and grows in height when you press Enter to start a new line.</p>
         *
         *  @default NaN
         *
         *  @see spark.components.RichEditableText#widthInChars
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get heightInLines():Number
        {
            return _heightInLines;
        }
        
        /**
         *  @private
         */
        public function set heightInLines(value:Number):void
        {
            if (value == _heightInLines)
                return;
            
            _heightInLines = value;
            heightInLinesChanged = true;
            
            heightConstraint = NaN;
            
            invalidateProperties();
            invalidateSize();
            invalidateDisplayList();
        }
        
        //----------------------------------
        //  imeMode
        //----------------------------------
        
        /**
         *  @private
         */
        private var _imeMode:String = null;
        
        /**
         *  Specifies the IME (input method editor) mode.
         *  The IME enables users to enter text in Chinese, Japanese, and Korean.
         *  Flex sets the specified IME mode when the control gets the focus,
         *  and sets it back to the previous value when the control loses the focus.
         *
         *  <p>The flash.system.IMEConversionMode class defines constants for the
         *  valid values for this property.
         *  You can also specify <code>null</code> to specify no IME.</p>
         *
         *  @default null
         * 
         *  @see flash.system.IMEConversionMode
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get imeMode():String
        {
            return _imeMode;
        }
        
        /**
         *  @private
         */
        public function set imeMode(value:String):void
        {
            _imeMode = value;
        }
        
        //----------------------------------
        //  lineBreak
        //----------------------------------
        
        [Inspectable(environment="none")]

        /**
         *  @private
         * 
         *  This property is only defined to implement the IEditableText
         *  interface. The lineBreak style should be used instead of this
         *  property.
         */
        public function get lineBreak():String
        {
            return getStyle("lineBreak");
        }
        
        /**
         *  @private
         */
        public function set lineBreak(value:String):void
        {
            setStyle("lineBreak", value);
        }
        
        //----------------------------------
        //  maxChars
        //----------------------------------
        
        /**
         *  @private
         */
        private var _maxChars:int = 0;
        
        [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 
        {
            return _maxChars;
        }
        
        /**
         *  @private
         */
        public function set maxChars(value:int):void
        {
            _maxChars = value;
        }
        
        //----------------------------------
        //  multiline
        //----------------------------------
        
        /**
         *  @private
         */
        private var _multiline:Boolean = true;
        
        [Inspectable(category="General", defaultValue="true")]
        
        /**
         *  Determines whether the user can enter multiline text.
         *
         *  <p>If <code>true</code>, the Enter key starts a new paragraph.
         *  If <code>false</code>, the Enter key doesn't affect the text
         *  but causes the RichEditableText to dispatch an <code>"enter"</code> 
         *  event.  If you paste text into the RichEditableText with a multiline 
         *  value of <code>false</code>, newlines are stripped out of the text. </p>  
         * 
         *  @default true
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get multiline():Boolean 
        {
            return _multiline;
        }
        
        /**
         *  @private
         */
        public function set multiline(value:Boolean):void
        {
            _multiline = value;
        }
 
        //----------------------------------
        //  restrict
        //----------------------------------
        
        /**
         *  @private
         */
        private var _restrict:String = null;
        
        [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 
        {
            return _restrict;
        }
        
        /**
         *  @private
         */
        public function set restrict(value:String):void
        {
            _restrict = value;
        }
        
        //----------------------------------
        //  selectable
        //----------------------------------
        
        /**
         *  @private
         */
        private var _selectable:Boolean = true;
        
        /**
         *  @private
         */
        private var selectableChanged:Boolean = false;
        
        [Inspectable(category="General", defaultValue="true")]
        
        /**
         *  A flag indicating whether the content is selectable
         *  with the mouse, or with the keyboard when the control
         *  has the keyboard focus.
         *
         *  <p>Making the text selectable lets you copy text from the control.</p>
         *
         *  <p>This property is ignored if the <code>editable</code>
         *  property is <code>true</code>.</p>
         *
         *  @default true
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get selectable():Boolean
        {
            return _selectable;
        }
        
        /**
         *  @private
         */
        public function set selectable(value:Boolean):void
        {
            if (value == _selectable)
                return;
            
            _selectable = value;
            selectableChanged = true;
            
            invalidateProperties();
            invalidateDisplayList();
        }
        
        //----------------------------------
        //  selectionActivePosition
        //----------------------------------
        
        /**
         *  @private
         */
        private var _selectionActivePosition:int = -1;
        
        [Bindable("selectionChange")]
        [Inspectable(category="General", defaultValue="-1")]
        
        /**
         *  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>The active position may be either the start
         *  or the end of the selection.</p>
         *
         *  <p>For example, if you drag-select from position 12 to position 8,
         *  then <code>selectionAnchorPosition</code> will be 12
         *  and <code>selectionActivePosition</code> will be 8,
         *  and when you press Left-Arrow <code>selectionActivePosition</code>
         *  will become 7.</p>
         *
         *  <p>A value of -1 indicates "not set".</p>
         *
         *  @default -1
         *
         *  @see spark.components.RichEditableText#selectionAnchorPosition
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get selectionActivePosition():int
        {
            return _selectionActivePosition;
        }
        
        //----------------------------------
        //  selectionAnchorPosition
        //----------------------------------
        
        /**
         *  @private
         */
        private var _selectionAnchorPosition:int = -1;
        
        [Bindable("selectionChange")]
        [Inspectable(category="General", defaultValue="-1")]
        
        /**
         *  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>The anchor position may be either the start
         *  or the end of the selection.</p>
         *
         *  <p>For example, if you drag-select from position 12 to position 8,
         *  then <code>selectionAnchorPosition</code> will be 12
         *  and <code>selectionActivePosition</code> will be 8,
         *  and when you press Left-Arrow <code>selectionActivePosition</code>
         *  will become 7.</p>
         *
         *  <p>A value of -1 indicates "not set".</p>
         *
         *  @default -1
         *
         *  @see spark.components.RichEditableText#selectionActivePosition
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get selectionAnchorPosition():int
        {
            return _selectionAnchorPosition;
        }
        
        //----------------------------------
        //  selectionHighlighting
        //----------------------------------
        
        /**
         *  @private
         */
        private var _selectionHighlighting:String =
            TextSelectionHighlighting.WHEN_FOCUSED;
        
        /**
         *  @private
         *  To indicate either selection highlighting or selection styles have
         *  changed.
         */
        private var selectionFormatsChanged:Boolean = false;
        
        [Inspectable(category="General", enumeration="always,whenActive,whenFocused", defaultValue="whenFocused")]
        
        /**
         *  Determines when the text selection is highlighted.
         *  
         *  <p>The allowed values are specified by the
         *  spark.components.TextSelectionHighlighting class.
         *  Possible values are <code>TextSelectionHighlighting.WHEN_FOCUSED</code>,
         *  <code>TextSelectionHighlighting.WHEN_ACTIVE</code>,
         *  and <code>TextSelectionHighlighting.ALWAYS</code>.</p>
         *
         *  <p><code>WHEN_FOCUSED</code> shows the text selection
         *  only when the component has keyboard focus.</p>
         *  
         *  <p><code>WHEN_ACTIVE</code> shows the text selection whenever
         *  the component's window is active, even if the component
         *  doesn't have the keyboard focus.</p>
         *
         *  <p><code>ALWAYS</code> shows the text selection,
         *  even if the component doesn't have the keyboard focus
         *  or if the component's window isn't the active window.</p>
         *  
         *  @default TextSelectionHighlighting.WHEN_FOCUSED
         *  
         *  @see spark.components.TextSelectionHighlighting
         * 
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get selectionHighlighting():String 
        {
            return _selectionHighlighting;
        }
        
        /**
         *  @private
         */
        public function set selectionHighlighting(value:String):void
        {
            if (value == _selectionHighlighting)
                return;
            
            _selectionHighlighting = value;
            selectionFormatsChanged = true;
            
            invalidateProperties();
            invalidateDisplayList();
        }
        
        //----------------------------------
        //  text
        //----------------------------------
        
        /**
         *  @private
         */
        private var _text:String = "";
        
        /**
         *  @private
         */
        private var textChanged:Boolean = false;
        
        [Bindable("change")]
        [Inspectable(category="General", defaultValue="")]
        
        /**
         *  The text String displayed by this component.
         *
         *  <p>Setting this property affects the <code>textFlow</code> property
         *  and vice versa.</p>
         *
         *  <p>If you set the <code>text</code> to a String such as
         *  <code>"Hello World"</code> and get the <code>textFlow</code>,
         *  it will be a TextFlow containing a single ParagraphElement
         *  with a single SpanElement.</p>
         *
         *  <p>If you set the <code>text</code> to null, it will be
         *  set to the default value which is an empty string.</p>
         * 
         *  <p>If the text contains explicit line breaks --
         *  CR ("\r"), LF ("\n"), or CR+LF ("\r\n") --
         *  then the content will be set to a TextFlow
         *  which contains multiple paragraphs, each with one span.</p>
         *
         *  <p>If you set the <code>textFlow</code> and get the <code>text</code>,
         *  the text in each paragraph will be separated by a single
         *  LF ("\n").</p>
         *
         *  <p>Setting this property also affects the properties
         *  specifying the control's scroll position and the text selection.
         *  It resets the <code>horizontalScrollPosition</code>
         *  and <code>verticalScrollPosition</code> to 0.
         *  Starting with Flex 4.6, the <code>selectionAnchorPosition</code> and 
         *  <code>selectionActivePosition</code> are preserved.
         *  Previously, the <code>selectionAnchorPosition</code>
         *  and <code>selectionActivePosition</code> were set
         *  to -1 to clear the selection.</p>
         *
         *  @default ""
         *
         *  @see spark.components.RichEditableText#textFlow
         *  @see spark.components.RichEditableText#horizontalScrollPosition
         *  @see spark.components.RichEditableText#verticalScrollPosition
         *  @see spark.components.RichEditableText#selectionAnchorPosition
         *  @see spark.components.RichEditableText#selectionActivePosition
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get text():String 
        {
            // Note: if displayAsPassword, _text will contain the actual text and the text flow will
            // contain the same number of passwordChars.
            
            // Go to the source if there isn't a pending change.  getText has its own buffering and 
            // only extracts the text from the TextFlow when it is damaged. 
            if (_textContainerManager && !textChanged && !textFlowChanged && !contentChanged && !displayAsPassword)
                return _textContainerManager.getText("\n");
            
            // Extracting the plaintext from a TextFlow is somewhat expensive,
            // as it involves iterating over the leaf FlowElements in the TextFlow.
            // Therefore we do this extraction only when necessary, namely when
            // you first set the 'content' or the 'textFlow'
            // (or mutate the TextFlow), and then get the 'text'.
            if (_text == null)
            {
                // If 'content' was last set,
                // we have to first turn that into a TextFlow.
                if (_content != null)
                    _textFlow = createTextFlowFromContent(_content);
                
                // Once we have a TextFlow, we can export its plain text.
                _text = staticPlainTextExporter.export(
                    _textFlow, ConversionType.STRING_TYPE) as String;
            }
            
            return _text;
        }
        
        /**
         *  @private
         *  This will create a TextFlow with a single paragraph with a single span 
         *  with exactly the text specified.  If there is whitespace and line 
         *  breaks in the text, they will remain, regardless of the settings of
         *  the lineBreak and whiteSpaceCollapse styles.
         */
        public function set text(value:String):void
        {
            // Treat setting the 'text' to null
            // as if it were set to the empty String
            // (which is the default state).
            if (value == null)
                value = "";
            
            // If value is the same as _text, make sure if was not produced from
            // setting 'textFlow' or 'content'.  For example, if you set a TextFlow 
            // corresponding to "Hello <span color="OxFF0000">World</span>"
            // and then get the 'text', it will be the String "Hello World"
            // But if you then set the 'text' to "Hello World"
            // this represents a change: the "World" should no longer be red.
            //
            // Note: this is needed to stop two-binding from recursing.
            if (source == "text" && text == value)
                return;
            
            _text = value;
            textChanged = true;
            source = "text";
            
            // Of 'text', 'textFlow', and 'content', the last one set wins.
            textFlowChanged = false;
            contentChanged = false;
            
            // The other two are now invalid and must be recalculated when needed.
            _textFlow = null;
            _content = null;
            
            invalidateProperties();
            invalidateSize();
            invalidateDisplayList();
            
            dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));                                   
        }
        
        //----------------------------------
        //  textContainerManager
        //----------------------------------
        
        /**
         *  @private
         */
        private var _textContainerManager:RichEditableTextContainerManager;
        
        /**
         *  @private
         *  The TLF TextContainerManager instance that displays,
         *  scrolls, and edits the text in this component.
         */
        mx_internal function get textContainerManager():TextContainerManager
        {
            return _textContainerManager;
        }
        
        //----------------------------------
        //  textFlow
        //----------------------------------
        
        /**
         *  @private
         *  Storage for the textFlow property.
         */
        private var _textFlow:TextFlow;
        
        /**
         *  @private
         */
        private var textFlowChanged:Boolean = false;
        
        /**
         *  The TextFlow representing the rich text displayed by this component.
         * 
         *  <p>A TextFlow is the most important class
         *  in the Text Layout Framework (TLF).
         *  It is the root of a tree of FlowElements
         *  representing rich text content.</p>
         *
         *  <p>You normally create a TextFlow from TLF markup
         *  using the <code>TextFlowUtil.importFromString()</code>
         *  or <code>TextFlowUtil.importFromXML()</code> methods.
         *  Alternately, you can use TLF's TextConverter class
         *  (which can import a subset of HTML) or build a TextFlow
         *  using methods like <code>addChild()</code> on TextFlow.</p>
         *
         *  <p>Setting this property affects the <code>text</code> property
         *  and vice versa.</p>
         *
         *  <p>If you set the <code>textFlow</code> and get the <code>text</code>,
         *  the text in each paragraph will be separated by a single
         *  LF ("\n").</p>
         *
         *  <p>If you set the <code>text</code> to a String such as
         *  <code>"Hello World"</code> and get the <code>textFlow</code>,
         *  it will be a TextFlow containing a single ParagraphElement
         *  with a single SpanElement.</p>
         *
         *  <p>If the text contains explicit line breaks --
         *  CR ("\r"), LF ("\n"), or CR+LF ("\r\n") --
         *  then the content will be set to a TextFlow
         *  which contains multiple paragraphs, each with one span.</p>
         *
         *  <p>Setting this property also affects the properties
         *  specifying the control's scroll position and the text selection.
         *  It resets the <code>horizontalScrollPosition</code>
         *  and <code>verticalScrollPosition</code> to 0,
         *  and it sets the <code>selectionAnchorPosition</code>
         *  and <code>selectionActivePosition</code>
         *  to -1 to clear the selection.</p>
         *
         *  <p>To turn a TextFlow object into TLF markup,
         *  use the <code>TextFlowUtil.export()</code> markup.</p>
         *
         *  <p>A single TextFlow cannot be shared by multiple instances
         *  of RichEditableText.
         *  To display the same text in a second instance, you must create
         *  a second TextFlow, either by using <code>TextFlowUtil.export()</code>
         *  and <code>TextFlowUtil.importFromXML()</code> or by using
         *  the <code>deepCopy()</code> method on TextFlow.</p>
         *
         *  @see spark.utils.TextFlowUtil#importFromString()
         *  @see spark.utils.TextFlowUtil#importFromXML()
         *  @see spark.components.RichEditableText#text
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 2.5
         *  @productversion Flex 4.5
         */
        public function get textFlow():TextFlow
        {
            // Note: this could be called before all properties are committed.
            
            // We might not have a valid _textFlow for two reasons:
            // either because the 'text' was set (which is the state
            // after construction) or because the 'content' was set.
            if (!_textFlow)
            {
                if (_content != null)
                {
                    _textFlow = createTextFlowFromContent(_content);
                    _content = null;
                }
                else
                {
                    _textFlow = staticPlainTextImporter.importToFlow(_text);
                }
                textFlowChanged = true;
            }
            
            // Make sure the interactionManager and controller are added to this textFlow.           
            if (textChanged || contentChanged || textFlowChanged)
            {
                _textContainerManager.setTextFlow(_textFlow);
                textChanged = contentChanged = textFlowChanged = false;
            }
            
            // If not read-only, make sure the textFlow has a composer in
            // place so that it can be modified by the caller if desired.
            if (editingMode != EditingMode.READ_ONLY)
            {
                _textContainerManager.beginInteraction();
                _textContainerManager.endInteraction();
            }
            
            return _textFlow;
        }
        
        /**
         *  @private
         */
        public function set textFlow(value:TextFlow):void
        {
            // Treat setting the 'textFlow' to null
            // as if 'text' were being set to the empty String
            // (which is the default state).
            if (value == null)
            {
                text = "";
                return;
            }
            
            if (value == _textFlow)
                return;
            
            _textFlow = value;
            textFlowChanged = true;
            source = "textFlow";
            
            // Of 'text', 'textFlow', and 'content', the last one set wins.
            textChanged = false;
            contentChanged = false;
            
            // The other two are now invalid and must be recalculated when needed.
            _text = null
            _content = null;
            
            invalidateProperties();
            invalidateSize();
            invalidateDisplayList();
            
            dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));                                   
        }
        
        //----------------------------------
        //  typicalText
        //----------------------------------
        
        /**
         *  @private
         */
        private var typicalTextChanged:Boolean;
                
        /**
         *  @private
         */
        private var _typicalText:String;
        
        /**
         *  @private
         *  Used when _typicalText is multiline
         */
        private var _typicalTextFlow:TextFlow;
        
        [Inspectable(category="General", defaultValue="null")]

        /**
         *  Text that is used to determine
         *  the default width and height of the control, 
         *  without actually being displayed.
         *
         *  <p>This property will be ignored if you specify an explicit width,
         *  a percent width, or both <code>left</code> and <code>right</code>
         *  constraints.</p>
         *
         *  <p>Use of this property causes the <code>widthInChars</code> 
         *  and <code>heightInLines</code> properties to be ignored. </p>
         *
         *  @default null
         *
         *  @see spark.primitives.heightInLines
         *  @see spark.primitives.widthInChars
         *
         *  @langversion 3.0
         *  @playerversion Flash 10.2
         *  @playerversion AIR 2.0
         *  @productversion Flex 4.5
         */
        public function get typicalText():String 
        {
            return _typicalText;
        }
        
        /**
         *  @private
         */
        public function set typicalText(value:String):void
        {
            if (value == _typicalText)
                return;
            
            _typicalText = value;
            
            typicalTextChanged = true;
            
            invalidateProperties();
            invalidateSize();
            invalidateDisplayList();
        }
        
        //----------------------------------
        //  widthInChars
        //----------------------------------
        
        /**
         *  @private
         *  These are measured in ems.
         */
        private var _widthInChars:Number = NaN;
        
        /**
         *  @private
         */
        private var widthInCharsChanged:Boolean = true;
        
        [Inspectable(category="General", minValue="0.0")]

        /**
         *  The default width of the control, measured in em units.
         *
         *  <p>An em is a unit of typographic measurement
         *  equal to the point size.
         *  It is not necessarily exactly the width of the "M" character,
         *  but in many fonts the "M" is about one em wide.
         *  The control's <code>fontSize</code> style is used,
         *  to calculate the em unit in pixels.</p>
         *
         *  <p>You would, for example, set this property to 20 if you want
         *  the width of the RichEditableText to be sufficient
         *  to display about 20 characters of text.</p>
         *
         *  <p>If this property is <code>NaN</code> (the default),
         *  then the component's default width will be determined
         *  from the text to be displayed.</p>
         *
         *  <p>This property will be ignored if you specify an explicit width,
         *  a percent width, or both <code>left</code> and <code>right</code>
         *  constraints.</p>
         *
         *  <p>This property will also be ignored if the <code>typicalText</code> 
         *  property is specified.</p>
         * 
         *  <p>RichEditableText's <code>measure()</code> method uses
         *  <code>widthInChars</code> and <code>heightInLines</code>
         *  to determine the <code>measuredWidth</code>
         *  and <code>measuredHeight</code>. 
         *  These are similar to the <code>cols</code> and <code>rows</code>
         *  of an HTML TextArea.</p>
         *
         *  <p>Since both <code>widthInChars</code> and <code>heightInLines</code>
         *  default to <code>NaN</code>, RichTextEditable "autosizes" by default:
         *  it starts out very small if it has no text, grows in width as you
         *  type, and grows in height when you press Enter to start a new line.</p>
         *
         *  @default NaN
         *
         *  @see spark.primitives.heightInLines
         *
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function get widthInChars():Number 
        {
            return _widthInChars;
        }
        
        /**
         *  @private
         */
        public function set widthInChars(value:Number):void
        {
            if (value == _widthInChars)
                return;
            
            _widthInChars = value;
            widthInCharsChanged = true;
            
            widthConstraint = NaN;
            
            invalidateProperties();
            invalidateSize();
            invalidateDisplayList();
        }
        
        //--------------------------------------------------------------------------
        //
        //  Overridden Methods: UIComponent
        //
        //--------------------------------------------------------------------------
        
        /**
         *  @private
         */
        override protected function initializeAccessibility():void
        {
            if (RichEditableText.createAccessibilityImplementation != null)
                RichEditableText.createAccessibilityImplementation(this);
        }
        
        /**
         *  @private
         */
        override public function parentChanged(p:DisplayObjectContainer):void
        {
            if (focusManager)
            {
                focusManager.removeEventListener(FlexEvent.FLEX_WINDOW_ACTIVATE, 
                    _textContainerManager.activateHandler)
                focusManager.removeEventListener(FlexEvent.FLEX_WINDOW_DEACTIVATE, 
                    _textContainerManager.deactivateHandler)
            }
            
            super.parentChanged(p);
            
            if (focusManager)
            {
                addActivateHandlers();
            }
            else
            {
                // if no focusmanager yet, add capture phase to detect when it
                // gets added
                if (systemManager)
                    systemManager.getSandboxRoot().addEventListener(FlexEvent.ADD_FOCUS_MANAGER, 
                        addFocusManagerHandler, true, 0, true)
                else
                    // no systemManager yet?  Check again when added to stage
                    addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
            }
            
        }
        
        /**
         *  @private
         */
        override public function removeChild(child:DisplayObject):DisplayObject
        {
            // not sure why this happens but it does if you just change
            // the embeddedFont context
            if (!child.parent)
                return child;
            
            if (child.parent == this)
                return super.removeChild(child);
            
            return child.parent.removeChild(child);
        }
        
        /**
         *  @private
         */
        override protected function commitProperties():void
        {
            super.commitProperties();
            
            updateStylesIfChanged();
            
            var oldAnchorPosition:int = _selectionAnchorPosition;
            var oldActivePosition:int = _selectionActivePosition;
            
            // EditingMode needs to be current before attempting to set a
            // selection below.
            if (enabledChanged || selectableChanged || editableChanged)
            {
                updateEditingMode();
                
                enabledChanged = false;
                editableChanged = false;
                selectableChanged = false;          
            }
            
            // Only one of textChanged, textFlowChanged, and contentChanged
            // will be true; the other two will be false because each setter
            // guarantees this.
            
            if (textChanged)
            {
                if (FlexVersion.compatibilityVersion > FlexVersion.VERSION_4_5) 
                    preserveSelectionOnSetText = true;

                // If the text has linebreaks (CR, LF, or CF+LF)
                // create a multi-paragraph TextFlow from it
                // and use the TextFlowTextLineFactory to render it.
                // Otherwise the StringTextLineFactory will put
                // all of the lines into a single paragraph
                // and FTE performance will degrade on a large paragraph.
                if (_text.indexOf("\n") != -1 || _text.indexOf("\r") != -1)
                {
                    _textFlow = staticPlainTextImporter.importToFlow(_text);
                    _textContainerManager.setTextFlow(_textFlow);
                }
                else
                {
                    _textContainerManager.setText(_text);
                }                
            }
            else if (textFlowChanged)
            {
                _textContainerManager.setTextFlow(_textFlow);
            }
            else if (contentChanged)
            {
                _textFlow = createTextFlowFromContent(_content);
                _textContainerManager.setTextFlow(_textFlow);
                
                // Content converted to textFlow.
                _content = null;
            }                        
            
            if (textChanged || textFlowChanged || contentChanged)
            {
                lastGeneration = _textFlow ? _textFlow.generation : 0;
                lastContentBoundsGeneration = 0;
                
                // Handle the case where the initial text, textFlow or content 
                // is displayed as a password.
                if (displayAsPassword)
                    displayAsPasswordChanged = true;
                
                // New text so remove any leftover constraints.
                // Used if an item renderer is being recycled.
                widthConstraint = NaN;
                heightConstraint = NaN;
                
                textChanged = false;
                textFlowChanged = false;
                contentChanged = false;
                invalidateSize();
                invalidateDisplayList();
            }
            
            // If displayAsPassword changed, it only applies to the display, 
            // not the underlying text.
            if (displayAsPasswordChanged)
            {
                preserveSelectionOnSetText = true;
                
                // If there is any text, convert it to the passwordChar.
                if (displayAsPassword)
                {
                    // Make sure _text is set with the actual text before we
                    // change the displayed text.
                    _text = _textContainerManager.getText("\n");
                    
                    // Paragraph terminators are lost during this substitution.
                    var textToDisplay:String = StringUtil.repeat(
                        passwordChar, _text.length);
                    
                    _textContainerManager.setText(textToDisplay);                            
                }
                else
                {
                    // Text was displayed as password.  Now display as plain text.
                    _textContainerManager.setText(_text);
                }
                
                // When TLF text is set above, TLF's textFlow is recreated so reset
                // our copy of the textFlow and the generation.
                _textFlow = null;
                lastGeneration = 0;
                lastContentBoundsGeneration = 0;
                
                displayAsPasswordChanged = false;
            }
            
            if (preserveSelectionOnSetText)
            {
                preserveSelectionOnSetText = false;
                
                if (oldAnchorPosition != -1)
                {
                    // This will return null if editingMode = readOnly which is true when either
                    // editable is false and/or enabled is false.
                    var selManager:ISelectionManager = _textContainerManager.beginInteraction();                    

                    // The visible selection will be refreshed during the update.
                    if (selManager)
                    {
                        selManager.selectRange(oldAnchorPosition, oldActivePosition);        
                        _textContainerManager.endInteraction();
                    }
                }           
            }
            
            if (clipAndEnableScrollingChanged)
            {
                // The TLF code seems to check for !off.
                _textContainerManager.horizontalScrollPolicy = "auto";
                _textContainerManager.verticalScrollPolicy = "auto";
                
                clipAndEnableScrollingChanged = false;
            }
            
            if (horizontalScrollPositionChanged)
            {
                var oldHorizontalScrollPosition:Number = 
                    _textContainerManager.horizontalScrollPosition;
                
                _textContainerManager.horizontalScrollPosition =
                    _horizontalScrollPosition; 
                
                dispatchPropertyChangeEvent("horizontalScrollPosition",
                    oldHorizontalScrollPosition, _horizontalScrollPosition);
                
                horizontalScrollPositionChanged = false;            
            }
            
            if (verticalScrollPositionChanged)
            {
                var oldVerticalScrollPosition:Number = 
                    _textContainerManager.verticalScrollPosition;
                
                _textContainerManager.verticalScrollPosition =
                    _verticalScrollPosition;
                
                dispatchPropertyChangeEvent("verticalScrollPosition",
                    oldVerticalScrollPosition, _verticalScrollPosition);
                
                verticalScrollPositionChanged = false;            
            }
        }
        
        /**
         *  @private
         */
        override protected function canSkipMeasurement():Boolean
        {
            autoSize = false;
            return super.canSkipMeasurement();
        }
        
        /**
         *  @private
         */
        override protected function measure():void 
        {
            var bounds:Rectangle;
            
            // If the damage handler is called while measuring text, this means
            // the text lines are damaged and the display needs to be updated. 
            // This flag tells the handler to invalidate just the display list.
            inMeasureMethod = true;
            
            lastMeasuredWidth = measuredWidth;
            lastMeasuredHeight = measuredHeight;
            
            super.measure();
            
            // Styles can be changed in event handlers while in the middle
            // of the component lifecycle.  Make sure they are not stale when
            // composing text.
            updateStylesIfChanged();
            
            // percentWidth and/or percentHeight will come back in as constraints
            // on the remeasure if we're autoSizing.
            
            // TODO:(cframpto) implement blockProgression rl for autoSize
            
            if (isMeasureFixed()) 
            {            
                autoSize = false;
                
                if (typicalText)
                {
                    if (typicalTextChanged)
                    {
                        // If the text has linebreaks (CR, LF, or CF+LF)
                        // create a multi-paragraph TextFlow from it
                        // and use the TextFlowTextLineFactory to render it.
                        // Otherwise the StringTextLineFactory will put
                        // all of the lines into a single paragraph
                        // and FTE performance will degrade on a large paragraph.
                        if (_typicalText.indexOf("\n") != -1 || _typicalText.indexOf("\r") != -1)
                        {
                            _typicalTextFlow = staticPlainTextImporter.importToFlow(_typicalText);
                            // this helped get the factory to generate the same bounds as the
                            // composer
                            _typicalTextFlow.hostFormat = _textContainerManager.hostFormat;
                        }
                        else
                            _typicalTextFlow = null;
                        typicalTextChanged = false;
                    }
                    // if multiline...
                    if (_typicalTextFlow)
                    {
                        // create the factory if needed
                        if (!staticTextFlowFactory)
                        {
                            staticTextFlowFactory = new TextFlowTextLineFactory();
                            // set bounds to natural bounds
                            staticTextFlowFactory.compositionBounds = unbounded;
                        }
                        if (_typicalTextFlow.flowComposer)
                        {
                            _typicalTextFlow.flowComposer.swfContext = 
                                ISWFContext(embeddedFontContext);
                        }                       
                        staticTextFlowFactory.swfContext = ISWFContext(embeddedFontContext);
                        // create the textlines
                        staticTextFlowFactory.createTextLines(tossTextLine, _typicalTextFlow);
                        // get the bounds
                        bounds = staticTextFlowFactory.getContentBounds();
                    }
                    else // single line
                    {
                        // create the factory if needed
                        if (!staticStringFactory)
                        {
                            staticStringFactory = new StringTextLineFactory();
                            // set bounds to natural bounds
                            staticStringFactory.compositionBounds = unbounded;
                        }
                        // create the textlines
                        staticStringFactory.text = _typicalText;
                        staticStringFactory.textFlowFormat = _textContainerManager.hostFormat;
                        staticStringFactory.swfContext = ISWFContext(embeddedFontContext);
                        staticStringFactory.createTextLines(tossTextLine);
                        // get the bounds
                        bounds = staticStringFactory.getContentBounds();
                    }           
                    
                    measuredWidth = Math.ceil(bounds.width);
                    measuredHeight = Math.ceil(bounds.height);                  
                }
                else
                {
                    // Go large.  For performance reasons, want to avoid a scrollRect 
                    // whenever possible in drawBackgroundAndSetScrollRect().  This is
                    // particularly true for 1 line TextInput components.
                    measuredWidth = !isNaN(explicitWidth) ? explicitWidth :
                        Math.ceil(calculateWidthInChars());
                    measuredHeight = !isNaN(explicitHeight) ? explicitHeight :
                        Math.ceil(calculateHeightInLines());
                }
            }
            else
            {
                var composeWidth:Number;
                var composeHeight:Number;
                
                // If we're here, then at one or both of the width and height can
                // grow to fit the text.  It is important to figure out whether
                // or not autoSize should be allowed to continue.  If in
                // updateDisplayList(), autoSize is true, then the 
                // compositionHeight is NaN to allow the text to grow.          
                autoSize = true;
                
                if (!isNaN(widthConstraint) || !isNaN(explicitWidth) || 
                    !isNaN(widthInChars))
                {
                    // width specified but no height
                    // if no text, start at one line high and grow
                    
                    if (!isNaN(widthConstraint))
                        composeWidth = widthConstraint;
                    else if (!isNaN(explicitWidth))                    
                        composeWidth = explicitWidth;
                    else
                        composeWidth = Math.ceil(calculateWidthInChars());
                    
                    // The composeWidth may be adjusted for minWidth/maxWidth
                    // except if we're using the explicitWidth.  
                    bounds = measureTextSize(composeWidth);
                    
                    // The measured width shouldn’t be pinned to the composeWidth if 
                    // the composeWidth is set by %, otherwise the measuredWidth 
                    // can keep stretching
                    if (!isNaN(explicitWidth) || !isNaN(widthInChars))
                        measuredWidth = textContainerManager.compositionWidth;
                    else
                        measuredWidth = Math.ceil(bounds.width); 
                    measuredHeight = Math.ceil(bounds.bottom);
                }
                else if (!isNaN(heightConstraint) || !isNaN(explicitHeight) || 
                    !isNaN(_heightInLines))
                {
                    // if no text, 1 char wide with specified height and grow
                    
                    if (!isNaN(heightConstraint))
                        composeHeight = heightConstraint;
                    else if (!isNaN(explicitHeight))
                        composeHeight = explicitHeight;
                    else
                        composeHeight = calculateHeightInLines();
                    
                    // The composeWidth may be adjusted for minWidth/maxWidth.
                    bounds = measureTextSize(NaN, composeHeight);
                    
                    measuredWidth = Math.ceil(bounds.right);               
                    measuredHeight = composeHeight;

                    // Have we already hit the limit with the existing text?  If we
                    // are beyond the composeHeight we can assume we've maxed out on
                    // the compose width as well (or the composeHeight isn't
                    // large enough for even one line of text).
                    if (bounds.bottom > composeHeight)
                        autoSize = false;
                }
                else
                {
                    // The composeWidth may be adjusted for minWidth/maxWidth.
                    bounds = measureTextSize(NaN);
                    
                    measuredWidth = Math.ceil(bounds.right);
                    measuredHeight = Math.ceil(bounds.bottom);
                }
                
                // Clamp the height, except if we're using the explicitHeight.
                if (isNaN(explicitHeight))
                {            
                    if (!isNaN(explicitMinHeight) && measuredHeight < explicitMinHeight)
                        measuredHeight = explicitMinHeight;
                    
                    // Reached max height so can't grow anymore.
                    if (!isNaN(explicitMaxHeight) && measuredHeight > explicitMaxHeight)
                    {
                        measuredHeight = explicitMaxHeight;
                        autoSize = false;
                    }
                }
                
                // Make sure we weren't previously scrolled. 
                if (autoSize && getStyle("lineBreak") == "toFit")
                {
                    _textContainerManager.horizontalScrollPosition = 0;
                    _textContainerManager.verticalScrollPosition = 0;                
                }               
                
                // If we remeasured, we composed and cleared the display.  We need to update the 
                // display if the size didn't change, since validateSize will not do it for us.  
                // This code path can be used by itemRenderer's since setLayoutBounds(), which is
                // where the constraints are set, is not always called.
                if (remeasuringText && 
                    lastMeasuredWidth == measuredWidth && lastMeasuredHeight == measuredHeight)
                {
                    _textContainerManager.updateContainer();
                }
            }
            
            remeasuringText = false;
            inMeasureMethod = false;
            
            //trace("measure", measuredWidth, measuredHeight, "autoSize", autoSize);
        }
        
        /**
         *  @private
         */
        override protected function updateDisplayList(unscaledWidth:Number,
                                                      unscaledHeight:Number):void 
        {
            inUpdateDLMethod = true;
            
            //trace("updateDisplayList", unscaledWidth, unscaledHeight, "autoSize", autoSize);
            
            // Styles can be changed in event handlers while in the middle
            // of the component lifecycle.  Make sure they are not stale when
            // composing text.
            updateStylesIfChanged();
            
            // Check if the auto-size text is constrained in some way and needs
            // to be remeasured.  If one of the dimension changes, the text may
            // compose differently and have a different size which the layout 
            // manager needs to know.
            // Don't exit early if we have changed size.  We may have to run 
            // drawBackgroundAndSetScrollRect 
            if (autoSize && 
                lastUnscaledHeight == unscaledHeight && lastUnscaledWidth == unscaledWidth && 
                remeasureText(unscaledWidth, unscaledHeight))
            {
                inUpdateDLMethod = false;
                return;
            }
            
            super.updateDisplayList(unscaledWidth, unscaledHeight);
            
            // If we're autoSizing we're telling the layout manager one set of
            // values and TLF another set of values so there is room for the text
            // to grow.
            
            // TODO:(cframpto) compositionWidth can be NaN when 
            // autoSize for blockProgression=="rl" is implemented
            if (!autoSize)
            {
                _textContainerManager.compositionWidth = unscaledWidth;
                _textContainerManager.compositionHeight = unscaledHeight;
            }
            
            // If scrolling, always compose with the composer so we get consistent
            // measurements.  The factory and the composer produce slightly 
            // different results which can confuse the scroller.  If there isn't a 
            // composer, this calls updateContainer so do it here now that the 
            // composition sizes are set so the results can be used.
            if (clipAndEnableScrolling && 
                _textContainerManager.composeState != 
                TextContainerManager.COMPOSE_COMPOSER)
            {
                _textContainerManager.convertToTextFlowWithComposer();
            }
            
            // The EditManager calls updateAllControllers() directly when there
            // is interactive input such as typing or cut/paste. This bypasses
            // our update cycle which matters if we are auto-sizing.
            // compositionWidth/Height are NaN and the background is drawn with
            // the old width/height because measureTextSize hasn't had a
            // chance to update the layout manager yet.  Once the layoutManager
            // has been updated, the compositionWidth/Height are still NaN
            // so TLF doesn't think there is anything to compose.  The text 
            // hasn't changed shape, but the background has.
            if (autoSize && !isNaN(lastUnscaledWidth) &&
                (lastUnscaledWidth != unscaledWidth || 
                    lastUnscaledHeight != unscaledHeight))
            {
                if (_textContainerManager.composeState == TextContainerManager.COMPOSE_COMPOSER)
                     _textContainerManager.getTextFlow().flowComposer.getControllerAt(0).shapesInvalid = true;
                else if (!_textContainerManager.isDamaged())
                    _textContainerManager.drawBackgroundAndSetScrollRect(0,0);
            }
               
            _textContainerManager.updateContainer();
            
            lastUnscaledWidth = unscaledWidth;
            lastUnscaledHeight = unscaledHeight;
            
            inUpdateDLMethod = false;
         }
        
        /**
         *  @private
         *  This is called by the layout manager the first time this
         *  component is measured, or later if its size changes. This
         *  is not always called before updateDisplayList().  For example,
         *  for recycled item renderers this is not called if the measured
         *  size doesn't change.
         * 
         *  width and height are NaN unless there are constraints on them.
         */
        override public function setLayoutBoundsSize(
                                    width:Number, height:Number,
                                    postLayoutTransform:Boolean = true):void
        {
            //trace("setLayoutBoundsSize", width, height);
            
            // Save these so when we are auto-sizing we know which dimensions
            // are constrained.  Without this it is not possible to differentiate 
            // between a measured width/height that is the same as the
            // constrained width/height to know whether that dimension can
            // be sized or must be fixed at the constrained value.                
            heightConstraint = height;

            super.setLayoutBoundsSize(width, height, postLayoutTransform);

            // Did we already constrain the width?
            if (widthConstraint == width)
                return;
            
            // No reflow for explicit lineBreak
            if (getStyle("lineBreak") == "explicit")
                return;
            
            // If we don't measure.
            // Call super so we don't call the override
            // and set autoSize to false;
            if (super.canSkipMeasurement())
                return;
            
            if (!isNaN(explicitHeight))
                return;
            
            // We support reflow only in the case of constrained width and
            // unconstrained height. Note that we compare with measuredWidth,
            // as for example the RichEditableText can be
            // constrained by the layout with "left" and "right", but the
            // container width itself may not be constrained and it would depend
            // on the element's measuredWidth.
            var constrainedWidth:Boolean = !isNaN(width) && (width != measuredWidth) && (width != 0); 
            if (!constrainedWidth)
                return;
            
            // We support reflow only when we don't have a transform.
            // We could add support for scale, but not skew or rotation.
            if (postLayoutTransform && hasComplexLayoutMatrix)
                return;
            
            widthConstraint = width;

            invalidateSize();
            
        }
        
        /**
         *  @inheritDoc
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        override public function stylesInitialized():void
        {
            super.stylesInitialized();
            
            ascent = NaN;
            descent = NaN;
            
            hostFormatChanged = true;
        }
        
        /**
         *  @inheritDoc
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        override public function styleChanged(styleProp:String):void
        {
            super.styleChanged(styleProp);
            
            // If null or "styleName" is passed it indicates that
            // multiple styles may have changed.  Otherwise it is a single style
            // so mark whether it is the selectionFormat that changed or the
            // hostFormat that changed.
            if (styleProp == null || styleProp == "styleName")
            {
                hostFormatChanged = true;
                selectionFormatsChanged = true;
                ascent = NaN;
                descent = NaN;
            }
            else if (styleProp == "focusedTextSelectionColor" || 
                styleProp == "unfocusedTextSelectionColor" ||
                styleProp == "inactiveTextSelectionColor")
            {
                selectionFormatsChanged = true;
            }
            else
            {
                hostFormatChanged = true;
                
                if (styleProp.indexOf("font") == 0 || styleProp == "cffHinting")
                {
                    // Regenerate font swfContext and metrics as well.
                    ascent = NaN;
                    descent = NaN;
                }
            }
            
            // Need to create new format(s).
            invalidateProperties();
        }
        
        /**
         *  @private
         */
        override public function setFocus():void
        {
            // We are about to set focus on this component.  If it is due to
            // a programmatic focus change we have to programatically do what the
            // mouseOverHandler and the mouseDownHandler do so that the user can 
            // type in this component without using the mouse first.  We need to
            // put a textFlow with a composer in place.
            if (editingMode != EditingMode.READ_ONLY &&
                _textContainerManager.composeState != 
                TextContainerManager.COMPOSE_COMPOSER)   
            {
                _textContainerManager.beginInteraction();
                _textContainerManager.endInteraction();
            }
            
            super.setFocus();
        }
        
        /**
         *  @private
         */
        override public function drawFocus(isFocused:Boolean):void
        {
            if (isFocused)
            {
                // For some composite components, the focused object may not
                // be "this". If so, we don't want to draw the focus.  This
                // replaces the parentDrawsFocus variable used in halo.
                var fm:IFocusManager = focusManager;
                if (fm && fm.getFocus() != this)
                    return;
            }
            
            super.drawFocus(isFocused);
        }
        
        //--------------------------------------------------------------------------
        //
        //  Methods: IViewport
        //
        //--------------------------------------------------------------------------
        
        //----------------------------------
        //  horizontalScrollPositionDelta
        //----------------------------------
        
        /**
         *  @inheritDoc
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function getHorizontalScrollPositionDelta(navigationUnit:uint):Number
        {
            var scrollR:Rectangle = scrollRect;
            if (!scrollR)
                return 0;
            
            // maxDelta is the horizontalScrollPosition delta required 
            // to scroll to the RIGHT and minDelta scrolls to LEFT. 
            var maxDelta:Number = contentWidth - scrollR.right;
            var minDelta:Number = -scrollR.left;
            
            // Scroll by a "character" which is 1 em (matches widthInChars()).
            var em:Number = getStyle("fontSize");
            
            switch (navigationUnit)
            {
                case NavigationUnit.LEFT:
                    return (scrollR.left <= 0) ? 0 : Math.max(minDelta, -em);
                    
                case NavigationUnit.RIGHT:
                    return (scrollR.right >= contentWidth) ? 0 : Math.min(maxDelta, em);
                    
                case NavigationUnit.PAGE_LEFT:
                    return Math.max(minDelta, -scrollR.width);
                    
                case NavigationUnit.PAGE_RIGHT:
                    return Math.min(maxDelta, scrollR.width);
                    
                case NavigationUnit.HOME: 
                    return minDelta;
                    
                case NavigationUnit.END: 
                    return maxDelta;
                    
                default:
                    return 0;
            }
        }
        
        //----------------------------------
        //  verticalScrollPositionDelta
        //----------------------------------
        
        /**
         *  @inheritDoc
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function getVerticalScrollPositionDelta(navigationUnit:uint):Number
        {
            var scrollR:Rectangle = scrollRect;
            if (!scrollR)
                return 0;
            
            // maxDelta is the horizontalScrollPosition delta required 
            // to scroll to the END and minDelta scrolls to HOME. 
            var maxDelta:Number = contentHeight - scrollR.bottom;
            var minDelta:Number = -scrollR.top;
            
            switch (navigationUnit)
            {
                case NavigationUnit.UP:
                    return _textContainerManager.getScrollDelta(-1);
                    
                case NavigationUnit.DOWN:
                    return _textContainerManager.getScrollDelta(1);
                    
                case NavigationUnit.PAGE_UP:
                    return Math.max(minDelta, -scrollR.height);
                    
                case NavigationUnit.PAGE_DOWN:
                    return Math.min(maxDelta, scrollR.height);
                    
                case NavigationUnit.HOME:
                    return minDelta;
                    
                case NavigationUnit.END:
                    return maxDelta;
                    
                default:
                    return 0;
            }       
        }
        
        //--------------------------------------------------------------------------
        //
        //  Methods
        //
        //--------------------------------------------------------------------------
        
        /**
         *  Inserts the specified text into the RichEditableText
         *  as if you had typed it.
         *
         *  <p>If a range was selected, the new text replaces the selected text.
         *  If there was an insertion point, the new text is inserted there.</p>
         *
         *  <p>An insertion point is then set after the new text.
         *  If necessary, the text will scroll to ensure
         *  that the insertion point is visible.</p>
         *
         *  @param text The text to be inserted.
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function insertText(text:String):void
        {      
            handleInsertText(text);  
        }
        
        /**
         *  Appends the specified text to the end of the RichEditableText,
         *  as if you had clicked at the end and typed.
         *
         *  <p>An insertion point is then set after the new text.
         *  If necessary, the text will scroll to ensure
         *  that the insertion point is visible.</p>
         *
         *  @param text The text to be appended.
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function appendText(text:String):void
        {
            handleInsertText(text, true);
        }
        
        /**
         *  @copy flashx.textLayout.container.ContainerController#scrollToRange() 
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function scrollToRange(anchorPosition:int, activePosition:int):void
        {
            // Make sure the properties are commited since the text could change.
            validateProperties();
            
            // Scrolls so that the text position is visible in the container. 
            textContainerManager.scrollToRange(anchorPosition, activePosition);       
        }
        
        /**
         *  Selects a specified range of characters.
         *
         *  <p>If either position is negative, it will deselect the text range.</p>
         *
         *  @param anchorPosition The character position specifying the end
         *  of the selection that stays fixed when the selection is extended.
         *
         *  @param activePosition The character position specifying the end
         *  of the selection that moves when the selection is extended.
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function selectRange(anchorPosition:int,
                                    activePosition:int):void
        {
            // Make sure the properties are commited since the text could change.
            validateProperties();
            
            if (editingMode == EditingMode.READ_ONLY)
            {
                var selectionState:SelectionState =
                    new SelectionState(textFlow, anchorPosition, activePosition);
                
                var selectionEvent:SelectionEvent = 
                    new SelectionEvent(SelectionEvent.SELECTION_CHANGE, 
                        false, false, selectionState);
                
                textContainerManager_selectionChangeHandler(selectionEvent);            
            }
            else
            {
                var im:ISelectionManager = _textContainerManager.beginInteraction();
                
                im.selectRange(anchorPosition, activePosition);        
                
                // Refresh the selection.  This does not cause a damage event.
                im.refreshSelection();
                
                _textContainerManager.endInteraction();
            }
            
            // Remember if the current selection is a range which was set
            // programatically.
            hasProgrammaticSelectionRange = (anchorPosition != activePosition);
        }
        
        /**
         *  Selects all of the text. This does not include the final paragraph
         *  terminator.
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function selectAll():void
        {
            selectRange(0, int.MAX_VALUE);
        }
       
        /**
         *  Returns a TextLayoutFormat object specifying the computed formats
         *  for the specified range of characters.
         *
         *  <p>If a format is not consistently set across the entire range,
         *  its value will be <code>undefined</code>.</p>
         *
         *  <p>You can specify a Vector of Strings containing the names of the
         *  formats that you care about; if you don't, all formats
         *  will be computed.</p>
         *  
         *  <p>If you don't specify a range, the selected range is used.</p>
         *
         *  @param requestedFormats A Vector of Strings specifying the names
         *  of the requested formats, or <code>null</code> to request all formats.
         *
         *  @param anchorPosition A character position specifying
         *  the fixed end of the selection.
         *
         *  @param activePosition A character position specifying
         *   the movable end of the selection.
         *
         *  @return A TextLayoutFormat object.
         *  
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function getFormatOfRange(requestedFormats:Vector.<String> = null,
                                         anchorPosition:int = -1,
                                         activePosition:int = -1):TextLayoutFormat
        {
            var format:TextLayoutFormat = new TextLayoutFormat();
            
            // Make sure all properties are committed.
            validateProperties();
            
            // This internal TLF object maps the names of format properties
            // to Property instances.
            // Each Property instance has a category property which tells
            // whether it is container-, paragraph-, or character-level.
            var description:Object = TextLayoutFormat.description;
            
            var p:String;
            var category:String;
            
            // Based on which formats have been requested, determine which
            // of the getCommonXXXFormat() methods we need to call.
            
            var needContainerFormat:Boolean = false;
            var needParagraphFormat:Boolean = false;
            var needCharacterFormat:Boolean = false;
            
            if (!requestedFormats)
            {
                requestedFormats = new Vector.<String>;
                for (p in description)
                    requestedFormats.push(p);
                
                needContainerFormat = true;
                needParagraphFormat = true;
                needCharacterFormat = true;
            }
            else
            {
                for each (p in requestedFormats)
                {
                    if (!(p in description))
                        continue;
                    
                    category = description[p].category;
                    
                    if (category == Category.CONTAINER)
                        needContainerFormat = true;
                    else if (category == Category.PARAGRAPH)
                        needParagraphFormat = true;
                    else if (category == Category.CHARACTER)
                        needCharacterFormat = true;
                }
            }
            
            // Get the common formats.
            
            var containerFormat:ITextLayoutFormat;
            var paragraphFormat:ITextLayoutFormat;
            var characterFormat:ITextLayoutFormat;
            
            if (anchorPosition == -1 && activePosition == -1)
            {
                anchorPosition = _selectionAnchorPosition;
                activePosition = _selectionActivePosition;
            }
            
            if (needContainerFormat)
            {
                containerFormat = 
                    _textContainerManager.getCommonContainerFormat();
            }
            
            if (needParagraphFormat)
            {
                paragraphFormat = 
                    _textContainerManager.getCommonParagraphFormat(
                        anchorPosition, activePosition);
            }
            
            if (needCharacterFormat)
            {
                characterFormat = 
                    _textContainerManager.getCommonCharacterFormat(
                        anchorPosition, activePosition);
            }
            
            // Extract the requested formats to return.
            for each (p in requestedFormats)
            {
                if (!(p in description))
                    continue;
                
                category = description[p].category;
                
                if (category == Category.CONTAINER && containerFormat)
                    format[p] = containerFormat[p];
                else if (category == Category.PARAGRAPH && paragraphFormat)
                    format[p] = paragraphFormat[p];
                else if (category == Category.CHARACTER && characterFormat)
                    format[p] = characterFormat[p];
            }
            
            return format;
        }
        
        /**
         *  Applies the specified formats to each element in the specified
         *  range that correspond to the given format.  
         *  It applies the character formats to the text in the specified range
         *  (no change is made if the specified range is a single point). 
         *  It applies the paragraph formats to any paragraphs at least 
         *  partially within the range (or a single paragraph if the range is a 
         *  single point).
         *  It applies the container formats to the container.
         * 
         *  <p>The supported formats are those in TextLayoutFormat.
         *  A value of <code>undefined</code> does not get applied.
         *  If you don't specify a range, the selected range is used.</p>
         *
         *  <p>The following example sets the <code>fontSize</code> and <code>color</code> of the selection:
         *  <pre>
         *  var textLayoutFormat:TextLayoutFormat = new TextLayoutFormat();
         *  textLayoutFormat.fontSize = 12;
         *  textLayoutFormat.color = 0xFF0000;
         *  myRET.setFormatOfRange(textLayoutFormat);
         *  </pre>
         *  </p>
         *  
         *  <p>If you use the results of <code>getFormatOfRange()</code> to
         *  specify the <code>format</code>, note that every format in the
         *  <code>TextLayoutFormat</code> has a
         *  computed value, which will be applied to each element that 
         *  corresponds to the given format.</p>
         * 
         *  <p>If you would like to specify a format to be applied to all the text
         *  it would be better to use <code>setStyle(format, value)</code>
         *  on the component itself.</p>
         * 
         *  <p>The following example sets the <code>fontSize</code> and <code>color</code> of all the text:
         *  <pre>
         *  myRET.setStyle("fontSize", 12);
         *  myRET.setStyle("color", 0xFF0000);
         *  </pre>
         *  </p>
         * 
         *  @param format The TextLayoutFormat to apply to the selection.
         * 
         *  @param anchorPosition A character position, relative to the beginning of the 
         *  text String, specifying the end of the selection that stays fixed when the 
         *  selection is extended with the arrow keys.
         * 
         *  @param activePosition A character position, relative to the beginning of the 
         *  text String, specifying the end of the selection that moves when the 
         *  selection is extended with the arrow keys. 
         * 
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion Flex 4
         */
        public function setFormatOfRange(format:TextLayoutFormat,
                                         anchorPosition:int=-1,
                                         activePosition:int=-1):void
        {
            // Make sure all properties are committed.  The damage handler for the 
            // applyTextFormat op will cause the remeasure and display update.
            validateProperties();
            
            // Assign each specified attribute to one of three format objects,
            // depending on whether it is container-, paragraph-,
            // or character-level. Note that these can remain null.
            var containerFormat:TextLayoutFormat;
            var paragraphFormat:TextLayoutFormat;
            var characterFormat:TextLayoutFormat;
            
            // This internal TLF object maps the names of format properties
            // to Property instances.
            // Each Property instance has a category property which tells
            // whether it is container-, paragraph-, or character-level.
            var description:Object = TextLayoutFormat.description;
            
            for (var p:String in description) 
            {
                if (format[p] === undefined)
                    continue;
                
                var category:String = description[p].category;
                
                if (category == Category.CONTAINER)
                {
                    if (!containerFormat)
                        containerFormat =  new TextLayoutFormat();
                    containerFormat[p] = format[p];
                }
                else if (category == Category.PARAGRAPH)
                {
                    if (!paragraphFormat)
                        paragraphFormat =  new TextLayoutFormat();
                    paragraphFormat[p] = format[p];
                }
                else if (category == Category.CHARACTER)
                {
                    if (!characterFormat)
                        characterFormat =  new TextLayoutFormat();
                    characterFormat[p] = format[p];
                }
            }
            
            // If the selection isn't specified, use the current one.
            if (anchorPosition == -1 && activePosition == -1)
            {
                anchorPosition = _selectionAnchorPosition;
                activePosition = _selectionActivePosition;
            }
            
            // Apply the three format objects to the current selection if
            // selectionState is null, else the specified selection.
            _textContainerManager.applyFormatOperation(
                characterFormat, paragraphFormat, containerFormat, 
                anchorPosition, activePosition);
        }
        
        /**
         *  @private
         */
        mx_internal function createTextContainerManager():RichEditableTextContainerManager
        {
            return new RichEditableTextContainerManager(this);
        }
        
        /**
         *  @private
         */
        private function updateStylesIfChanged():void
        {
            
            if (hostFormatChanged)
            {
                // Side-effect is it marks the text as damaged.
                _textContainerManager.hostFormat = new CSSTextLayoutFormat(this);
                
                hostFormatChanged = false;
            }
            
            if (isNaN(ascent) || isNaN(descent))
            {
                // If the CSS styles for this component specify an embedded font,
                // embeddedFontContext will be set to the module factory that
                // should create TextLines (since they must be created in the
                // SWF where the embedded font is.)
                // Otherwise, this will be null.
                embeddedFontContext = getEmbeddedFontContext();
                
                _textContainerManager.swfContext =
                    ISWFContext(embeddedFontContext);                       
                
                // Note: CSSTextLayoutFormat has special processing
                // for the fontLookup style. If it is "auto",
                // the fontLookup format is set to either
                // "device" or "embedded" depending on whether
                // embeddedFontContext is null or non-null.
                
                // Recalcuate the ascent and descent.
                calculateFontMetrics();    
            }
            
            if (selectionFormatsChanged)
            {
                _textContainerManager.invalidateSelectionFormats();
                
                selectionFormatsChanged = false;
            }
        }
        
        /**
         *  @private
         *  Uses the component's CSS styles to determine the module factory
         *  that should creates its TextLines.
         */
        private function getEmbeddedFontContext():IFlexModuleFactory
        {
            var fontContext:IFlexModuleFactory;
            
            var fontLookup:String = getStyle("fontLookup");
            if (fontLookup != FontLookup.DEVICE)
            {
                var font:String = getStyle("fontFamily");
                var bold:Boolean = getStyle("fontWeight") == "bold";
                var italic:Boolean = getStyle("fontStyle") == "italic";
                
                fontContext = getFontContext(font, bold, italic, true);
            }
            
            return fontContext;
        }
        
        /**
         *  @private
         *  Return true if there is a width and height to use for the measure.
         */
        mx_internal function isMeasureFixed():Boolean
        {
            // This can be called from RET EditManager when hostFormat is null 
            // because a style changed and commitProperties hasn't run yet.
            if (!_textContainerManager.hostFormat)
                updateStylesIfChanged();            
            
            if (_textContainerManager.hostFormat.blockProgression != 
                BlockProgression.TB)
            {
                return true;
            }

            if (typicalText != null)
                return true;
            
            // Is there some sort of width and some sort of height?
            return  (!isNaN(explicitWidth) || !isNaN(_widthInChars) ||
                !isNaN(widthConstraint)) &&
                    (!isNaN(explicitHeight) || !isNaN(_heightInLines) ||
                        !isNaN(heightConstraint));
        }
        
        /**
         *  @private
         *  Returns the bounds of the measured text.  The initial composeWidth may
         *  be adjusted for minWidth or maxWidth.  The value used for the compose
         *  is in _textContainerManager.compositionWidth.
         */
        private function measureTextSize(composeWidth:Number, 
                                         composeHeight:Number=NaN):Rectangle
        {   
            // Adjust for explicit min/maxWidth so the measurement is accurate.
            // If no explicitWidth or widthInChars
            if (isNaN(explicitWidth) && isNaN(widthInChars))
            {
                // then if there is an explicit minWidth and
                // a specified composeWidth and the composeWidth
                // is less than the minWidth, use the minWidth
                if (!isNaN(explicitMinWidth) &&
                    !isNaN(composeWidth) && composeWidth < minWidth)
                {
                    composeWidth = minWidth;
                }
                // if composeWidth is NaN, just compose and see what happens
                // and fix up the measurements afterwards.  See final check
                // at the end of the method
                
                // On the other hand, if there is an explicit maxWidth and
                // no specified composeWidth then use maxWidth, 
                // or if there is a a specified composeWidth 
                // and the composeWidth is greater than maxWidth, use the maxWidth.
                if (!isNaN(explicitMaxWidth) &&
                    isNaN(composeWidth) || 
                    composeWidth > maxWidth)
                {
                    composeWidth = maxWidth;
                }
            }
            
            // If the width is NaN it can grow up to TextLine.MAX_LINE_WIDTH wide.
            // If the height is NaN it can grow to allow all the text to fit.
            _textContainerManager.compositionWidth = composeWidth;
            _textContainerManager.compositionHeight = composeHeight;
            
            // If scrolling, always compose with the composer so we get consistent
            // measurements.  The factory and the composer produce slightly 
            // different results which can confuse the scroller.  If there isn't a 
            // composer, this calls updateContainer so do it here now that the 
            // composition sizes are set so the results can be used.
            if (clipAndEnableScrolling && 
                _textContainerManager.composeState != 
                TextContainerManager.COMPOSE_COMPOSER)
            {
                _textContainerManager.convertToTextFlowWithComposer();
            }
                        
            // Compose only.  The display should not be updated.
            _textContainerManager.compose();
            
            // Adjust width and height for text alignment.
            var bounds:Rectangle = _textContainerManager.getContentBounds();
            
            // If it's an empty text flow, there is one line with one
            // character so the height is good for the line but we
            // need to give it some width other than optional padding.
            
            if (_textContainerManager.getText("\n").length == 0) 
            {
                // Empty text flow.  One Em wide so there
                // is a place to put the insertion cursor.
                bounds.width = bounds.width + getStyle("fontSize");
            }
            
            //trace("measureTextSize", composeWidth, "->", bounds.width, composeHeight, "->", bounds.height);
            
            // one final check:  If there is no explicitWidth...
            if (isNaN(explicitWidth) && isNaN(widthInChars))
            {
                // but there is a minWidth, and no specified composeWidth
                // and we measure out to be less than the minWidth
                // report the minWidth anyway.
                if (!isNaN(explicitMinWidth) &&
                    isNaN(composeWidth) &&
                    bounds.width < minWidth)
                {
                    bounds.width = minWidth;
                }
            }
            return bounds;
        }
        
        
        /**
         *  @private
         *  If auto-sizing text, it may need to be remeasured if it is 
         *  constrained in one dimension by the layout manager.  If it is
         *  constrained in both dimensions there is no need to remeasure.  
         *  Changing one dimension may change the size of the measured text 
         *  and the layout manager needs to know this.
         */
        private function remeasureText(width:Number, height:Number):Boolean
        {   
            // Neither dimensions changed.  If auto-sizing we're still auto-sizing.
            if (width == measuredWidth && height == measuredHeight)
                return false;
            
            // Either constraints are preventing auto-sizing or we need to
            // remeasure which will reset autoSize.
            autoSize = false;
            
            // If no width or height, there is nothing to remeasure since 
            // there is no room for text.
            if (width == 0 || height == 0) 
                return false;
            
            // If we're using typical text, no need to remeasure.
            if (typicalText != null)
                return false;
            
            // No reflow for explicit lineBreak
            if (_textContainerManager.hostFormat.lineBreak == "explicit")
                return false;
            
            if (!isNaN(widthConstraint))
            {
                // Do we have a constrained width and an explicit height?
                // If so, the sizes are set so no need to remeasure now.
                if (!isNaN(explicitHeight) || !isNaN(_heightInLines) ||
                    !isNaN(heightConstraint))
                {
                    return false;
                }
            } 
            
            if (!isNaN(heightConstraint))
            {        
                // Do we have a constrained height and an explicit width?
                // If so, the sizes are set so no need to remeasure now.
                if (!isNaN(explicitWidth) || !isNaN(_widthInChars))
                    return false;
            }                       
            
            // Width or height is different than what was measured.  Since we're
            // auto-sizing, need to remeasure, so the layout manager leaves the
            // correct amount of space for the component.
            invalidateSize();
            
            // Need to make sure the container is updated after the text is re-composed 
            // to measure it.
            remeasuringText = true;
            
            return true;            
        }
        
        /**
         *  @private
         *  This method is called when anything affecting the
         *  default font, size, weight, etc. changes.
         *  It calculates the 'ascent', 'descent', and
         *  instance variables, which are used in measure().
         */
        private function calculateFontMetrics():void
        {
            var fontDescription:FontDescription = new FontDescription();
            
            var s:String;
            
            s = getStyle("cffHinting");
            if (s != null)
                fontDescription.cffHinting = s;
            
            s = getStyle("fontFamily");
            if (s != null)
                fontDescription.fontName = s;
            
            s = getStyle("fontLookup");
            if (s != null)
            {
                // FTE understands only "device" and "embeddedCFF"
                // for fontLookup. But Flex allows this style to be
                // set to "auto", in which case we automatically
                // determine it based on whether the CSS styles
                // specify an embedded font.
                if (s != "device")
                {
                    s = TextUtil.resolveFontLookup(
                        _textContainerManager.swfContext, 
                        _textContainerManager.hostFormat);
                }
                fontDescription.fontLookup = s;
            }
            
            s = getStyle("fontStyle");
            if (s != null)
                fontDescription.fontPosture = s;
            
            s = getStyle("fontWeight");
            if (s != null)
                fontDescription.fontWeight = s;
            
            var elementFormat:ElementFormat = new ElementFormat();
            elementFormat.fontDescription = fontDescription;
            elementFormat.fontSize = getStyle("fontSize");
            
            var textElement:TextElement = new TextElement();
            textElement.elementFormat = elementFormat;
            textElement.text = "M";
            
            var textBlock:TextBlock = new TextBlock();
            textBlock.content = textElement;
            
            var swfContext:ISWFContext = ISWFContext(embeddedFontContext);
            
            var textLine:TextLine;
            if (swfContext)
            {
                textLine = swfContext.callInContext(
                    textBlock.createTextLine, textBlock,
                    [ null, 1000 ]);
            }
            else
                textLine = textBlock.createTextLine(null, 1000);
            
            ascent = textLine.ascent;
            descent = textLine.descent;
        }
        
        /**
         *  @private
         */
        private function calculateWidthInChars():Number
        {
            var em:Number = getStyle("fontSize");
            
            var effectiveWidthInChars:int;
            
            // If both height and width are NaN use 10 chars.  Otherwise if only 
            // width is NaN, use 1.                
            if (isNaN(_widthInChars))
                effectiveWidthInChars = isNaN(_heightInLines) ? 10 : 1;
            else
                effectiveWidthInChars = _widthInChars;
            
            // Without the explicit casts, if padding values are non-zero, the
            // returned width is a very large number.
            return getStyle("paddingLeft") +
                effectiveWidthInChars * em +
                getStyle("paddingRight");
        }
        
        /**
         *  @private
         *  Calculates the height needed for heightInLines lines using the default
         *  font.
         */
        private function calculateHeightInLines():Number
        {
            var height:Number = getStyle("paddingTop") + getStyle("paddingBottom");
            
            if (_heightInLines == 0)
                return height;
            
            var effectiveHeightInLines:int;
            
            // If both height and width are NaN use 10 lines.  Otherwise if 
            // only height is NaN, use 1.
            if (isNaN(_heightInLines))
                effectiveHeightInLines = isNaN(_widthInChars) ? 10 : 1;   
            else
                effectiveHeightInLines = _heightInLines;
            
            // Position of the baseline of first line in the container.
            value = getStyle("firstBaselineOffset");
            if (value == lineHeight)
                height += lineHeight;
            else if (value is Number)
                height += Number(value);
            else
                height += ascent;
            
            // Distance from baseline to baseline.  Can be +/- number or 
            // or +/- percent (in form "120%") or "undefined".  
            if (effectiveHeightInLines > 1)
            {
                var value:Object = getStyle("lineHeight");     
                var lineHeight:Number =
                    RichEditableText.getNumberOrPercentOf(value, getStyle("fontSize"));
                
                // Default is 120%
                if (isNaN(lineHeight))
                    lineHeight = getStyle("fontSize") * 1.2;
                
                height += (effectiveHeightInLines - 1) * lineHeight;
            }            
            
            // Add in descent of last line.
            height += descent;              
            
            return height;
        }
        
        /**
         *  @private
         */
        private function createTextFlowFromContent(content:Object):TextFlow
        {
            var textFlow:TextFlow ;
            
            if (content is TextFlow)
            {
                textFlow = content as TextFlow;
            }
            else if (content is Array)
            {
                textFlow = new TextFlow();
                textFlow.whiteSpaceCollapse = getStyle("whiteSpaceCollapse");
                textFlow.mxmlChildren = content as Array;
                textFlow.whiteSpaceCollapse = undefined;
            }
            else
            {
                textFlow = new TextFlow();
                textFlow.whiteSpaceCollapse = getStyle("whiteSpaceCollapse");
                textFlow.mxmlChildren = [ content ];
                textFlow.whiteSpaceCollapse = undefined;
            }
            
            return textFlow;
        }
        
        /**
         *  @private
         */
        private function updateEditingMode():void
        {
            var newEditingMode:String = EditingMode.READ_ONLY;
            
            if (enabled)
            {
                if (_editable)
                    newEditingMode = EditingMode.READ_WRITE;
                else if (_selectable)
                    newEditingMode = EditingMode.READ_SELECT;
            }
            
            editingMode = newEditingMode;
        }
        
        /**
         *  @private
         * 
         *  This is used when text is either inserted or appended via the API.
         */
        private function handleInsertText(newText:String, isAppend:Boolean=false):void
        {
            // Make sure all properties are committed.  The damage handler for the 
            // insert will cause the remeasure and display update.
            validateProperties();
            
            if (isAppend)
            {
                // Set insertion pt to the end of the current text.
                _selectionAnchorPosition = text.length;
                _selectionActivePosition = _selectionAnchorPosition;
            }
            else
            {
                // Insert requires a selection, or it is a noop.
                if (_selectionAnchorPosition == -1 || _selectionActivePosition == -1)
                    return;
            }
            
            // This will update the selection after the operation is done.
            var success:Boolean =
                _textContainerManager.insertTextOperation(
                    newText, _selectionAnchorPosition, _selectionActivePosition);
            
            if (success)
                dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
        }
        
        /**
         *  @private
         */
        private function handlePasteOperation(op:PasteOperation):void
        {
            var hasConstraints:Boolean = 
                restrict || maxChars || displayAsPassword;
            
            // If there are no constraints and multiline text is allowed
            // there is nothing that needs to be done.
            if (!hasConstraints && multiline)
                return;
            
            // Make sure the clipboard has something to paste.
            if (op.textScrap == null || op.textScrap.textFlow == null)
                return;
            
            // If copied/cut from displayAsPassword field the pastedText
            // is '*' characters but this is correct.
            var pastedText:String = staticPlainTextExporter.export(
                op.textScrap.textFlow, ConversionType.STRING_TYPE) as String;
            
            // If there are no constraints and no newlines there is nothing
            // more to do.
            if (!hasConstraints && pastedText.indexOf("\n") == -1)
                return;
            
            // Save this in case we modify the pasted text.  We need to know
            // how much text to delete.
            var textLength:int = pastedText.length;
            
            // Keep _text in sync with the text flow.  If there was a selection
            // for the paste, delete that text, then insert the pasted
            // text.
            if (_displayAsPassword)
            {
                _text = splice(_text, op.absoluteStart, op.absoluteEnd, "");
                _text = splice(_text, op.absoluteStart, op.absoluteStart, pastedText);
            }
            
            // If multiline is false, strip newlines out of pasted text
            // This will not strip newlines out of displayAsPassword fields
            // since the text is the passwordChar and newline won't be found.
            if (!multiline)
                pastedText = pastedText.replace(ALL_NEWLINES_REGEXP, "");
            
            // We know it's an EditManager or we wouldn't have gotten here.
            var editManager:IEditManager = 
                EditManager(_textContainerManager.beginInteraction());
            
            // Generate a CHANGING event for the PasteOperation but not for the
            // DeleteTextOperation or the InsertTextOperation which are also part
            // of the paste.
            dispatchChangeAndChangingEvents = false;
            
            // Replace the same text, the same place where the paste was done.
            // This will go thru the InsertPasteOperation and do the right
            // things with restrict, maxChars, displayAsPassword and _text.
            var selectionState:SelectionState = new SelectionState(
                  op.textFlow, op.absoluteStart, op.absoluteStart + textLength);
            editManager.insertText(pastedText, selectionState);        
            
            // All done with the edit manager.
            _textContainerManager.endInteraction();
            
            dispatchChangeAndChangingEvents = true;
        }
        
        //--------------------------------------------------------------------------
        //
        //  Event handlers
        //
        //--------------------------------------------------------------------------
        
        /**
         *  @private
         *  find the right time to listen to the focusmanager
         */
        private function addedToStageHandler(event:Event):void
        {
            if (event.target == this)
            {
                removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
                callLater(addActivateHandlers);
            }    
        }
        
        /**
         *  @private
         *  add listeners to focusManager
         */
        private function addActivateHandlers():void
        {
            if (!focusManager)
                return;
            
            focusManager.addEventListener(FlexEvent.FLEX_WINDOW_ACTIVATE, 
                _textContainerManager.activateHandler, false, 0, true)
            focusManager.addEventListener(FlexEvent.FLEX_WINDOW_DEACTIVATE, 
                _textContainerManager.deactivateHandler, false, 0, true)
        }
        
        /**
         *  @private
         *  Called when a FocusManager is added to an IFocusManagerContainer.
         *  We need to check that it belongs
         *  to us before listening to it.
         *  Because we listen to sandboxroot, you cannot assume the type of
         *  the event.
         */
        private function addFocusManagerHandler(event:Event):void
        {
            if (focusManager == event.target["focusManager"])
            {
                systemManager.getSandboxRoot().removeEventListener(FlexEvent.ADD_FOCUS_MANAGER, 
                    addFocusManagerHandler, true)
                addActivateHandlers();
            }
        }
        
        /**
         *  @private
         *  RichEditableTextContainerManager overrides focusInHandler and calls
         *  this before executing its own focusInHandler.
         */
        mx_internal function focusInHandler(event:FocusEvent):void
        {
            // When TCM is simulating a focusIn event, event will be null.
            // Ignore these and wait for the actual focus in event.
            if (event == null)
                return;
            
            //trace("focusIn handler");
            
            var fm:IFocusManager = focusManager;
            if (fm && editingMode == EditingMode.READ_WRITE)
                fm.showFocusIndicator = true;
            
            // showFocusIndicator must be set before this is called.
            super.focusInHandler(event);
            
            if (editingMode == EditingMode.READ_WRITE)
            {
                // If the focusIn was because of a mouseDown event, let TLF
                // handle the selection.  Otherwise it was because we tabbed in
                // or we programatically set the focus.
                if (!mouseDown)
                {
                    var selectionManager:ISelectionManager = 
                        _textContainerManager.beginInteraction();       
                    
                    if (multiline)
                    {
                        if (!selectionManager.hasSelection())
                            selectionManager.selectRange(0, 0);
                    } 
                    else if (!hasProgrammaticSelectionRange)
                    {
                        selectionManager.selectAll();
                    }
                    
                    selectionManager.refreshSelection();
                    
                    _textContainerManager.endInteraction();       
                }
                
                if (_imeMode != null)
                {
                    // When IME.conversionMode is unknown it cannot be
                    // set to anything other than unknown(English)      
                    try
                    {
                        if (IME.conversionMode != IMEConversionMode.UNKNOWN)
                        {
                            IME.conversionMode = _imeMode;
                        }
                    }
                    catch(e:Error)
                    {
                        // on Windows, setting a Japanese IME mode when in
                        // english throws an error (on Mac it doesn't)
                        // so ignore errors we get.
                    }
                }            
            }
            
            if (focusManager && multiline)
                focusManager.defaultButtonEnabled = false;
        }
        
        /**
         *  @private
         *  RichEditableTextContainerManager overrides focusOutHandler and calls
         *  this before executing its own focusOutHandler.
         */
        mx_internal function focusOutHandler(event:FocusEvent):void
        {
            //trace("focusOut handler");
            
            super.focusOutHandler(event);
            
            // By default, we clear the undo history when a RichEditableText loses 
            // focus.
            if (clearUndoOnFocusOut && undoManager)
                undoManager.clearAll();
            
            if (focusManager)
                focusManager.defaultButtonEnabled = true;
            
            
            dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));                           
        }
        
        /**
         *  @private
         *  RichEditableTextContainerManager overrides keyDownHandler and calls
         *  this before executing its own keyDownHandler.
         */ 
        mx_internal function keyDownHandler(event:KeyboardEvent):void
        {
            if (editingMode != EditingMode.READ_WRITE)
                return;
            
            // Handle the ENTER key here, if multiline text is not allowed or
            // if there isn't enough room for more characters to be added.
            if (event.keyCode == Keyboard.ENTER)
            {        
                if (!multiline)
                {
                    dispatchEvent(new FlexEvent(FlexEvent.ENTER));
                    event.preventDefault();
                    return;
                }
                
                // Multiline.  Make sure there is room before acting on it.
                if (_maxChars != 0 && text.length >= _maxChars)
                {
                    event.preventDefault();
                    return;
                }                

                // Let the TLF EditManager handle the ENTER key.  If editing
                // a list, depending on the position, it will either add a new
                // list item or close off the list.  Otherwise it will split
                // the paragraph.
            }
        }
        
        /**
         *  @private
         */
        mx_internal function mouseDownHandler(event:MouseEvent):void
        {
            mouseDown = true;
            
            // Need to get called even if mouse events are dispatched
            // outside of this component.  For example, when the user does
            // a mouse down in RET, drags the mouse outside of the 
            /// component, and then releases the mouse.
            systemManager.getSandboxRoot().addEventListener(
                MouseEvent.MOUSE_UP, 
                systemManager_mouseUpHandler, true /*useCapture*/);
        }
        
        /**
         *  @private
         */
        private function systemManager_mouseUpHandler(event:MouseEvent):void
        {
            mouseDown = false;
            
            systemManager.getSandboxRoot().removeEventListener(
                MouseEvent.MOUSE_UP, 
                         systemManager_mouseUpHandler, true /*useCapture*/);
        }
        
        /**
         *  @private
         *  call a TLF method so we don't leak
         */
        private function removedFromStageHandler(event:Event):void
        {
            if (event.target == this)
            {
                TextContainerManager.releaseReferences();
            }    
        }
        
        /**
         *  @private
         *  If the textFlow hasn't changed the generation remains the same.
         *  Changing the composition width and/or height does not change the
         *  generation.  The bounds can change as a result of different
         *  composition dimensions or as a result of more of the text flow
         *  being composed.  Only as much of the text flow as is displayed is
         *  composed.  If not all of the text flow is composed, its content height
         *  is estimated.  Until the entire text flow is composed its content
         *  height can increase or decrease while scrolling thru the flow.  
         *
         *  If the following conditions are met with the contentWidth and the
         *  contentHeight reported to the scroller, the scroller can avoid the 
         *  situation we've seen where it tries to add a scroll bar which causes the 
         *  text to reflow, which changes the content bounds, which causes the 
         *  scroller to react, and potentially loop indefinately.
         * 
         *       if width is reduced the height should grow or stay the same
         *       if height is reduced the width should grow or stay the same
         *       if width and height are reduced then either the width or height
         *           should grow or stay the same.
         *
         *  toFit
         *      width       height
         *      
         *      smaller     smaller     height pinned to old height
         *      smaller     larger      ok
         *      larger      larger      ok
         *      larger      smaller     ok
         *       
         *  explicit
         *      width       height
         *      smaller     smaller     width pinned to old width
         *      smaller     larger      width pinned to old width
         *      larger      larger      ok
         *      larger      smaller     ok
         */
        private function adjustContentBoundsForScroller(bounds:Rectangle):void
        {   
            // Already reported bounds at least once for this generation of
            // the text flow so we have to be careful to mantain consistency
            // for the scroller.
            if (_textFlow.generation == lastContentBoundsGeneration)
            {          
                if (bounds.width <= _contentWidth)
                {
                    if (_textContainerManager.hostFormat.lineBreak == "toFit")
                    {
                        if (bounds.height < _contentHeight)
                            bounds.height = _contentHeight;
                    }
                    else
                    {
                        // The width may get smaller if the compose height is 
                        // reduced and fewer lines are composed.  Use the old 
                        // content width which is more accurate.
                        bounds.width = _contentWidth;
                    }
                }
            }
            
            lastContentBoundsGeneration = _textFlow.generation;
        }
        
        /**
         *  @private
         *  Called when the TextContainerManager dispatches a 'compositionComplete'
         *  event when it has recomposed the text into TextLines.
         */
        private function textContainerManager_compositionCompleteHandler(
            event:CompositionCompleteEvent):void
        {
            //trace("compositionComplete");
            
            var oldContentWidth:Number = _contentWidth;
            var oldContentHeight:Number = _contentHeight;
            
            var newContentBounds:Rectangle = 
                _textContainerManager.getContentBounds();
            
            // If x and/or y are not 0, adjust for what is visible.  For example, if there is an 
            // image which is wider than the composeWidth and float="right", x will be negative
            // and the part of the image between x and 0 will not be visible so it should
            // not be included in the reported width.  This will avoid a scrollbar that does
            // nothing.
            newContentBounds.width += newContentBounds.x;
            newContentBounds.height += newContentBounds.y;
            
            // Try to prevent the scroller from getting into a loop while
            // adding/removing scroll bars.
            if (_textFlow && clipAndEnableScrolling)
                adjustContentBoundsForScroller(newContentBounds);
            
            var newContentWidth:Number = newContentBounds.width;        
            var newContentHeight:Number = newContentBounds.height;
            
            // TODO:(cframpto) handle blockProgression == RL
            
            if (newContentWidth != oldContentWidth)
            {
                _contentWidth = newContentWidth;
                
                //trace("composeWidth", _textContainerManager.compositionWidth, "contentWidth", oldContentWidth, newContentWidth);
                
                // If there is a scroller, this triggers the scroller layout.
                dispatchPropertyChangeEvent(
                    "contentWidth", oldContentWidth, newContentWidth);
            }
            
            if (newContentHeight != oldContentHeight)
            {
                _contentHeight = newContentHeight;
                
                //trace("composeHeight", _textContainerManager.compositionHeight, "contentHeight", oldContentHeight, newContentHeight);
                
                // If there is a scroller, this triggers the scroller layout.
                dispatchPropertyChangeEvent(
                    "contentHeight", oldContentHeight, newContentHeight);
            } 
        }
        
        /**
         *  @private
         *  Called when the TextContainerManager dispatches a 'damage' event.
         *  The TextFlow could have been modified interactively or programatically.
         */
        private function textContainerManager_damageHandler(event:DamageEvent):void
        {
            if (event.damageLength == 0)
                return;
            
            if (inUpdateDLMethod)
                return;
            
            // Text that is being measured is damaged so update the display.
            if (inMeasureMethod)
            {
                invalidateDisplayList();
                return;
            }
            
            //trace("damageHandler", "generation", _textFlow ? _textFlow.generation : -1, "lastGeneration", lastGeneration);
            
            // The following textContainerManager functions can trigger a damage
            // event:
            //    setText/setTextFlow
            //    set hostFormat
            //    set compositionWidth/compositionHeight
            //    set horizontalScrollPosition/veriticalScrollPosition
            //    set swfContext
            //    updateContainer or compose: always if TextFlowFactory, sometimes 
            //        if flowComposer
            // or the textFlow can be modified directly.
            
            // If no changes, don't recompose/update.  The TextFlowFactory 
            // createTextLines dispatches damage events every time the textFlow
            // is composed, even if there are no changes. 
            if (_textFlow && _textFlow.generation == lastGeneration)
                return;
            
            // If there are pending changes, don't wipe them out.  We have
            // not gotten to commitProperties() yet.
            if (textChanged || textFlowChanged || contentChanged)
                return;
            
            // In this case we always maintain _text with the underlying text and
            // display the appropriate number of passwordChars.  If there are any
            // interactive editing operations _text is updated during the operation.
            if (!displayAsPassword)
                _text = null;
            
            _content = null;        
            _textFlow = _textContainerManager.getTextFlow();
                        
            lastGeneration = _textFlow.generation;
            
            // We don't need to call invalidateProperties()
            // because the hostFormat and the _textFlow are still valid.
            
            // If the textFlow content is modified directly or if there is a style 
            // change by changing the textFlow directly the size could change.
            invalidateSize();
            
            invalidateDisplayList();
        }
        
        /**
         *  @private
         *  Called when the TextContainerManager dispatches a 'scroll' event
         *  as it autoscrolls.
         */
        private function textContainerManager_scrollHandler(event:Event):void
        {
            var oldHorizontalScrollPosition:Number = _horizontalScrollPosition;
            var newHorizontalScrollPosition:Number =
                _textContainerManager.horizontalScrollPosition;
            
            if (newHorizontalScrollPosition != oldHorizontalScrollPosition)
            {
                //trace("hsp scroll", oldHorizontalScrollPosition, "->", newHorizontalScrollPosition);
                
                _horizontalScrollPosition = newHorizontalScrollPosition;
                
                dispatchPropertyChangeEvent("horizontalScrollPosition",
                    oldHorizontalScrollPosition, newHorizontalScrollPosition);
            }
            
            var oldVerticalScrollPosition:Number = _verticalScrollPosition;
            var newVerticalScrollPosition:Number =
                _textContainerManager.verticalScrollPosition;
            
            if (newVerticalScrollPosition != oldVerticalScrollPosition)
            {
                //trace("vsp scroll", oldVerticalScrollPosition, "->", newVerticalScrollPosition);
                
                _verticalScrollPosition = newVerticalScrollPosition;
                
                dispatchPropertyChangeEvent("verticalScrollPosition",
                    oldVerticalScrollPosition, newVerticalScrollPosition);
            }
        }
        
        /**
         *  @private
         *  Called when the TextContainerManager dispatches a 'selectionChange' event.
         */
        private function textContainerManager_selectionChangeHandler(
            event:SelectionEvent):void
        {
            if (preserveSelectionOnSetText)
                return;
            
            var oldAnchor:int = _selectionAnchorPosition;
            var oldActive:int = _selectionActivePosition;
            
            var selectionState:SelectionState = event.selectionState;
            
            if (selectionState)
            {
                _selectionAnchorPosition = selectionState.anchorPosition;
                _selectionActivePosition = selectionState.activePosition;
            }
            else
            {
                _selectionAnchorPosition = -1;
                _selectionActivePosition = -1;
            }
            
            // Selection changed so reset.
            hasProgrammaticSelectionRange = false;
            
            // Only dispatch the event if the selection has really changed.
            var changed:Boolean = oldAnchor != _selectionAnchorPosition ||
                oldActive != _selectionActivePosition;
                                   
            if (changed)
            { 
                //trace("selectionChangeHandler", _selectionAnchorPosition, _selectionActivePosition);
                dispatchEvent(new FlexEvent(FlexEvent.SELECTION_CHANGE));
            }
        }
        
        /**
         *  @private
         *  Called when the TextContainerManager dispatches an 'operationBegin'
         *  event before an editing operation.
         */     
        private function textContainerManager_flowOperationBeginHandler(
            event:FlowOperationEvent):void
        {
            //trace("flowOperationBegin", "level", event.level);
            
            var op:FlowOperation = event.operation;
            
            // The text flow's generation will be incremented if the text flow
            // is modified in any way by this operation.
            
            if (op is InsertTextOperation)
            {
                var insertTextOperation:InsertTextOperation =
                    InsertTextOperation(op);
                
                var textToInsert:String = insertTextOperation.text;
                
                // Note: Must process restrict first, then maxChars,
                // then displayAsPassword last.
                
                // The text deleted by this operation.  If we're doing our
                // own manipulation of the textFlow or thisis a result of a paste operation
                // we have to take the deleted text into account as well as the inserted text.
                var delSelOp:SelectionState = 
                    insertTextOperation.deleteSelectionState;
                
                var delLen:int = (delSelOp == null) ? 0 :
                    delSelOp.absoluteEnd - delSelOp.absoluteStart;
                
                if (_restrict != null)
                {
                    textToInsert = StringUtil.restrict(textToInsert, restrict);
                    // If restrict is the result of a paste the text has already been inserted
                    // into the buffer and has to be removed so don't short-circuit the operation.
                    if (textToInsert.length == 0 && delLen == 0)
                    {
                        event.preventDefault();
                        return;
                    }
                }
                
                if (maxChars != 0)
                {
                    var length1:int = text.length - delLen;
                    var length2:int = textToInsert.length;
                    if (length1 + length2 > maxChars)
                        textToInsert = textToInsert.substr(0, maxChars - length1);
                }
                
                if (_displayAsPassword)
                {
                    // Remove deleted text.
                    if (delLen > 0)
                    {
                        _text = splice(_text, delSelOp.absoluteStart, 
                                       delSelOp.absoluteEnd, "");                                    
                    }
                    
                    // Add in the inserted text.
                    if (textToInsert.length > 0)
                    {
                        _text = splice(_text, insertTextOperation.absoluteStart,
                            insertTextOperation.absoluteStart, textToInsert);
                        
                        // Display the passwordChar rather than the actual text.
                        textToInsert = StringUtil.repeat(passwordChar,
                            textToInsert.length);
                    }
                }
                
                insertTextOperation.text = textToInsert;
            }
            else if (op is CopyOperation)
            {
                if (_displayAsPassword)
                {
                    // For security, don't allow passwords to be copied.
                    event.preventDefault();
                    return;
                }
            }
            else if (op is PasteOperation)
            {
                // Paste is implemented in operationEnd.  The basic idea is to allow 
                // the paste to go through unchanged, but group it together with a 
                // second operation that modifies text as part of the same 
                // transaction. This is vastly simpler for TLF to manage. 
            }
            else if (op is DeleteTextOperation || op is CutOperation)
            {
                var flowTextOperation:FlowTextOperation =
                    FlowTextOperation(op);
                
                // Eat 0-length deletion.  This can happen when insertion point is 
                // at start of container when a backspace is entered
                // or when the insertion point is at the end of the
                // container and a delete key is entered.
                if (flowTextOperation.absoluteStart == 
                    flowTextOperation.absoluteEnd)
                {
                    event.preventDefault();
                    return;
                }           
                
                if (_displayAsPassword)
                {
                    if (op is DeleteTextOperation)
                    {
                        _text = splice(_text, flowTextOperation.absoluteStart,
                            flowTextOperation.absoluteEnd, "");
                    }
                    else
                    {
                        // For security, don't allow passwords to be cut.
                        event.preventDefault();
                        return;
                    }
                }
            }
            
            // Dispatch a 'changing' event from the RichEditableText
            // as notification that an editing operation is about to occur.
            // The level will be 0 for single operations, and at the start
            // of a composite operation.
            if (dispatchChangeAndChangingEvents && event.level == 0)
            {
                var newEvent:TextOperationEvent =
                    new TextOperationEvent(TextOperationEvent.CHANGING);
                newEvent.operation = op;
                dispatchEvent(newEvent);
                
                // If the event dispatched from this RichEditableText is canceled,
                // cancel the one from the EditManager, which will prevent
                // the editing operation from being processed.
                if (newEvent.isDefaultPrevented())
                    event.preventDefault();
            }
        }
        
        /**
         *  @private
         *  Called when the TextContainerManager dispatches an 'operationEnd' event
         *  after an editing operation.
         */
        private function textContainerManager_flowOperationEndHandler(
            event:FlowOperationEvent):void
        {
            //trace("flowOperationEnd", "level", event.level);
            
            // Paste is a special case.  Any mods have to be made to the text
            // which includes what was pasted.
            if (event.operation is PasteOperation)
                handlePasteOperation(PasteOperation(event.operation));
        }
        
        /**
         *  @private
         *  Called when the TextContainerManager dispatches an 'operationComplete' 
         *  event after an editing operation.
         */
        private function textContainerManager_flowOperationCompleteHandler(
            event:FlowOperationEvent):void
        {
            //trace("flowOperationComplete", "level", event.level);
            
            // Dispatch a 'change' event from the RichEditableText
            // as notification that an editing operation has occurred.
            // The flow is now in a state that it can be manipulated.
            // The level will be 0 for single operations, and at the end
            // of a composite operation.
            if (dispatchChangeAndChangingEvents && event.level == 0)
            {
                var newEvent:TextOperationEvent =
                    new TextOperationEvent(TextOperationEvent.CHANGE);
                newEvent.operation = event.operation;
                dispatchEvent(newEvent);
            }
        }
        
        /**
         *  @private
         *  Called when an InlineGraphicElement is asynchronously loaded.
         *  The event status could be "sizePending", "loaded" or "error". 
         *  Must call updateAllContainers() to cause transition to the next
         *  graphic status.
         */
        private function textContainerManager_inlineGraphicStatusChangeHandler (
            event:StatusChangeEvent):void
        {            
            if (event.status == InlineGraphicElementStatus.READY)
            {
                // Now that the actual size of the graphic is available need to
                // optionally remeasure and updateContainer.
                if (autoSize)
                    invalidateSize();                
            }

            // Call updateAllContainers().
            invalidateDisplayList();
        }    
        
        /**
         *  @private
         *  release the textline as it is temporary.
         */
        private function tossTextLine(textLine:DisplayObject):void
        {
            TextUtil.recycleTextLine(textLine as TextLine);
        }
    }

}
