asTestApps/src/helpers/FTETextFieldNew.as (3,215 lines of code) (raw):
////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package helpers // mx.core
{
	
import flash.display.DisplayObject;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.StyleSheet;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.text.TextLineMetrics;
import flash.text.engine.ElementFormat;
import flash.text.engine.FontDescription;
import flash.text.engine.FontLookup;
import flash.text.engine.FontMetrics;
import flash.text.engine.FontPosture;
import flash.text.engine.FontWeight;
import flash.text.engine.Kerning;
import flash.text.engine.LineJustification;
import flash.text.engine.SpaceJustifier;
import flash.text.engine.TextBlock;
import flash.text.engine.TextElement;
import flash.text.engine.TextLine;
import flash.utils.Dictionary;
import flashx.textLayout.compose.ISWFContext;
import flashx.textLayout.compose.TextLineRecycler;
import flashx.textLayout.formats.LeadingModel;
import flashx.textLayout.formats.LineBreak;
import flashx.textLayout.formats.TextDecoration;
import mx.core.FTETextField;
import mx.core.FlexGlobals;
import mx.core.IFlexModuleFactory;
import mx.core.IFontContextComponent;
import mx.core.mx_internal;
use namespace mx_internal;
/**
 *  FTETextField is a Sprite which displays text by using the new
 *  Flash Text Engine to implement the old TextField API.
 * 
 * This is a version that was optimized from the Flex 4.1 original.  That is included as FTETextField41
 * 
 * These changes had measurable benefits (biggest benefit on top)
 * a) reusing TextLines "in place" don't remove them from the parent and readd them.  Seems like this shows an issue in player
 * b) tracking number of characters and not calling createTextLine the final time to get a null line back
 * c) Remove the per FTETextField event listeners and have two listener attached to global application that then fan out all the validateNow calls to all the invalid FTETextFields.  Note:  Alex isn't sure this is the right way to do this but didn't have a better answer at this time.
 * d) two static TextBlock objects so that each composeParagraph didn't need to create a SpaceJustifier
 * e) the HTMLHelper class is split into a separate included class
 * 
 *  @see flash.text.TextField
 *
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @langversion 3.0
 */
public class FTETextFieldNew extends Sprite implements IFontContextComponent
{
	// Current slot count: 21
	// (1 for every type except 2 for Number)
	
	//--------------------------------------------------------------------------
	//
	//  Class initialization
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 */
	private static function initClass():void
	{	
		staticTextBlockAllButLast = new TextBlock();
		staticTextBlockAllButLast.textJustifier = new SpaceJustifier("en",LineJustification.ALL_BUT_LAST);
		
		staticTextBlockUnjustified = new TextBlock();
		staticTextBlockAllButLast.textJustifier = new SpaceJustifier("en",LineJustification.UNJUSTIFIED);
		
		FlexGlobals.topLevelApplication.addEventListener(Event.RENDER, staticRenderHandler);
		FlexGlobals.topLevelApplication.addEventListener(Event.ENTER_FRAME, staticRenderHandler);
	}
	
	initClass();
	
	static private var invalidFields:Dictionary = new Dictionary();
	
	/** @private */
	static mx_internal function staticRenderHandler(e:Event):void
	{
		var count:int = 0;
		for (var field:Object in invalidFields)
		{
			(field as FTETextFieldNew).validateNow();
			count++;
		}
		// all done
		if (count)
			invalidFields = new Dictionary();
	}
	
	//--------------------------------------------------------------------------
	//
	//  Class constants
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 *  TextField has fixed 2-pixel padding.
	 */
	mx_internal static const PADDING_LEFT:Number = 2;
	mx_internal static const PADDING_TOP:Number = 2;
	mx_internal static const PADDING_RIGHT:Number = 2;
	mx_internal static const PADDING_BOTTOM:Number = 2;
	
	/**
	 *  @private
	 *  This regular expression is used to replace LF with CR
	 *  when the text property is set.
	 */
	private static const ALL_LINEFEEDS:RegExp = /\n/g;
	
	/**
	 *  @private
	 *  Masks for bits inside the 'flags' var
	 *  which store the state of Boolean TextField properties.
	 */
	private static const FLAG_BACKGROUND:uint = 1 << 0;
	private static const FLAG_BORDER:uint = 1 << 1;
	private static const FLAG_CONDENSE_WHITE:uint = 1 << 2;
	private static const FLAG_EMBED_FONTS:uint = 1 << 3;
	private static const FLAG_MULTILINE:uint = 1 << 4;
	private static const FLAG_WORD_WRAP:uint = 1 << 5;
	
	/**
	 *  @private
	 *  Masks for bits inside the 'flags' var
	 *  which control what work validateNow() needs to do.
	 */
	private static const FLAG_TEXT_SET:uint = 1 << 6;
	private static const FLAG_HTML_TEXT_SET:uint = 1 << 7;
	private static const FLAG_TEXT_LINES_INVALID:uint = 1 << 8;
	private static const FLAG_GRAPHICS_INVALID:uint = 1 << 9;
	
	/**
	 *  @private
	 *  Masks for bits inside the 'flags' var
	 *  tracking misc boolean variables.
	 */
	mx_internal static const FLAG_EFFECTIVE_CONDENSE_WHITE:uint = 1 << 10;
	private static const FLAG_VALIDATE_IN_PROGRESS:uint = 1 << 11;
	private static const FLAG_HAS_SCROLL_RECT:uint = 1 << 12;
	// TODO (gosmith): Does TextField maintain
	// an internal vs. external concept of scrollRect?
		
	/**
	 *  @private
	 */
	private static const ALL_INVALIDATION_FLAGS:uint =
		FLAG_TEXT_LINES_INVALID |
		FLAG_GRAPHICS_INVALID;
	
	
	//--------------------------------------------------------------------------
	//
	//  Class variables
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 *  Used for initializing _defaultTextFormat.
	 */
	private static var textField:TextField = new TextField();
	
	// We can re-use single instances of a few FTE classes over and over,
	// since they just serve as a factory for the TextLines that we care about.
	
	/**
	 *  @private
	 * Create separate TextBlocks for the two different justification styles.  Saves recreating the justifier on every compose.
	 */
	private static var staticTextBlockAllButLast:TextBlock;
	private static var staticTextBlockUnjustified:TextBlock;
	
	/**
	 *  @private
	 */
	private static var staticTextElement:TextElement = new TextElement();
	
	
    /**
     *  @private
     *  This is the max textLine.x + textLine.textWidth of all the composed
     *  lines.  It is used to determine whether the text must be clipped.
     */
    mx_internal var clipWidth:Number;
	
	/** 
	 * @private
	 * Encapsulate all TLF and HTML related code into a helper that is only created when needed.
	 */
	private var _htmlHelper:HTMLHelper;
    
	//--------------------------------------------------------------------------
	//
	//  Class methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 */
	private static function rint(x:Number):Number
	{
		var i:Number = Math.round(x);
		if (i - 0.5 == x && i & 1)
			--i;
		return i;
	}
	
	/**
	 *  @private
	 */
	private static function cloneTextFormat(
								textFormat:TextFormat):TextFormat
	{
		var newTextFormat:TextFormat = new TextFormat(
			textFormat.font, textFormat.size, textFormat.color,
			textFormat.bold, textFormat.italic, textFormat.underline,
			textFormat.url, textFormat.target, textFormat.align,
			textFormat.leftMargin, textFormat.rightMargin, textFormat.indent,
			textFormat.leading);
		
		newTextFormat.blockIndent = textFormat.blockIndent;
		newTextFormat.bullet = textFormat.bullet;
		newTextFormat.kerning = textFormat.kerning;
		newTextFormat.letterSpacing = textFormat.letterSpacing;
		newTextFormat.tabStops = textFormat.tabStops;
		
		return newTextFormat;
	}
	
	/**
	 *  @private
	 */
	private static function applyTextFormat(src:TextFormat, dst:TextFormat):void
	{
		if (src.align != null)
			dst.align = src.align;
		
		if (src.blockIndent != null)
			dst.blockIndent = src.blockIndent;
			
		if (src.bold != null)
			dst.bold = src.bold;
			
		if (src.bullet != null)
			dst.bullet = src.bullet;
			
		if (src.color != null)
			dst.color = src.color;
			
		if (src.font != null)
			dst.font = src.font;
			
		if (src.indent != null)
			dst.indent = src.indent;
			
		if (src.italic != null)
			dst.italic = src.italic;
			
		if (src.kerning != null)
			dst.kerning != src.kerning;
			
		if (src.leading != null)
			dst.leading = src.leading;
			
		if (src.leftMargin != null)
			dst.leftMargin = src.leftMargin;
			
		if (src.letterSpacing != null)
			dst.letterSpacing = src.letterSpacing;
			
		if (src.rightMargin != null)
			dst.rightMargin = src.rightMargin;
			
		if (src.size != null)
			dst.size = src.size;
			
		if (src.tabStops != null)
			dst.tabStops = src.tabStops;
			
		if (src.target != null)
			dst.target = src.target;
			
		if (src.underline != null)
			dst.underline = src.underline;
			
		if (src.url != null)
			dst.url = src.url;
	}
	
	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  Constructor.
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function FTETextFieldNew()
	{
		super();
		
		// The mouse should not be aware of the TextLines.
		// Otherwise, FTETextField will dispatch mouseOver and mouseOut
		// events over each line, thich TextField doesn't do.
		mouseChildren = false;
		doubleClickEnabled = false;
		
		tabChildren = false;
		
		// Use a static TextField to initialize the defaultTextFormat.
		// This should be faster than creating a TextFormat object
		// and filling it out.
		// It will also take care of setting the 'font' field,
		// which is platform-dependent.
		_defaultTextFormat = textField.defaultTextFormat;
		
		addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
	}
	
	//--------------------------------------------------------------------------
	//
	//  Variables
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 *  Apps are likely to create thousands of instances of FTETextField,
	 *  so in order to minimize memory usage we store flags as 1 bit
	 *  inside a uint instead of making each one a 4-byte Boolean var.
	 */
	private var flags:uint = 0;
	
	/**
	 *  @private
	 *  When we render the text using FTE,
	 *  this object represents the formatting for FTE.
	 *  Every time the defaultTextFormat is set,
	 *  this object is released because it is invalid.
	 *  It is regenerated just in time to render the text.
	 */
	private var elementFormat:ElementFormat;
	
	//--------------------------------------------------------------------------
	//
	//  Overridden properties: DisplayObject
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	//  height
	//----------------------------------
	
	/**
	 *  @private
	 */
	private var _height:Number = 100;
	
	/**
	 *  @private
	 */
	override public function get height():Number
	{
		// If we're autosizing, _height may be invalid.
		// For example, the 'text' may have been set
		// but the TextLines for that text haven't
		// been created yet.
		if (autoSize != TextFieldAutoSize.NONE)
			validateNow();
		
		return _height;
	}
	
	/**
	 *  @private
	 */
	override public function set height(value:Number):void
	{
		// TextField ignores NaN and negative values.
		if (isNaN(value) || value < 0)
			return;
				
		if (value == _height)
			return;
		
		_height = value;
		
		// The TextLines may need to be recreated
		// and the border and background may need to be redrawn.
		setFlag(FLAG_TEXT_LINES_INVALID |
				FLAG_GRAPHICS_INVALID);
				
		invalidate();
	}
	
	//----------------------------------
	//  scrollRect
	//----------------------------------
	
	/*
	 *  Workaround for a Flash Player problem.
     *  Don't read the <code>scrollRect</code> property if its value has not been set,
	 *  because this will cause a large memory allocation.
	 *  And ignore attempts to reset the scrollRect property to null
	 *  (its default value) if we've never set it. 
	 */
	
	/**
	 *  @private
	 */
	override public function get scrollRect():Rectangle
	{
		return testFlag(FLAG_HAS_SCROLL_RECT) ? super.scrollRect : null;
	}
	
	/**
	 *  @private 
	 */
	override public function set scrollRect(value:Rectangle):void
	{
		if (!testFlag(FLAG_HAS_SCROLL_RECT) && !value)
			return;
		setFlag(FLAG_HAS_SCROLL_RECT);
		super.scrollRect = value;
	}
	
	//----------------------------------
	//  width
	//----------------------------------
	
	/**
	 *  @private
	 */
	private var _width:Number = 100;
	
	/**
	 *  @private
	 */
	override public function get width():Number
	{
		// If we're autosizing, _width may be invalid.
		// For example, the 'text' may have been set
		// but the TextLines for that text haven't
		// been created yet.
		if (autoSize != TextFieldAutoSize.NONE)
			validateNow();
		
		return _width;
	}
	
	/**
	 *  @private
	 */
	override public function set width(value:Number):void
	{
		// TextField ignores NaN and negative values.
		if (isNaN(value) || value < 0)
			return;
		
		if (value == _width)
			return;
		
		_width = value;
		
		// The TextLines may need to be recreated
		// and the border and background may need to be redrawn.
		setFlag(FLAG_TEXT_LINES_INVALID |
				FLAG_GRAPHICS_INVALID);
				
		invalidate();
	}
	
	//--------------------------------------------------------------------------
	//
	//  Properties: TextField
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	//  alwaysShowSelection
	//----------------------------------
	
	/**
	 *  This property is not implemented in FTETextField
	 *  because FTETextField does not support selection.
	 *  Accessing it will throw a runtime error.
	 * 
	 *  @see flash.text.TextField#alwaysShowSelection
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get alwaysShowSelection():Boolean
	{
		throw new Error(notImplemented("alwaysShowSelection"));
	}
	
	/**
	 *  @private
	 */
	public function set alwaysShowSelection(value:Boolean):void
	{
		throw new Error(notImplemented("alwaysShowSelection"));
	}
	
	//----------------------------------
	//  antiAliasType
	//----------------------------------
	
	/**
	 *  This property has no effect in FTETextField
	 *  because FTE uses a newer font renderer than TextField.
	 *  Getting it will always return <code>null</code>
	 *  and setting it will do nothing.
	 *  
	 *  @see flash.text.TextField#antiAliasType
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get antiAliasType():String
	{
		return null;
	}
	
	/**
	 *  @private
	 */
	public function set antiAliasType(value:String):void
	{
	}
	
	//----------------------------------
	//  autoSize
	//----------------------------------
	
	/**
	 *  @private
	 */
	private var _autoSize:String = TextFieldAutoSize.NONE;
	
	/**
	 *  @copy flash.text.TextField#autoSize
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get autoSize():String
	{
		return _autoSize;
	}
	
	/**
	 *  @private
	 */
	public function set autoSize(value:String):void
	{
		// TextField throws this RTE when invalid values are set.
		if (value != TextFieldAutoSize.NONE &&
			value != TextFieldAutoSize.LEFT &&
			value != TextFieldAutoSize.CENTER &&
			value != TextFieldAutoSize.RIGHT)
		{
			var message:String = getErrorMessage("badParameter", "autoSize");
			throw new ArgumentError(message);
		}
		
		if (value == autoSize)
			return;
		
		_autoSize = value;
		
		// The TextLines may need to be recreated
		// and the border and background may need to be redrawn.
		setFlag(FLAG_TEXT_LINES_INVALID |
				FLAG_GRAPHICS_INVALID);
		
		invalidate();
	}
	
	//----------------------------------
	//  background
	//----------------------------------
	
	/**
	 *  @copy flash.text.TextField#background
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get background():Boolean
	{
		return testFlag(FLAG_BACKGROUND);
	}
	
	/**
	 *  @private
	 */
	public function set background(value:Boolean):void
	{
		if (value == background)
			return;
		setFlagToValue(FLAG_BACKGROUND, value);
		
		// The border and background need to be redrawn.
		setFlag(FLAG_GRAPHICS_INVALID);
		
		invalidate();
	}
	
	//----------------------------------
	//  backgroundColor
	//----------------------------------
	
	/**
	 *  @private
	 */
	private var _backgroundColor:uint = 0xFFFFFF;
	
	/**
	 *  @copy flash.text.TextField#backgroundColor
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get backgroundColor():uint
	{
		return _backgroundColor;
	}
	
	/**
	 *  @private
	 */
	public function set backgroundColor(value:uint):void
	{
		if (value == _backgroundColor)
			return;
		
		_backgroundColor = value;
		
		// The border and background need to be redrawn.
		setFlag(FLAG_GRAPHICS_INVALID);
		
		invalidate();
	}
	
	//----------------------------------
	//  border
	//----------------------------------
	
	/**
	 *  @copy flash.text.TextField#border
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get border():Boolean
	{
		return testFlag(FLAG_BORDER);
	}
	
	/**
	 *  @private
	 */
	public function set border(value:Boolean):void
	{
		if (value == border)
			return;
		
		setFlagToValue(FLAG_BORDER,value);
		
		// The border and background need to be redrawn.
		setFlag(FLAG_GRAPHICS_INVALID);
        
        // The border increases the width and height by 1 pixel, so if there
        // is a scrollRect, it has to be modified as well.
        if (testFlag(FLAG_TEXT_SET | FLAG_HTML_TEXT_SET))
            setFlag(FLAG_TEXT_LINES_INVALID);
            
		invalidate();
	}
	
	//----------------------------------
	//  borderColor
	//----------------------------------
	
	/**
	 *  @private
	 */
	private var _borderColor:uint = 0x000000;
	
	/**
	 *  @copy flash.text.TextField#borderColor
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get borderColor():uint
	{
		return _borderColor;
	}
	
	/**
	 *  @private
	 */
	public function set borderColor(value:uint):void
	{
		if (value == _borderColor)
			return;
		
		_borderColor = value;
		
		// The border and background need to be redrawn.
		setFlag(FLAG_GRAPHICS_INVALID);
		
		invalidate();
	}
	
	//----------------------------------
	//  bottomScrollV
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support scrolling.
	 *  Accessing it will throw a runtime error.
	 *
	 *  @see flash.text.TextField#bottomScrollV
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get bottomScrollV():int
	{
		throw new Error(notImplemented("bottomScrollV"));
	}
	
	//----------------------------------
	//  caretIndex
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support editing.
	 *  Accessing it will throw a runtime error.
	 *
	 *  @see flash.text.TextField#caretIndex
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get caretIndex():int
	{
		throw new Error(notImplemented("caretIndex"));
	}
	
	//----------------------------------
	//  condenseWhite
	//----------------------------------
	
	/**
	 *  @copy flash.text.TextField#condenseWhite
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get condenseWhite():Boolean
	{
		return testFlag(FLAG_CONDENSE_WHITE);
	}
	
	/**
	 *  @private
	 */
	public function set condenseWhite(value:Boolean):void
	{
		setFlagToValue(FLAG_CONDENSE_WHITE, value);
		// Note: There is nothing else to do immediately;
		// the new value doesn't have any effect
		// until 'htmlText' is set later.
	}
	
	//----------------------------------
	//  defaultTextFormat
	//----------------------------------
	
	/**
	 *  @private
	 *  Storage for the defaultTextFormat property.
	 *  This variable is initialized in the constructor
	 *  to a TextFormat instance filled with default values.
	 *  The setter applies non-null incoming formats
	 *  to the object stored here.
	 *  The getter returns a copy of the object stored here.
	 *  Note that No field of this TextFormat will ever be null.
	 */
	mx_internal var _defaultTextFormat:TextFormat;
	
	/**
	 *  @copy flash.text.TextField#defaultTextFormat
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get defaultTextFormat():TextFormat
	{
		// TextField returns a new TextFormat instance each time
		// you access defaultTextFormat; the proof is that
		//   textField.defaultTextFormat != textField.defaultTextFormat
		// is true.
		return cloneTextFormat(_defaultTextFormat);    	
	}
	
	/**
	 *  @private
	 */
	public function set defaultTextFormat(value:TextFormat):void
	{
		// TextField throws this RTE if a null value is set.
		if (!value)
		{
			var message:String = getErrorMessage("nullParameter", "format");
			throw new TypeError(message);
		}
		
		// Apply non-null formats in the incoming TextFormat
		// to the defaultTextFormat.
		applyTextFormat(value, _defaultTextFormat);
		
		// These FTE and TLF formatting objects are now invalid
		// and must be recreated when needed.
		elementFormat = null;
		
		if (_htmlHelper)
			_htmlHelper.hostFormat = null;
		
		// Note: Setting this does NOT cause already-rendered text
		// to change its format.
		// If establishes the formatting for text set or added later.
	}
	
	//----------------------------------
	//  displayAsPassword
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support editing.
	 *  Accessing it will throw a runtime error.
	 *
	 *  @see flash.text.TextField#displayAsPassword
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get displayAsPassword():Boolean
	{
		throw new Error(notImplemented("displayAsPassword"));
	}
	
	/**
	 *  @private
	 */
	public function set displayAsPassword(value:Boolean):void
	{
		throw new Error(notImplemented("displayAsPassword"));
	}
	
	//----------------------------------
	//  embedFonts
	//----------------------------------
	
	/**
	 *  @copy flash.text.TextField#embedFonts
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get embedFonts():Boolean
	{
		return testFlag(FLAG_EMBED_FONTS);
	}
	
	/**
	 *  @private
	 */
	public function set embedFonts(value:Boolean):void
	{
		if (value == embedFonts)
			return;
		
		setFlagToValue(FLAG_EMBED_FONTS, value);
		
		// These FTE and TLF formatting objects are now invalid
		// and must be recreated when needed.
		elementFormat = null;
		if (_htmlHelper)
			_htmlHelper.hostFormat = null;
		// The TextLines may need to be recreated
		// and the border and background may need to be redrawn.
		setFlag(FLAG_TEXT_LINES_INVALID |
				FLAG_GRAPHICS_INVALID);
				
		invalidate();
	}
	
	//----------------------------------
	//  gridFitType
	//----------------------------------
	
	/**
	 *  This property has no effect in FTETextField
	 *  because FTE uses a newer font renderer than TextField.
	 *  Getting it will always return <code>null</code>
	 *  and setting it will do nothing.
	 *  
	 *  @see flash.text.TextField#gridFitType
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get gridFitType():String
	{
		return null;
	}
	
	/**
	 *  @private
	 */
	public function set gridFitType(value:String):void
	{
	}
	
	//----------------------------------
	//  htmlText
	//----------------------------------
	
	
	/**
	 *  @copy flash.text.TextField#htmlText
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get htmlText():String
	{
		// When you set the htmlText and then get it,
		// what you get is not necessarily what you set.
		// The easiest way to handle this is to make sure
		// that the text is composed (which will null out _htmlText
		// if there is no styleSheet) and then execute the code
		// below to export HTML from the TextFlow.
		validateNow();
		
		// When 'text' is set, _htmlText is nulled out
		// to indicate that it is invalid
		// and must be recalculated.
		if (!_htmlHelper)
			_htmlHelper = new HTMLHelper(this);
		
		return _htmlHelper.getHtmlText(_text);
	}
	
	/**
	 *  @private
	 */
	public function set htmlText(value:String):void
	{
		// TextField throws this RTE if a null value is set.
		// It seems like this should say
		//   "Parameter htmlText must be non-null",
		// but that's not what TextField does.
		if (value == null)
		{
			var message:String = getErrorMessage("nullParameter", "text");
			throw new TypeError(message);
		}
		
		// Note: We don't return early if value == _htmlText
		// because the defaultTextFormat may have changed
		// in which case we need to recompose.
		
		// Remember the value of condenseWhite at the time
		// that htmlText is set, because it could be changed
		// before the TextLines are rendered.
		setFlagToValue(FLAG_EFFECTIVE_CONDENSE_WHITE,
					   testFlag(FLAG_CONDENSE_WHITE));
		
		if (!_htmlHelper)
			_htmlHelper = new HTMLHelper(this);
		
		_htmlHelper._htmlText = value;
		_htmlHelper.explicitHTMLText = value;
		
		// _text is now invalid and will get regenerated on demand.
		_text = null;
		
		clearFlag(FLAG_TEXT_SET);
		
		setFlag(FLAG_HTML_TEXT_SET |
				FLAG_TEXT_LINES_INVALID |
				FLAG_GRAPHICS_INVALID);
		
		invalidate();
		
		// NOTE: With hmtlText, what you set is NOT what you get.
		// You can set incomplete (or no) markup
		// and get back complete markup.
	}
	
	//----------------------------------
	//  length
	//----------------------------------
	
	/**
	 *  @copy flash.text.TextField#length
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get length():int
	{
		return text.length;
	}
	
	//----------------------------------
	//  maxChars
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support editing.
	 *  Accessing it will throw a runtime error.
	 *
	 *  @see flash.text.TextField#maxChars
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get maxChars():int
	{
		throw new Error(notImplemented("maxChars"));
	}
	
	/**
	 *  @private
	 */
	public function set maxChars(value:int):void
	{
		throw new Error(notImplemented("maxChars"));
	}
	
	//----------------------------------
	//  maxScrollH
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support scrolling.
	 *  Accessing it will throw a runtime error.
	 *
	 *  @see flash.text.TextField#maxScrollH
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get maxScrollH():int
	{
		throw new Error(notImplemented("maxScrollH"));
	}
	
	//----------------------------------
	//  maxScrollV
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support scrolling.
	 *  Accessing it will throw a runtime error.
	 *
	 *  @see flash.text.TextField#maxScrollV
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get maxScrollV():int
	{
		throw new Error(notImplemented("maxScrollV"));
	}
	
	//----------------------------------
	//  mouseWheelEnabled
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support scrolling.
	 *  Getting it will always return <code>false</code>
	 *  and setting it will do nothing.
	 *
	 *  @see flash.text.TextField#mouseWheelEnabled
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get mouseWheelEnabled():Boolean
	{
		return false;
	}
	
	/**
	 *  @private
	 */
	public function set mouseWheelEnabled(value:Boolean):void
	{
	}
	
	//----------------------------------
	//  multiline
	//----------------------------------
	
	/**
	 *  This property has no effect in FTETextField
	 *  because FTETextField does not support editing.
	 *  However, you can get and set it.
	 *
	 *  @see flash.text.TextField#multiline
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get multiline():Boolean
	{
		return testFlag(FLAG_MULTILINE);
	}
	
	/**
	 *  @private
	 */
	public function set multiline(value:Boolean):void
	{
		setFlagToValue(FLAG_MULTILINE, value);
	}
	
	//----------------------------------
	//  numLines
	//----------------------------------
	
	/**
	 *  @copy flash.text.TextField#numLines
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get numLines():int
	{
		validateNow();
		
		// All the of the children of this Sprite are TextLines,
		// so the number of lines is the number of children.
		// TextContainerManager can create Shapes as well,
		// but only when using TLF's backgroundColor and backgroundAlpha
		// formatting on spans, which FTETextField doesn't use.
		return numChildren;
	}
	
	//----------------------------------
	//  restrict
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support scrolling.
	 *  Accessing it will throw a runtime error.
	 *  
	 *  @see flash.text.TextField#restrict
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get restrict():String
	{
		throw new Error(notImplemented("restrict"));
	}
	
	/**
	 *  @private
	 */
	public function set restrict(value:String):void
	{
		throw new Error(notImplemented("restrict"));
	}
	
	//----------------------------------
	//  scrollH
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support scrolling.
	 *  Accessing it will throw a runtime error.
	 *
	 *  @see flash.text.TextField#scrollH
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get scrollH():int
	{
		throw new Error(notImplemented("scrollH"));
	}
	
	/**
	 *  @private
	 */
	public function set scrollH(value:int):void
	{
		throw new Error(notImplemented("scrollH"));
	}
	
	//----------------------------------
	//  scrollV
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support scrolling.
	 *  Accessing it will throw a runtime error.
	 *
	 *  @see flash.text.TextField#scrollV
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get scrollV():int
	{
		throw new Error(notImplemented("scrollV"));
	}
	
	/**
	 *  @private
	 */
	public function set scrollV(value:int):void
	{
		throw new Error(notImplemented("scrollV"));
	}
	
	//----------------------------------
	//  selectable
	//----------------------------------
	
	/**
	 *  Setting this property has no effect in FTETextField
	 *  because FTETextField does not support selection.
	 *  If you get it, it will always be <code>false</code>.
	 *
	 *  @see flash.text.TextField#selectable
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get selectable():Boolean
	{
		return false;
	}
	
	/**
	 *  @private
	 */
	public function set selectable(value:Boolean):void
	{
	}
	
	//----------------------------------
	//  selectionBeginIndex
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support selection.
	 *  Accessing it will throw a runtime error.
	 *
	 *  @see flash.text.TextField#selectionBeginIndex
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get selectionBeginIndex():int
	{
		throw new Error(notImplemented("selectionBeginIndex"));
	}
	
	//----------------------------------
	//  selectionEndIndex
	//----------------------------------
	
	/**
	 *  This property has not been implemented in FTETextField
	 *  because FTETextField does not support selection.
	 *  Accessing it will throw a runtime error.
	 * 
	 *  @see flash.text.TextField#selectionEndIndex
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get selectionEndIndex():int
	{
		throw new Error(notImplemented("selectionEndIndex"));
	}
	
	//----------------------------------
	//  sharpness
	//----------------------------------
	
	/**
	 *  This property has no effect in FTETextField.
	 *  because FTE uses a newer font renderer than TextField.
	 *  Getting it will always return <code>NaN</code>
	 *  and setting it will do nothing.
	 *  
	 *  @see flash.text.TextField#sharpness
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get sharpness():Number
	{
		return NaN;
	}
	
	/**
	 *  @private
	 */
	public function set sharpness(value:Number):void
	{
	}
	
	//----------------------------------
	//  styleSheet
	//----------------------------------
	
	/**
	 *  @copy flash.text.TextField#styleSheet
	 * 
     *  @see flash.text.StyleSheet
     * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get styleSheet():StyleSheet
	{
		// Note: TextField does NOT return a copy of the StyleSheet.
		return _htmlHelper ? _htmlHelper._styleSheet : null;
	}
	
	/**
	 *  @private
	 */
	public function set styleSheet(value:StyleSheet):void
	{
		// TextField allows a null value to be set;
		// in fact, this is the default.
		
		// Note: We don't return early if value == _styleSheet
		// because the same StyleSheet instance could be coming 
		// in again but might have new values in it.
		if (!_htmlHelper)
			_htmlHelper = new HTMLHelper(this);
		
		_htmlHelper.styleSheet = value;
        
		// The TextLines may need to be recreated
		// and the border and background may need to be redrawn.
		setFlag(FLAG_TEXT_LINES_INVALID |
				FLAG_GRAPHICS_INVALID);
				
		invalidate();
	}
	
	//----------------------------------
	//  text
	//----------------------------------
	
	/**
	 *  @private
	 */
	private var _text:String = "";
	
	/**
	 *  @copy flash.text.TextField#text
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get text():String
	{
		// When 'htmlText' is set, _text is nulled out
		// to indicate that it is invalid
		// and must be rexported from the TextFlow.
		if (_text == null)
		{
			// Convert the LF characters that TLF exports
			// into CR characters.
			_text = _htmlHelper.getText().replace(ALL_LINEFEEDS, "\r");
		}
			
		return _text;
	}
	
	/**
	 *  @private
	 */
	public function set text(value:String):void
	{
		// TextField throws this RTE if a null value is set.
		if (value == null)
		{
			var message:String = getErrorMessage("nullParameter", "text");
			throw new TypeError(message);
		}
		
		// Note: We don't return early if value == _text
		// because the defaultTextFormat may have changed
		// in which case we need to recompose.
		
		// TextField turns all LF characters into CR characters,
		// including treating the Windows line-ending-sequence
		// CR+LF as two CRs.
		_text = value.replace(ALL_LINEFEEDS, "\r");
		
		// _htmlText is now invalid and will get regenerated on demand
		_htmlHelper =  null;
		
		clearFlag(FLAG_HTML_TEXT_SET);
		
		setFlag(FLAG_TEXT_SET |
				FLAG_TEXT_LINES_INVALID |
				FLAG_GRAPHICS_INVALID);
		
		invalidate();
	}
	
	//----------------------------------
	//  textColor
	//----------------------------------
	
	/**
	 *  @copy flash.text.TextField#textColor
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get textColor():uint
	{
		// textColor is not an independent format in TextField;
		// getting textColor simply returns the color
		// in the defaultTextFormat.
		return uint(_defaultTextFormat.color);
	}
	
	/**
	 *  @private
	 *  Setting the textColor changes the color in the defaultTextFormat
	 *  and redraws the text in the new color.
	 */
	public function set textColor(value:uint):void
	{
		if (value == textColor)
			return;
		_defaultTextFormat.color = value;
        
        // These FTE and TLF formatting objects are now invalid
        // and must be recreated when needed.
        elementFormat = null;
		if (_htmlHelper)
			_htmlHelper.hostFormat = null;
		setFlag(FLAG_TEXT_LINES_INVALID);
		
		invalidate();
	}
	
	//----------------------------------
	//  textHeight
	//----------------------------------
	
	/**
	 *  @private
	 */
	mx_internal var _textHeight:Number = 0;
	
	/**
	 *  @copy flash.text.TextField#textHeight
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get textHeight():Number
	{
		validateNow();
		
		return _textHeight;
	}
	
	//----------------------------------
	//  textWidth
	//----------------------------------
	
	/**
	 *  @private
	 */
	mx_internal var _textWidth:Number = 0;
	
	/**
	 *  @copy flash.text.TextField#textWidth
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get textWidth():Number
	{
		validateNow();
		
		return _textWidth;
	}
	
	//----------------------------------
	//  thickness
	//----------------------------------
	
	/**
	 *  This property has no effect in FTETextField
	 *  because FTE uses a newer font renderer than TextField.
	 *  Getting it will always return <code>NaN</code>
	 *  and setting it will do nothing.
	 *  
	 *  @see flash.text.TextField#thickness
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get thickness():Number
	{
		return NaN;
	}
	
	/**
	 *  @private
	 */
	public function set thickness(value:Number):void
	{
	}
	
	//----------------------------------
	//  type
	//----------------------------------
	
	/**
	 *  @copy flash.text.TextField#type
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get type():String
	{
		return TextFieldType.DYNAMIC;
	}
	
	/**
	 *  @private
	 */
	public function set type(value:String):void
	{
		var message:String;
		
		// TextField throws this RTE when invalid values are set.
		if (value != TextFieldType.DYNAMIC &&
			value != TextFieldType.INPUT)
		{
			message = getErrorMessage("badParameter", "type");
			throw new ArgumentError(message);
		}
		
		if (value == TextFieldType.INPUT)
		{
			message = getErrorMessage("unsupportedTypeInFTETextField");
			throw new Error(message);
		}
	}
	
	//----------------------------------
	//  useRichTextClipboard
	//----------------------------------
	
	/**
	 *  This property is not implemented in FTETextField
	 *  because FTETextField does not support selection
	 *  or clipboard operations.
	 *  Accessing it will throw a runtime error.
	 *  
	 *  @see flash.text.TextField#useRichTextClipboard
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get useRichTextClipboard():Boolean
	{
		throw new Error(notImplemented("useRichTextClipboard"));
	}
	/**
	 *  @private
	 */
	public function set useRichTextClipboard(value:Boolean):void
	{
		throw new Error(notImplemented("useRichTextClipboard"));
	}
	
	//----------------------------------
	//  wordWrap
	//----------------------------------
	
	/**
	 *  @copy flash.text.TextField#wordWrap
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get wordWrap():Boolean
	{
		return testFlag(FLAG_WORD_WRAP);
	}
	
	/**
	 *  @private
	 */
	public function set wordWrap(value:Boolean):void
	{
		if (value == wordWrap)
			return;
		
		setFlagToValue(FLAG_WORD_WRAP, value);
		
		// These FTE and TLF formatting objects are now invalid
		// and must be recreated when needed.
		elementFormat = null;
		if (_htmlHelper)
			_htmlHelper.hostFormat = null;
		
		// The TextLines may need to be recreated
		// and the border and background may need to be redrawn.
		setFlag(FLAG_TEXT_LINES_INVALID |
				FLAG_GRAPHICS_INVALID);
		
		invalidate();
	}
	
	//--------------------------------------------------------------------------
	//
	//  Properties
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	//  direction
	//----------------------------------
	
	/**
	 *  @private
	 *  Storage for the direction property.
	 */
	private var _direction:String = "ltr";
	
	/**
	 *  The directionality of the text displayed by the FTETextField.
	 * 
	 *  <p>The allowed values are <code>"ltr"</code> for left-to-right text,
	 *  as in Latin-style scripts,
	 *  and <code>"rtl"</code> for right-to-left text,
	 *  as in Arabic and Hebrew.</p>
	 * 
	 *  <p><strong>Note:</strong> This property does not exist in the
	 *  flash.text.TextField API.</p>
	 *
	 *  @default "ltr"
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get direction():String
	{
		return _direction;
	}
	
	/**
	 *  @private
	 */
	public function set direction(value:String):void
	{
		if (value != "ltr" && value != "rtl")
		{
			var message:String = getErrorMessage("badParameter", "direction");
			throw new ArgumentError(message);
		}
		
		if (value == _direction)
			return;
		
		_direction = value;
		
		// These FTE and TLF formatting objects are now invalid
		// and must be recreated when needed.
		elementFormat = null;
		if (_htmlHelper)
			_htmlHelper.hostFormat = null;
		
		// The TextLines may need to be recreated
		// and the border and background may need to be redrawn.
		setFlag(FLAG_TEXT_LINES_INVALID |
				FLAG_GRAPHICS_INVALID);
		
		invalidate();
	}
	
	//----------------------------------
	//  fontContext
	//----------------------------------
	
	/**
	 *  @private
	 *  Storage for the fontContext property.
	 */
	private var _fontContext:IFlexModuleFactory;
	
	/**
	 *  The IFlexModuleFactory instance that FTETextField
	 *  uses for creating TextLine objects.  This is usually, but not always, 
     *  an ISWFContext.
	 * 
	 *  <p>Set this if you need lines to be created in a different
	 *  SWF context than the one containing the TLF code.</p>
	 * 
	 *  <p><strong>Note:</strong> This property does not exist in the 
	 *  flash.text.TextField API.</p>
	 * 
	 *  @default null
	 *
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function get fontContext():IFlexModuleFactory
	{
		return _fontContext;
	}
	
	/**
	 *  @private
	 */
	public function set fontContext(value:IFlexModuleFactory):void
	{
		// FTETextField allows a null value to be set;
		// in fact, this is the default.
		if (value == _fontContext)
			return;
				
		_fontContext = value;
		
		// The TextLines may need to be recreated
		// and the border and background may need to be redrawn.
		setFlag(FLAG_TEXT_LINES_INVALID |
				FLAG_GRAPHICS_INVALID);
		
		invalidate();
	}
	
	//----------------------------------
	//  locale
	//----------------------------------
	
	/**
	 *  @private
	 *  Storage for the locale property.
	 */
	private var _locale:String = "en";
	
	/**
	 *  The locale of the text displayed by FTETextField.
	 * 
	 *  <p>FTE and TLF use this locale to map Unicode characters
	 *  to font glyphs and to find fallback fonts.</p>
	 */
	public function get locale():String
	{
		return _locale;
	}
	
	/**
	 *  @private
	 */
	public function set locale(value:String):void
	{
		if (value == _locale)
			return;
			
		_locale = value;
		
		// These FTE and TLF formatting objects are now invalid
		// and must be recreated when needed.
		elementFormat = null;
		if (_htmlHelper)
			_htmlHelper.hostFormat = null;
		
		// The TextLines may need to be recreated
		// and the border and background may need to be redrawn.
		setFlag(FLAG_TEXT_LINES_INVALID |
				FLAG_GRAPHICS_INVALID);
		
		invalidate();
	}
	
	//--------------------------------------------------------------------------
	//
	//  Properties: Private helpers
	//
	//--------------------------------------------------------------------------
		
    /**
     *  @private
     *  The start margin.  Used when using FTE to construct the individual 
     *  text lines and textLine alignment is done in this class.
     */
    private function get leftMargin():Number
    {
        return (direction == "ltr" ?
            Number(_defaultTextFormat.leftMargin) :
            Number(_defaultTextFormat.rightMargin));        
        
    }
    
    /**
     *  @private
     *  The end margin.  Used when using FTE to construct the individual 
     *  text lines and textLine alignment is done in this class.
     */
    private function get rightMargin():Number
    {
        return (direction == "ltr" ?
            Number(_defaultTextFormat.rightMargin) :
            Number(_defaultTextFormat.leftMargin));        
    }
    
	//--------------------------------------------------------------------------
	//
	//  Methods: TextField
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
	 * 
     *  @param newText n/a
     *  
	 *  @see flash.text.TextField#appendText()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function appendText(newText:String):void
	{
		throw new Error(notImplemented("appendText()"));
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
     *  
     *  @param charIndex n/a
     *  
     *  @return n/a
	 * 
	 *  @see flash.text.TextField#getCharBoundaries()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getCharBoundaries(charIndex:int):Rectangle
	{
		throw new Error(notImplemented("getCharBoundaries()"));
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
	 * 
     *  @param x n/a
     *  @param y n/a
     *
     *  @return n/a
     * 
	 *  @see flash.text.TextField#getCharIndexAtPoint()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getCharIndexAtPoint(x:Number, y:Number):int
	{
		throw new Error(notImplemented("getCharIndexAtPoint()"));
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
	 * 
     *  @param charIndex n/a
     * 
     *  @return n/a
     * 
	 *  @see flash.text.TextField#getFirstCharInParagraph()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getFirstCharInParagraph(charIndex:int):int
	{
		throw new Error(notImplemented("getFirstCharInParagraph()"));
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
     * 
     *  @param x n/a
     * 
     *  @param y n/a
     * 
     *  @return n/a
	 * 
	 *  @see flash.text.TextField#getLineIndexAtPoint()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getLineIndexAtPoint(x:Number, y:Number):int
	{
		throw new Error(notImplemented("getLineIndexAtPoint()"));
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
     * 
     *  @param charIndex n/a
     * 
     *  @return n/a
	 * 
	 *  @see flash.text.TextField#getLineIndexOfChar()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getLineIndexOfChar(charIndex:int):int
	{
		throw new Error(notImplemented("getLineIndexOfChar()"));
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
     * 
     *  @param lineIndex n/a
     * 
     *  @return n/a
	 * 
	 *  @see flash.text.TextField#getLineLength()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getLineLength(lineIndex:int):int
	{
		throw new Error(notImplemented("getLineLength()"));
	}
	
	/**
	 *  @copy flash.text.TextField#getLineMetrics()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getLineMetrics(lineIndex:int):TextLineMetrics
	{
		validateNow();
		
		// TextField throws this RTE when invalid values are set.
		if (lineIndex < 0 || lineIndex >= numChildren)
		{
			var message:String = getErrorMessage("badIndex");
			throw new RangeError(message);
		}
		
		// The nth line is the nth child.
		var textLine:TextLine = TextLine(getChildAt(lineIndex));
		
        // Convert textLine.x to the global coordinate space.  The new point 
        // x is relative to textLine.x.
        var x:Number = Math.round(textLine.localToGlobal(new Point(0, 0)).x);
        var width:Number = Math.round(textLine.textWidth);
		
		// TextField computes ascent and descent differently than FTE does.
		// Adding FTE's ascent and descent produces
		// a reasonable approximation of TextField's ascent.
		// TextField's ascent, descent, and leading are always rounded.
		// Rounding FTE's ascent and descent separately, then adding,
		// produces a "TextField ascent" of 12 + 3 or 15 for Arial 12
		// (Flex's default font) on Windows, exactly matching a real
		// TextField's ascent in this most-common case.
		var ascent:Number = Math.round(textLine.ascent) + Math.round(textLine.descent)
		var descent:Number = Math.round(textLine.descent);
		var leading:Number = Math.round(Number(_defaultTextFormat.leading));
		
		var height:Number = ascent + descent + leading;
		
		return new TextLineMetrics(x, width, height, ascent, descent, leading);
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
     * 
     *  @param lineIndex n/a
     * 
     *  @return n/a
	 * 
	 *  @see flash.text.TextField#getLineOffset()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getLineOffset(lineIndex:int):int
	{
		throw new Error(notImplemented("getLineOffset()"));
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
     * 
     *  @param lineIndex n/a
     * 
     *  @return n/a
	 * 
	 *  @see flash.text.TextField#getLineText()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getLineText(lineIndex:int):String
	{
		throw new Error(notImplemented("getLineText()"));
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
     * 
     *  @param charIndex n/a
     * 
     *  @return n/a
	 * 
	 *  @see flash.text.TextField#getParagraphLength()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getParagraphLength(charIndex:int):int
	{
		throw new Error(notImplemented("getParagraphLength()"));
	}
	
	/**
	 *  This method has been implemented in FTETextField
	 *  to simply return a copy of the <code>defaultTextFormat</code>,
	 *  because FTETextField does not support formatting a range.
     * 
     *  @param beginIndex n/a
     * 
     *  @param endIndex n/a
     * 
     *  @return n/a
	 * 
	 *  @see flash.text.TextField#getTextFormat()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getTextFormat(beginIndex:int = -1,
								  endIndex:int = -1):TextFormat
	{
		// TextField returns a new TextFormat instance each time
		// you call getTextFormat(); the proof is that
		//   textField.getTextFormat() != textField.getTextFormat()
		// is true.
		return cloneTextFormat(_defaultTextFormat);
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
     * 
     *  @param value n/a
	 *
 	 *  @see flash.text.TextField#replaceSelectedText()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function replaceSelectedText(value:String):void
	{
		throw new Error(notImplemented("replaceSelectedText()"));
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
     * 
     *  @param beginIndex n/a
     * 
     *  @param endIndex n/a
     * 
     *  @param newText n/a
	 * 
	 *  @see flash.text.TextField#replaceText()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function replaceText(beginIndex:int, endIndex:int,
								newText:String):void
	{
		throw new Error(notImplemented("replaceText()"));
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because FTETextField does not support selection.
	 *  It will throw a runtime error if called.
     * 
     *  @param beginIndex n/a
     * 
     *  @param endIndex n/a
	 * 
	 *  @see flash.text.TextField#setSelection()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function setSelection(beginIndex:int, endIndex:int):void
	{
		throw new Error(notImplemented("setSelection()"));
	}
	
	/**
     *  This method has no effect on a FTETextField if <code>beginIndex</code>
     *  or <code>endIndex</code> does not equal -1
	 *  because FTETextField does not support formatting a range.
     * 
     *  @param format n/a
     * 
     *  @param beginIndex n/a
     * 
     *  @param endIndex n/a
	 *
	 *  @see flash.text.TextField#setTextFormat()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function setTextFormat(format:TextFormat,
								  beginIndex:int = -1,
								  endIndex:int = -1):void
	{
        if (beginIndex == -1 && endIndex == -1)
        {
            defaultTextFormat = format;
            
            // The format changed.  Some of the attributes such as indent
            // and blockIndent require the text to be regenerated.
            setFlag(FLAG_TEXT_LINES_INVALID | FLAG_GRAPHICS_INVALID);
            validateNow();
        }
	}
	
	/**
	 *  This method has not been implemented in FTETextField
	 *  because very few components use it in TextField.
	 *  It will throw a runtime error if called.
     * 
     *  @param id n/a
     * 
     *  @return n/a
	 *
	 *  @see flash.text.TextField#getImageReference()
	 * 
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @langversion 3.0
	 */
	public function getImageReference(id:String):DisplayObject
	{
		throw new Error(notImplemented("getImageReference()"));
	}
	
	//--------------------------------------------------------------------------
	//
	//  Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 */
	mx_internal function testFlag(mask:uint):Boolean
	{
		return (flags & mask) != 0;
	}
	
	/**
	 *  @private
	 */
	mx_internal function setFlag(mask:uint):void
	{
		flags |= mask;
	}
	
	/**
	 *  @private
	 */
	private function clearFlag(mask:uint):void
	{
		flags &= ~mask;
	}
	
	/**
	 *  @private
	 */
	private function setFlagToValue(mask:uint, value:Boolean):void
	{
		if (value)
			flags |= mask;
		else
			flags &= ~mask;
	}
	
	/**
	 *  @private
	 *  This method will cause a 'render' event later,
	 *  in response to which validateNow() will get called.
	 */
	private function invalidate():void
	{
		if (stage)
			stage.invalidate();
		invalidFields[this] = 0;
	}
	
	/**
	 *  @private
	 *  This method is the workhorse of FTETextField.
	 *  It puts it into a state where all properties are consistent
	 *  with each other and where it is rendering what the properties
	 *  specify.
	 */
	private function validateNow():void
	{
		// TODO (gosmith): When do we get recursive validateNow()?
		if (!testFlag(ALL_INVALIDATION_FLAGS) ||
			testFlag(FLAG_VALIDATE_IN_PROGRESS))
		{
			return;
		}
		
		setFlag(FLAG_VALIDATE_IN_PROGRESS);
		if (testFlag(FLAG_TEXT_LINES_INVALID))
		{
			// Remove the previous TextLines
			// (and recycle them, if supported by the player).
			_textWidth = 0;
			_textHeight = 0;
			clipWidth = 0;
						
			// Determine the composition width and height.
			var compositionWidth:Number = NaN; 
			var compositionHeight:Number = NaN; 
			if (_autoSize == TextFieldAutoSize.NONE)
			{
				compositionWidth = _width;
				compositionHeight = _height;
			}
			else if (wordWrap)
			{
				compositionWidth = _width;
			}
			
			if (testFlag(FLAG_HTML_TEXT_SET))
			{
				if (!_htmlHelper.hostFormat)
					createHostFormat();
				
				_htmlHelper.composeHTMLText(compositionWidth, compositionHeight);							
			}
			else
			{
				if (!elementFormat)
					createElementFormat();	
				
				composeText(compositionWidth, compositionHeight);							
			}
		
			var origX:Number = x;
			var origWidth:Number = _width; 
			var origHeight:Number = _height; 
					
			if (_autoSize != TextFieldAutoSize.NONE)
			{
				_height = _textHeight + PADDING_TOP + PADDING_BOTTOM;
				if (!wordWrap)
				{
					_width = _textWidth + PADDING_LEFT + PADDING_RIGHT;
                    
                    var blockIndent:Number = Number(_defaultTextFormat.blockIndent);
                    var indent:Number = Number(_defaultTextFormat.indent);
                    // Factor in indents and margins if the combined total
                    // is positive.
                    if (blockIndent + indent + leftMargin > 0)
                        _width += blockIndent + indent + leftMargin;
                    
                    // Right margin seems to always be considered but if its
                    // negative the width can't get smaller than the text width.
                    _width += rightMargin;
                    if (rightMargin >  0)
                    {
                        clipWidth = _width;                       
                    }
                    else
                    {
                        if (_width - PADDING_LEFT - PADDING_RIGHT < _textWidth ) 
                            _width = _textWidth + PADDING_LEFT + PADDING_RIGHT;
                        // force clipping
                        clipWidth = origWidth + 1;
                    }
                    
					// adjust x for CENTER and RIGHT cases
					if (_autoSize == TextFieldAutoSize.RIGHT)
						x += origWidth - _width;
					else if (_autoSize == TextFieldAutoSize.CENTER)
						x += (origWidth - _width) / 2;
				}
				if (_height != origHeight || _width != origWidth || x != origX)
					setFlag(FLAG_GRAPHICS_INVALID);
			}
			
			if (clipWidth > origWidth || _textHeight > origHeight)
			{
				// need to clip
                //trace("clip", "_textWidth", _textWidth, "origWidth", origWidth);
				var r:Rectangle = scrollRect;
				if (!r)
					r = new Rectangle();
				r.left = 0;
				r.top = 0;
				r.right = _width;
				r.bottom = _height;
                
                // Expand scrollRect by one pixel so the bottom and right
                // borders are not cliped.  See note below.
                if (testFlag(FLAG_GRAPHICS_INVALID) && border)
                {
                    r.width++;
                    r.height++;
                }
				scrollRect = r;
			}
			else 
			{
				// don't need to clip
				//trace("don't clip", "_textWidth", _textWidth, "origWidth", origWidth);
				scrollRect = null;
			}
		}
		
		// Draw the border and background last,
		// once the width and height are known.
		if (testFlag(FLAG_GRAPHICS_INVALID))
		{
			var g:Graphics = graphics;
			g.clear();
			// First draw the background, then draw the border.
			// This is because TextField actually does something strange --- it expands itselft 1 pixel right and down when drawing a border
			// and fill without the stroke with the required stroking path does not match the "background sans border" behavior of TextField.
		
			// Width/Height rounding differences between TextField and FTETextField...
			// For width or height of the form E.5 where E is a positive even integer, Flash 10 on Windows seems to 
			// "round to even", i.e., round the dimension down to E rather than up to E+1. However we currently just 
			// round consistently up to E+1 using Math.round() here since for now are willing to live with this difference.
			var w:Number = rint(_width);
			var h:Number = rint(_height);
				
			// Even if no background is requested, we fill the bounds
			// with alpha=0 pixels so that mouse events are generated.
			g.beginFill(backgroundColor, background ? 1.0 : 0.0);
			g.drawRect(0, 0, w, h);
			g.endFill();
			
			if (border)
			{
				g.lineStyle(1, borderColor);
				g.drawRect(0.5, 0.5, _width, _height); // TextField actually expands by a pixel down and to the right when it has a border!
			}
		}
		
		clearFlag(ALL_INVALIDATION_FLAGS | FLAG_VALIDATE_IN_PROGRESS);
	}
	
	/**
	 *  @private
	 */
	private function createElementFormat():void
	{
		var fontDescription:FontDescription = new FontDescription();
		
		fontDescription.fontLookup = embedFonts ?
									 FontLookup.EMBEDDED_CFF :
									 FontLookup.DEVICE;
		
		fontDescription.fontName = _defaultTextFormat.font;
		
		fontDescription.fontPosture = _defaultTextFormat.italic ?
									  FontPosture.ITALIC :
									  FontPosture.NORMAL;
		
		fontDescription.fontWeight = _defaultTextFormat.bold ?
									 FontWeight.BOLD :
									 FontWeight.NORMAL;
		
		elementFormat = new ElementFormat();
				
		elementFormat.color = uint(_defaultTextFormat.color);
				
		elementFormat.fontDescription = fontDescription;
		
		elementFormat.fontSize = Number(_defaultTextFormat.size);
		
		elementFormat.kerning = _defaultTextFormat.kerning ?
								Kerning.AUTO :
								Kerning.OFF;
								
		elementFormat.locale = locale;
				
		elementFormat.trackingRight = Number(_defaultTextFormat.letterSpacing);
	}
	
	/**
	 *  @private
	 */
	private function createHostFormat():void
	{
		_htmlHelper.hostFormat = new FTETextFieldHostFormat(this);
	}
	
	// used during compose to track nextLineIndex
	static private var nextLineIndex:int;
	
	/**
	 *  @private
	 */
	private function removeExcessTextLines():void
	{
		var n:int = numChildren;
		for (var i:int = nextLineIndex; i < n; i++)
		{
			// Repeatedly removing the 0th child is supposed
			// to be the fastest way to remove all children.
			var textLine:TextLine = TextLine(removeChildAt(nextLineIndex));
			
			// TLF provides a TextLine cache,
			// for use with recreateTextLine().
			TextLineRecycler.addLineForReuse(textLine);
		}
	}
	
	/**
	 *  @private
	 */
	private function composeText(compositionWidth:Number,
								 compositionHeight:Number):void
	{
		var innerWidth:Number =
			compositionWidth - PADDING_LEFT - PADDING_RIGHT;
		var innerHeight:Number =
			compositionHeight - PADDING_TOP - PADDING_BOTTOM;
			
		// FTE's emBox's top gives the ascent and its bottom gives the descent.
		// TextField computes ascent and descent differently than FTE does.
		// Adding FTE's ascent and descent produces
		// a reasonable approximation of TextField's ascent.
		// TextField's ascent, descent, and leading are always rounded.
		// Rounding FTE's ascent and descent separately, then adding,
		// produces a "TextField ascent" of 12 + 3 or 15 for Arial 12
		// (Flex's default font) on Windows, exactly matching a real
		// TextField's ascent in this most-common case.
		var emBox:Rectangle;
        if (fontContext)
            emBox = fontContext.callInContext(elementFormat.getFontMetrics, elementFormat, []).emBox;
        else
            emBox = elementFormat.getFontMetrics().emBox;
		var ascent:int = Math.round(-emBox.top) + Math.round(emBox.bottom);
		var descent:int = Math.round(emBox.bottom);
		var leading:Number = Math.round(Number(_defaultTextFormat.leading));
		
		// Break the text into paragraphs at CR characters.
		// (Each LF character has already been turned into a CR.)
		// We could use split(), but that would create a temporary Array.
		var paragraphY:int = 0;
		var n:int = text.length;
		var i:int = 0;
		
		nextLineIndex = 0;
		
		do
		{
			var j:int = text.indexOf("\r", i);
			if (j == -1)
				j = n;
			var paragraphText:String = i == 0 && j == n ?
									   text :
									   text.substring(i, j);
			
			// Use an FTE TextBlock to compose TextLines
			// for one paragraph of the text, keeping track
			// of how far down we've composed.
			paragraphY = createTextLines(innerWidth, innerHeight,
										 paragraphText, paragraphY,
										 ascent, descent);
										 
			// TextField puts the same leading between paragraphs
			// as between lines in a paragraph.
			paragraphY += leading;
			
			i = j + 1;
		}
		while (j < n);
		
		removeExcessTextLines();
		
		// At this point, all TextLines have been composed
		// and have the correct spacing, but are all left-aligned
		// starting at (0, 0).
		// This method will adjust their x and y so that they
		// are correctly aligned and inset by the left and top padding and 
        // indent and margins.
		alignTextLines(innerWidth);
		
		_textWidth = Math.round(_textWidth);
		_textHeight = Math.round(
			numChildren * (ascent + descent) +
			(numChildren - 1) * Number(_defaultTextFormat.leading));
        clipWidth = Math.round(clipWidth);
	}
	/**
	 *  @private
	 *  Stuffs the specified paragraph text and formatting info into a TextBlock
	 *  and uses it to create as many TextLines as fit into the bounds.
	 *  Returns true if all the text was composed into textLines.
	 */
	private function createTextLines(innerWidth:Number,
									 innerHeight:Number,
									 paragraphText:String,
									 paragraphY:int,
									 ascent:int, descent:int):int
	{
		var textBlock:TextBlock = _defaultTextFormat.align == "justify" ? staticTextBlockAllButLast : staticTextBlockUnjustified;
		// Set the TextBlock's content.
		// Note: If there is no text, we do what TLF does and compose
		// a paragraph terminator character, so that a TextLine
		// gets created and we can measure it.
		// It will have a width of 0 but a height equal
		// to the font's ascent plus descent.
		staticTextElement.text = paragraphText.length > 0 ?
								 paragraphText :
								 "\u2029";
		staticTextElement.elementFormat = elementFormat;
		
		
		textBlock.content = staticTextElement;
		
		// And its bidiLevel.
		textBlock.bidiLevel = direction == "ltr" ? 0 : 1;
		
		// Then create and add TextLines using this TextBlock.
		paragraphY = createTextLinesFromTextBlock(
						innerWidth, innerHeight,
						textBlock, staticTextElement.text.length, paragraphY,
						ascent, descent);
		
		// Cleans up and sets the validity of the lines associated 
		// with the TextBlock to TextLineValidity.INVALID.
		var firstLine:TextLine = textBlock.firstLine;
		if (firstLine)
			textBlock.releaseLines(firstLine, textBlock.lastLine);
			
		return paragraphY;     
	}
	
	/**
	 *  @private
	 *  Compose into textLines.  bounds on input is size of composition
	 *  area and on output is the size of the composed content.
	 *  The caller must call releaseLinesFromTextBlock() to release the
	 *  textLines from the TextBlock.
	 * 
	 *  Returns true if all the text was composed into textLines.
	 */
	private function createTextLinesFromTextBlock(innerWidth:Number,
												  innerHeight:Number,
												  textBlock:TextBlock, textLength:int,
												  paragraphY:int,
												  ascent:int,
												  descent:int):int
	{
		if (innerWidth < 0 || innerHeight < 0)
			return paragraphY;
		
        var blockIndent:Number = Number(_defaultTextFormat.blockIndent);
        var indent:Number = Number(_defaultTextFormat.indent);
        
		var maxLineWidthBeforeIndent:Number =
			wordWrap ? innerWidth : TextLine.MAX_LINE_WIDTH;
        var maxLineWidth:Number = maxLineWidthBeforeIndent;
                
		var n:int = 0;
		var nextTextLine:TextLine;
		var nextY:int = paragraphY;
		var textLine:TextLine;
        
        const thisLeftMargin:Number = leftMargin;
        const thisRightMargin:Number = rightMargin;
        // TextField seems to do this.  You can see it with wordWrap and 
        // indent > width or when rightMargin > width.  In the former case,
        // the first line is visually empty but contains a character which is 
        // clipped and the second line starts with the second letter.  
        // The clipped first line serves as a placeholder so that the rest
        // of the lines which may be visible are composed.
		var fitSomething:Boolean = true;
		
		var charsConsumed:int = 0;
        
		// Generate TextLines, stopping when we run out of text
		// or reach the bottom of the requested bounds.
		// In this loop the lines are positioned within the rectangle
		// (0, 0, innerWidth, innerHeight), with left alignment.
		while (charsConsumed < textLength)
		{
            // Adjust the compose width for indents and margins. 
            if (n <= 1)
            {
                var totalIndent:Number = blockIndent + thisLeftMargin;
                if (n == 0)
                    totalIndent += indent;
                
                if (totalIndent < 0)
                    totalIndent = 0;                
               else if (totalIndent > _width - PADDING_LEFT - PADDING_RIGHT)                
                    totalIndent = _width - PADDING_LEFT - PADDING_RIGHT;
                maxLineWidth = 
                    maxLineWidthBeforeIndent - totalIndent;
                
                if (wordWrap)
                    maxLineWidthBeforeIndent -= thisRightMargin;
                // Stay within the bounds to avoid exception.  Since 
                // fitSomething is true it is okay if maxLineWidth is < 0.
                if (maxLineWidth > TextLine.MAX_LINE_WIDTH)
                    maxLineWidth = TextLine.MAX_LINE_WIDTH;
            }        
            var recycleLine:TextLine = (nextLineIndex < numChildren) ? (getChildAt(nextLineIndex) as TextLine) : TextLineRecycler.getLineForReuse();
			if (recycleLine)
			{
				if (fontContext as ISWFContext)
				{
					nextTextLine = fontContext.callInContext(
						textBlock["recreateTextLine"], textBlock,
						[ recycleLine, textLine, maxLineWidth, 0.0, fitSomething ]);		
				}        
				else
				{
					nextTextLine = textBlock["recreateTextLine"](
						recycleLine, textLine, maxLineWidth, 0.0, fitSomething);
				}  
			}
			else
			{
				if (fontContext as ISWFContext)
				{
					nextTextLine = fontContext.callInContext(
						textBlock.createTextLine, textBlock,
						[ textLine, maxLineWidth, 0.0, fitSomething ]);
				}
				else
				{
					nextTextLine = textBlock.createTextLine(
						textLine, maxLineWidth, 0.0, fitSomething);
				}
			}
			nextTextLine.doubleClickEnabled = false;
			
			charsConsumed += nextTextLine.rawTextLength;
			
			// Determine the natural baseline position for this line.
			// Note: The y coordinate of a TextLine is the location
			// of its baseline, not of its "top".
			if (n == 0)
				nextY += ascent;
			else
				nextY += descent + _defaultTextFormat.leading + ascent;
			
			// We'll keep this line.
			textLine = nextTextLine;
			n++;
			
			// Assign its location based on left/top alignment.
			// Its x position is 0 by default.
			textLine.y = nextY;
			
            // Adjust for positive indent/left margin.  Do it here rather
            // than at the end when alignment is done so the first 
            // line of each paragraph is indented properly.
            textLine.x = totalIndent;            
            
			if (_defaultTextFormat.underline)
			{
				// FTE doesn't render underlines,
				// but it can tell us where to draw them.
				// You can't draw in a TextLine but it can have children,
				// so we create a child Shape to draw them in.
				
                var fontMetrics:FontMetrics;
                if (fontContext)
                    fontMetrics = fontContext.callInContext(elementFormat.getFontMetrics, elementFormat, []);
                else
                    fontMetrics = elementFormat.getFontMetrics();
				
				var shape:Shape = new Shape();
				var g:Graphics = shape.graphics;
				g.lineStyle(fontMetrics.underlineThickness, 
					elementFormat.color, elementFormat.alpha);
				g.moveTo(0, fontMetrics.underlineOffset);
				g.lineTo(textLine.textWidth, fontMetrics.underlineOffset);
				
				textLine.addChild(shape);
			}
			
			if (nextLineIndex == numChildren)
				addChild(textLine);
			nextLineIndex++;
		}
		
		return nextY + descent;
	}
	
	/**
	 *  @private
     *  Returns with _textWidth and clipWidth set.
	 */
	private function alignTextLines(innerWidth:Number):void
	{
        // This is only the case when we are auto sizing.  In this case
        // we don't want to do any alignment.
        if (isNaN(innerWidth))
            innerWidth = 0;
    
		var align:String = _defaultTextFormat.align;
		var leftAligned:Boolean = 
			align == "left" ||
			align == "justify" && direction == "ltr";
		var centerAligned:Boolean = align == "center";
		var rightAligned:Boolean =
			align == "right" ||
            align == "justify" && direction == "rtl"; 
		
		// Calculate loop constants for horizontal alignment.
		var leftOffset:Number = PADDING_LEFT;
		var centerOffset:Number = leftOffset + innerWidth / 2;
		var rightOffset:Number = leftOffset + innerWidth;
        const thisRightMargin:Number = rightMargin;
                
        // Reposition each line if necessary.
        // based on the horizontal alignment,
        // and adjusting for the padding.
        var n:int = numChildren;
        for (var i:int = 0; i < n; i++)
        {
            var textLine:TextLine = TextLine(getChildAt(i));
            
            _textWidth = Math.max(_textWidth, textLine.textWidth);
            
            var width:Number = textLine.x + textLine.textWidth + thisRightMargin;
            
            // Only align if there is width to do so.
            if (leftAligned || (width >= innerWidth && direction == "ltr"))
                textLine.x += leftOffset;
            else if (rightAligned || (width >= innerWidth && direction == "rtl"))
                textLine.x += rightOffset - width;
            else if (centerAligned)
                textLine.x += centerOffset - width / 2;
                
            // If x < 0 then need to force clipping in validateNow() so text 
            // won't leak in this direction.
            if (textLine.x < 0)
                clipWidth = int.MAX_VALUE;
            else
                clipWidth = Math.max(clipWidth, textLine.x + textLine.textWidth);
                    
            textLine.y += PADDING_TOP;
        }
	}
	
	/**
	 *  @private
	 *  Provides RTE messages.
	 *  FTETextField is deliberately kept independent
	 *  of the rest of the Flex framework.
	 *  Therefore it doesn't have access to localized resource strings
	 *  in the ResourceManager and simply has hard-coded English Strings.
	 *  However, framework subclasses such as UIFTETextField override
	 *  this method to provide localized messages from ResourceManager.
	 */
	mx_internal function getErrorMessage(key:String, param:String = null):String
	{
		var message:String = "";
		
		switch (key)
		{
			case "badParameter":
			{
				// This message matches the one in Flash Player.
				message = "Parameter " + param + " must be one of the accepted values.";
				break;
			}
				
			case "nullParameter":
			{
				// This message matches the one in Flash Player.
				message = "Parameter " + param + " must be non-null.";
				break;
			}
				
			case "badIndex":
			{
				// This message matches the one in Flash Player.
				message = "The supplied index is out of bounds.";
				break;
			}
				
			case "notImplementedInFTETextField":
			{
				message = "'" + param + "' is not implemented in FTETextFieldNew.";
				break;
			}
				
			case "unsupportedTypeInFTETextField":
			{
				message = "FTETextFieldNew does not support setting type to \"input\".";
				break;
			}
		}
		
		return message;
	}
	
	/**
	 *  @private
	 */
	private function notImplemented(name:String):String
	{
		return getErrorMessage("notImplementedInFTETextField", name);
	}
	
	//--------------------------------------------------------------------------
	//
	//  Event handlers
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 */
	private function addedToStageHandler(event:Event):void
	{
		invalidate();
	}
	
	
	/**
	 *  @private
	 */
	mx_internal function inlineGraphicStatusChangeHandler(
		event:Event):void
	{
		setFlag(FLAG_TEXT_LINES_INVALID |
			FLAG_GRAPHICS_INVALID);
		
		invalidate();
	}
}
}
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.geom.Rectangle;
import flash.text.StyleSheet;
import flash.text.engine.FontLookup;
import flash.text.engine.FontPosture;
import flash.text.engine.FontWeight;
import flash.text.engine.Kerning;
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.EditingMode;
import flashx.textLayout.elements.Configuration;
import flashx.textLayout.elements.FlowElement;
import flashx.textLayout.elements.LinkElement;
import flashx.textLayout.elements.IConfiguration;
import flashx.textLayout.elements.IFormatResolver;
import flashx.textLayout.elements.LinkElement;
import flashx.textLayout.elements.ParagraphElement;
import flashx.textLayout.elements.SpanElement;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.events.FlowElementMouseEvent;
import flashx.textLayout.events.StatusChangeEvent;
import flashx.textLayout.factory.TextFlowTextLineFactory;
import flashx.textLayout.formats.ITextLayoutFormat;
import flashx.textLayout.formats.LeadingModel;
import flashx.textLayout.formats.LineBreak;
import flashx.textLayout.formats.TextDecoration;
import flashx.textLayout.formats.TextLayoutFormat;
import mx.core.mx_internal;
import helpers.FTETextFieldNew;
use namespace mx_internal;
/**
 *  @private
 */
class FTETextFieldTextContainerManager extends TextContainerManager
{
	/**
	 *  @private
	 */
	public function FTETextFieldTextContainerManager(container:Sprite, configuration:IConfiguration = null)
	{
		super(container, configuration);
	}
	
	/**
	 *  @private
	 */
	override public function drawBackgroundAndSetScrollRect(scrollX:Number, scrollY:Number):Boolean
	{
		return true;
	}
}
/**
 *  @private
 */
class FTETextFieldHostFormat extends TextLayoutFormat
{
	public function FTETextFieldHostFormat(textField:FTETextFieldNew)
	{
		super();
		
		this.textField = textField;
	}
	
	private var textField:FTETextFieldNew;
	
	public override function get color():*
	{
		return textField._defaultTextFormat.color;
	}
	
	public override function get direction():*
	{
		return textField.direction;
	}
	
	public override function get fontFamily():*
	{
		return textField._defaultTextFormat.font;
	}
	
	public override function get fontLookup():*
	{
		return textField.embedFonts ?
			FontLookup.EMBEDDED_CFF :
			FontLookup.DEVICE;
	}
	
	public override function get fontSize():*
	{
		return textField._defaultTextFormat.size;
	}
	
	public override function get fontStyle():*
	{
		return textField._defaultTextFormat.italic ?
			FontPosture.ITALIC :
			FontPosture.NORMAL;
	}
	
	public override function get fontWeight():*
	{
		return textField._defaultTextFormat.bold ?
			FontWeight.BOLD :
			FontWeight.NORMAL;
	}
	
	public override function get kerning():*
	{
		return textField._defaultTextFormat.kerning ?
			Kerning.AUTO :
			Kerning.OFF;
	}
	
	public override function get leadingModel():*
	{
		return LeadingModel.APPROXIMATE_TEXT_FIELD;
	}
	
	public override function get lineBreak():*
	{
		return textField.wordWrap ?
			LineBreak.TO_FIT :
			LineBreak.EXPLICIT;
	}
	
	public override function get lineHeight():*
	{
		return textField._defaultTextFormat.leading;
	}
	
	public override function get locale():*
	{
		return textField.locale;
	}
	
	public override function get paddingBottom():*
	{
		return FTETextFieldNew.PADDING_BOTTOM;
	}
	
	public override function get paddingLeft():*
	{
		return FTETextFieldNew.PADDING_LEFT;
	}
	
	public override function get paddingRight():*
	{
		return FTETextFieldNew.PADDING_RIGHT;
	}
	
	public override function get paddingTop():*
	{
		return FTETextFieldNew.PADDING_TOP;
	}
	
	public override function get paragraphEndIndent():*
	{
		return textField._defaultTextFormat.rightMargin;
	}
	
	public override function get paragraphStartIndent():*
	{
		return textField._defaultTextFormat.leftMargin;
	}
	
	public override function get textAlign():*
	{
		return textField._defaultTextFormat.align;
	}
	
	public override function get textAlignLast():*
	{
		return textField._defaultTextFormat.align;
	}
	
	public override function get textDecoration():*
	{
		return textField._defaultTextFormat.underline ?
			TextDecoration.UNDERLINE :
			TextDecoration.NONE;
	}
	
	public override function get textIndent():*
	{
		return textField._defaultTextFormat.indent;
	}
	
	public override function get trackingRight():*
	{
		return textField._defaultTextFormat.letterSpacing;
	}
}
/**
 *  @private
 *  To attach TextField styling via a style sheet to a TextFlow.
 */
class FTETextFieldStyleResolver implements IFormatResolver
{
    //--------------------------------------------------------------------------
    //
    //  Class variables
    //
    //--------------------------------------------------------------------------
    
    /**
     *  Map of TextField StyleSheet CSS properties to their equivalent
     *  TLF properties. This is only the styles which have different names.
     */
    private static const textFieldToTLFStyleMap:Object =
	{
        "leading": "lineHeight",    
        "letterSpacing": "trackingRight",
        "marginLeft": "paragraphStartIndent",
        "marginRight": "paragraphEndIndent"
    };
    
    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------
    
    public function FTETextFieldStyleResolver(styleSheet:StyleSheet):void
    {
        _styleSheet = styleSheet;
    }
    
    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------
    
    /**
     *  The TextField style sheet that will be used to create the TLF style
     *  objects.
     */
    private var _styleSheet:StyleSheet;
    
    //--------------------------------------------------------------------------
    //
    //  IFormatResolver
    //
    //--------------------------------------------------------------------------
    
    /**
     *  Given a FlowElement or ContainerController object, return any format 
     *  settings for it.
     *
     *  Return format settings for the specified object.
     */
    public function resolveFormat(elem:Object):ITextLayoutFormat
    {
        var attr:TextLayoutFormat = null;
        
        // ContainerController will inherit via the container so it is not
        // handled here.  Map HTML <body> to TextFlow, HTML <p> to
        // ParagraphElement and HTML <span> to SpanElement.
        if (elem is FlowElement)
        {
            if (elem is TextFlow)
                attr = addStyleAttributes(attr, "body");               
            else if (elem is ParagraphElement)
                attr = addStyleAttributes(attr, "p");
            else if (elem is SpanElement)
                attr = addStyleAttributes(attr, "span");
            
            // Apply class selector over any format from above.
            if (elem.styleName != null)
                attr = addStyleAttributes(attr, "." + elem.styleName);
        }        
        
        return attr;
    }
    
    /**
     *  Given a FlowElement or ContainerController object and the name of a 
     *  format property, return the format value or undefined if 
     *  the value is not found.
     *
     *  Return the value of the specified format for the specified object.
     */
    public function resolveUserFormat(elem:Object,userStyle:String):*
    {
        var flowElem:FlowElement = elem as FlowElement;
        var attr:TextLayoutFormat;
        
        // support non-tlf styles
        if (flowElem)
        {
            if (flowElem.styleName)
            {
                attr = addStyleAttributes(null, "." + flowElem.styleName);
            }
            else if (flowElem is LinkElement)
            {
                if (userStyle == "linkNormalFormat")
                    attr = addStyleAttributes(null, "a:link");
                    
                else if (userStyle == "linkHoverFormat")
                    attr = addStyleAttributes(null, "a:hover");
                    
                else if (userStyle == "linkActiveFormat")
                    attr = addStyleAttributes(null, "a:active");
            }
            else
            {
                attr = addStyleAttributes(null, userStyle);
            }            
        }
        
        return attr != null ? attr : undefined;
    }
    
    /** 
     * Invalidates any cached formatting information for a TextFlow so that 
     * formatting must be recomputed.
     */
    public function invalidateAll(tf:TextFlow):void
    {
    }
    
    /**
     * Invalidates cached formatting information on this element because, for 
     * example, the parent changed, or the id or the styleName changed.
     */
    public function invalidate(target:Object):void
    {
    }
    
    /** 
     *  Return the format resolver for the copy of the TextFlow.
     */
    public function getResolverForNewFlow(oldFlow:TextFlow,newFlow:TextFlow):IFormatResolver
    { 
        return this; 
    }
    
    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------
    
    /**
     *  Look up the styleSelector in the TextField style sheet and build the
     *  object of corresponding TLF styles and values.  Return null if the
     *  styleSelector is not found in the style sheet.
     */
    private function addStyleAttributes(
        attr:TextLayoutFormat, 
        styleSelector:String):TextLayoutFormat
    {
        var foundStyle:Object = _styleSheet.getStyle(styleSelector);
        if (foundStyle != null)
        { 		
            for (var p:* in foundStyle)
            {
                var propStyle:Object = foundStyle[p];
                
                if (attr == null)
                    attr = new TextLayoutFormat();
                
                if (textFieldToTLFStyleMap[p])
                {
                    // different name, same values
                    var tlfProp:String = textFieldToTLFStyleMap[p];
                    attr[tlfProp] = propStyle;
                }
                else if (p == "color")
                {
                    // convert from "#000000" to "0x000000" format
                    var color:String = String(propStyle);
                    if (color && color.charAt(0) == "#")
                        attr.color = "0x"+color.substring(1);
                }
                else if (p == "display")
                {
                    // TODO(cframpto): if we decide to support this.
                }
                else if (p == "kerning")
                {
                    // convert from true/false to on/off
                    if (Boolean(propStyle))
                        attr.kerning = flash.text.engine.Kerning.ON;
                    else                
                        attr.kerning = flash.text.engine.Kerning.OFF;
                }
                else
                {
                    // same name, same values
                    attr[p] = propStyle;
                }
            }
        }    
        
        return attr;
    }
}
/** @private Used when needing an HTML view of things. */
class HTMLHelper
{
	/**
	 *  @private
	 */
	private static var plainTextImporter:ITextImporter;
	
	/**
	 *  @private
	 */
	private static var plainTextExporter:ITextExporter;
	
	/**
	 *  @private
	 */
	private static var collapsingHTMLImporter:ITextImporter;
	
	/**
	 *  @private
	 */
	private static var preservingHTMLImporter:ITextImporter;
	
	/**
	 *  @private
	 */
	private static var htmlExporter:ITextExporter;
	
	/**
	 *  @private
	 */
	private static function initClass():void
	{
		var format:TextLayoutFormat;
		var config:Configuration;
		
		// Create an importer for plain text.
		plainTextImporter =
			TextConverter.getImporter(TextConverter.PLAIN_TEXT_FORMAT);
		
		// Create an exporter for plain text.
		plainTextExporter =
			TextConverter.getExporter(TextConverter.PLAIN_TEXT_FORMAT);
		
		// Create an importer for TEXT_FIELD_HTML_FORMAT that collapses whitespace.
		// Note: We have to make a copy of the textFlowInitialFormat,
		// which has various formats set to "inherit",
		// and then modify it and set it back.
		config = new Configuration();
		format = new TextLayoutFormat(config.textFlowInitialFormat);
		format.whiteSpaceCollapse = "collapse";
		config.textFlowInitialFormat = format;
		collapsingHTMLImporter =
			TextConverter.getImporter(TextConverter.TEXT_FIELD_HTML_FORMAT, config);
		collapsingHTMLImporter.throwOnError = false;
		
		// Create an importer for TEXT_FIELD_HTML_FORMAT that preserves whitespace.
		// Note: We have to make a copy of the textFlowInitialFormat,
		// which has various formats set to "inherit",
		// and then modify it and set it back.
		config = new Configuration();
		format = new TextLayoutFormat(config.textFlowInitialFormat);
		format.whiteSpaceCollapse = "preserve";
		config.textFlowInitialFormat = format;
		preservingHTMLImporter =
			TextConverter.getImporter(TextConverter.TEXT_FIELD_HTML_FORMAT, config);
		preservingHTMLImporter.throwOnError = false;
		
		// Create an exporter for TEXT_FIELD_HTML_FORMAT.
		htmlExporter =
			TextConverter.getExporter(TextConverter.TEXT_FIELD_HTML_FORMAT);
	}
	
	initClass();
	
	/**
	 *  @private
	 */
	private static var factory:TextFlowTextLineFactory =
		new TextFlowTextLineFactory();
	
	private var _field:FTETextFieldNew;
	
	public function HTMLHelper(field:FTETextFieldNew)
	{
		this._field = field;
	}
	
	/**
	 *  @private
	 *  If htmlText is set, composeHtmlText is called each time the text lines
	 *  are invalidated.  The setters for many of the properties invalidate
	 *  the text lines.  So although _htmlText is set to null, if there is no
	 *  styleSheet, to indicate to the htmlText getter that it needs to import
	 *  the html from the textFlow, we need the orginal htmlText for the cases 
	 *  where we need to regenerate the html text lines.
	 */
	public var explicitHTMLText:String = null;
	
	/**
	 *  @private
	 *  When we render the htmlText using TLF,
	 *  this object represents the formatting for TLF.
	 *  Every time the defaultTextFormat is set,
	 *  this object is released because it is invalid.
	 *  It is regenerated just in time to render the htmlText.
	 */
	public var hostFormat:ITextLayoutFormat;
	
	/**
	 *  @private
	 *  When we render the htmlText using TLF,
	 *  this object represents the rich text to be displayed.
	 *  It is created by using TLF's HTML importer to import the htmlText.
	 */
	public var textFlow:TextFlow;
	
	/**
	 *  @private
	 *  When we render the htmlText using TLF,
	 *  this object composes the textFlow
	 *  (with the hostFormat applied to it)
	 *  to create TextLines in this Sprite.
	 */
	public var textContainerManager:TextContainerManager;
	
	/**
	 *  @private
	 */
	public var _htmlText:String = null;
	
	/**
	 *  @private
	 */
	public var _styleSheet:StyleSheet = null;
	
	//----------------------------------
	//  htmlImporter
	//----------------------------------
	
	/**
	 *  @private
	 */
	private function get htmlImporter():ITextImporter
	{
		// Note that which importer we return depends on the value
		// of condenseWhite was at the time that htmlText was set,
		// not on the current value of condenseWhite,
		// since it could change between htmlText being set
		// and the TextLines being composed.
		return _field.testFlag(FTETextFieldNew.FLAG_EFFECTIVE_CONDENSE_WHITE) ?
			collapsingHTMLImporter :
			preservingHTMLImporter;
	}	
	
	/**
	 *  @private
	 */
	public function composeHTMLText(compositionWidth:Number,
									 compositionHeight:Number):void
	{
		var textFlow:TextFlow = textFlow = htmlImporter.importToFlow(explicitHTMLText);
		
		// Unless there is a styleSheet, _htmlText is now invalid
		// and needs to be regenerated on demand,
		// because with htmlText what-you-set-is-not-what-you-get.
		if (!_styleSheet)
			_htmlText = null;
		
		if (!textFlow)
			return;
		
		textFlow.addEventListener(MouseEvent.CLICK, linkClickHandler);
		
		textFlow.addEventListener(
			StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE,
			_field.inlineGraphicStatusChangeHandler);
		
		if (!textContainerManager)
			textContainerManager = new FTETextFieldTextContainerManager(_field);
		
		textContainerManager.compositionWidth = compositionWidth;
		textContainerManager.compositionHeight = compositionHeight;
		
		textContainerManager.editingMode = EditingMode.READ_ONLY;
		
		textContainerManager.hostFormat = hostFormat;
		
		textContainerManager.swfContext = _field.fontContext as ISWFContext;
		
		textContainerManager.setTextFlow(textFlow)
		
		// Add a formatResolver if there is a style sheet.  Force a flow
		// composer to be created, if there isn't one, so the format resolver
		// will be used.
		if (_styleSheet && !textFlow.formatResolver)
		{
			textFlow.formatResolver = new FTETextFieldStyleResolver(_styleSheet);
			textContainerManager.beginInteraction();
			textContainerManager.endInteraction();
		}
		
		textContainerManager.updateContainer();
		
		var bounds:Rectangle = textContainerManager.getContentBounds();
		
		_field._textWidth = Math.round(bounds.width);
		_field._textHeight = Math.round(bounds.height);
		
		// TLF takes care of clipping so none should be needed here.
		_field.clipWidth = _field._textWidth;
	}
		
	/**
	 *  @private
	 */
	private function linkClickHandler(event:FlowElementMouseEvent):void
	{
		// Need to remove the event: portion of the href if it has one.
		// Only dispatch the event if it has the event portion.
		var href:String = LinkElement(event.flowElement).href;
		if (href.indexOf("event:") == 0)
		{
			var textEvent:TextEvent = new TextEvent(TextEvent.LINK);
			textEvent.text = href.substring(6);
			_field.dispatchEvent(textEvent);
		}
	}
	
	/**
	 *  @private
	 */
	public function getHtmlText(fieldText:String):String
	{		
		if (_htmlText == null)
		{
			// We can optimize the default case
			// that there is no text or hmtlText.
			if (fieldText == "")
			{
				_htmlText = "";
			}
			else
			{
				// Import the plain text into a TextFlow,
				// and then export the TextFlow into HTML.
				if (!textFlow)
					textFlow = plainTextImporter.importToFlow(fieldText);
				_htmlText = String(htmlExporter.export(
					textFlow, ConversionType.STRING_TYPE));
			}
		}	
		
		return _htmlText;
	}
	
	/**
	 *  @private
	 */
	public function set styleSheet(value:StyleSheet):void
	{	
		_styleSheet = value;
		
		var textFlow:TextFlow = textFlow;
		if (textFlow && textFlow.formatResolver)
			textFlow.formatResolver = null;
	}
	
	public function getText():String
	{
		var rslt:String;
		
		// If we don't already have a TextFlow,
		// create one by importing the htmlText.
		if (!textFlow)
			textFlow = htmlImporter.importToFlow(_htmlText);
		
		// Export plain text from the TextFlow.
		return String(plainTextExporter.export(
			textFlow, ConversionType.STRING_TYPE));
	}
}