////////////////////////////////////////////////////////////////////////////////
//
// ADOBE SYSTEMS INCORPORATED
// Copyright 2007-2010 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE:  Adobe permits you to use, modify, and distribute this file 
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
package flashx.textLayout.compose
{
	import flash.display.DisplayObject;
	import flash.display.GraphicsPathCommand;
	import flash.display.GraphicsPathWinding;
	import flash.display.Shape;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.text.engine.ElementFormat;
	import flash.text.engine.FontMetrics;
	import flash.text.engine.TextBlock;
	import flash.text.engine.TextElement;
	import flash.text.engine.TextLine;
	import flash.text.engine.TextLineValidity;
	import flash.text.engine.TextRotation;
	import flash.utils.Dictionary;
	
	import flashx.textLayout.container.ContainerController;
	import flashx.textLayout.debug.Debugging;
	import flashx.textLayout.debug.assert;
	import flashx.textLayout.edit.ISelectionManager;
	import flashx.textLayout.edit.SelectionFormat;
	import flashx.textLayout.elements.BackgroundManager;
	import flashx.textLayout.elements.ContainerFormattedElement;
	import flashx.textLayout.elements.FlowElement;
	import flashx.textLayout.elements.FlowLeafElement;
	import flashx.textLayout.elements.FlowValueHolder;
	import flashx.textLayout.elements.InlineGraphicElement;
	import flashx.textLayout.elements.LinkElement;
	import flashx.textLayout.elements.LinkState;
	import flashx.textLayout.elements.ListElement;
	import flashx.textLayout.elements.ListItemElement;
	import flashx.textLayout.elements.ParagraphElement;
	import flashx.textLayout.elements.SpanElement;
	import flashx.textLayout.elements.SubParagraphGroupElementBase;
	import flashx.textLayout.elements.TCYElement;
	import flashx.textLayout.elements.TextFlow;
	import flashx.textLayout.factory.StringTextLineFactory;
	import flashx.textLayout.formats.BackgroundColor;
	import flashx.textLayout.formats.BlockProgression;
	import flashx.textLayout.formats.Direction;
	import flashx.textLayout.formats.Float;
	import flashx.textLayout.formats.FormatValue;
	import flashx.textLayout.formats.IListMarkerFormat;
	import flashx.textLayout.formats.ITextLayoutFormat;
	import flashx.textLayout.formats.JustificationRule;
	import flashx.textLayout.formats.LeadingModel;
	import flashx.textLayout.formats.LineBreak;
	import flashx.textLayout.formats.ListMarkerFormat;
	import flashx.textLayout.formats.ListStylePosition;
	import flashx.textLayout.formats.TextAlign;
	import flashx.textLayout.formats.TextDecoration;
	import flashx.textLayout.formats.TextLayoutFormat;
	import flashx.textLayout.tlf_internal;
	import flashx.textLayout.utils.CharacterUtil;
	import flashx.textLayout.utils.Twips;
	
	use namespace tlf_internal;
	
	
	/** 
	 * The TextFlowLine class represents a single line of text in a text flow.
	 * 
	 * <p>Use this class to access information about how a line of text has been composed: its position, 
	 * height, width, and so on. When the text flow (TextFlow) is modified, the lines immediately before and at the  
	 * site of the modification are marked as invalid because they need to be recomposed. Lines after
	 * the site of the modification might not be damaged immediately, but they might be regenerated once the
	 * text is composed. You can access a TextFlowLine that has been damaged, but any values you access
	 * reflect the old state of the TextFlow. When the TextFlow is recomposed, it generates new lines and you can 
	 * get the new line for a given position by calling <code>TextFlow.flowComposer.findLineAtPosition()</code>.</p>
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	public final class TextFlowLine implements IVerticalJustificationLine 
	{
		
		/** @private - the selection block cache */
		static private var _selectionBlockCache:Dictionary = new Dictionary(true);
				
		private var _absoluteStart:int;		// text-offset of start of line - from beginning of the TextFlow
		private var _textLength:int;		// number of chars to next line (incl trailing spaces, etc.)
		private var _x:Number = 0;			// left edge of line; left as Number because it is user-settable
		private var _y:Number = 0;			// top edge of line; left as Number because it is user-settable

		private var _height:Number = 0;				// y advance
		private var _outerTargetWidth:Number = 0;	// width line is composed to, excluding indents


		private var _boundsLeftTW:int = 2;			// text line bounds: logical left
		private var _boundsRightTW:int = 1;			// text line bounds: logical right (if left > right, then it is not set)
		
		private var _para:ParagraphElement;				// owning paragraph
		private var _controller:ContainerController;	// what frame the line was composed into
		private var _columnIndex:int;					// column number in the container
		
		private var _adornCount:int = 0;
				
		static private const VALIDITY_MASK:uint = 7;		// 3 bits
		static private const ALIGNMENT_SHIFT:uint = 3;
		static private const ALIGNMENT_MASK:uint = 24; 		// 2 bits
		static private const NUMBERLINE_MASK:uint = 32;		// 1 bit
		static private const GRAPHICELEMENT_MASK:uint = 64;	// 1 bit
		
		private var _flags:uint;
		
		// added to support TextFlowLine when TextLine not available

		private var _ascent:Number;
		private var _descent:Number;
		private var _targetWidth:Number;
		private var _lineOffset:Number;
		private var _lineExtent:Number;	// content bounds logical width for the line
		private var _accumulatedLineExtent:Number;
		private var _accumulatedMinimumStart:Number;
		private var _numberLinePosition:int;
		

		/** Constructor - creates a new TextFlowLine instance. 
		 *  <p><strong>Note</strong>: No client should call this. It's exposed for writing your own composer.</p>
		 *
		 * @param textLine The TextLine display object to use for this line.
		 * @param paragraph The paragraph (ParagraphElement) in which to place the line.
		 * @param outerTargetWidth The width the line is composed to, excluding indents.
		 * @param lineOffset The line's offset in pixels from the appropriate container inset (as dictated by paragraph direction and container block progression), prior to alignment of lines in the paragraph. 
		 * @param absoluteStart	The character position in the text flow at which the line begins.
		 * @param numChars	The number of characters in the line.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 *
	 	 * @see flash.text.engine.TextLine
	 	 * @see flashx.textLayout.elements.ParagraphElement
	 	 * @see #absoluteStart
	 	 */
		public function TextFlowLine(textLine:TextLine, paragraph:ParagraphElement, outerTargetWidth:Number = 0, lineOffset:Number = 0, absoluteStart:int = 0, numChars:int = 0)
		{
			initialize(paragraph, outerTargetWidth, lineOffset, absoluteStart,numChars,textLine);		
		}
		
		/** @private */
		tlf_internal function initialize(paragraph:ParagraphElement, outerTargetWidth:Number = 0, lineOffset:Number = 0, absoluteStart:int = 0, numChars:int = 0, textLine:TextLine = null):void
		{
			_para = paragraph;
			_outerTargetWidth = outerTargetWidth;
			_absoluteStart = absoluteStart;
			_textLength = numChars;

			_adornCount = 0;
			_lineExtent = 0;
			_accumulatedLineExtent = 0;
			_accumulatedMinimumStart = TextLine.MAX_LINE_WIDTH;
			
			_flags = 0;
			_controller = null;
			
			if (textLine)
			{
				textLine.userData = this;
				_targetWidth = textLine.specifiedWidth;
				_ascent = textLine.ascent;
				_descent = textLine.descent;
				// the docs say this is true
				CONFIG::debug { assert(textLine.textHeight == textLine.ascent+textLine.descent,"bad textheight"); }
				_lineOffset = lineOffset;
				setValidity(textLine.validity);
				CONFIG::debug { assert (textLine.validity == TextLineValidity.VALID, "Initializing TextFlowLine to invalid TextLine"); }
				setFlag(textLine.hasGraphicElement?GRAPHICELEMENT_MASK:0,GRAPHICELEMENT_MASK);
			}
			else 
				setValidity(TextLineValidity.INVALID);
		}
		
		private function setFlag(value:uint,mask:uint):void
		{
			CONFIG::debug { assert((value|mask) == mask,"TFL:setFlag bad value"); }
			_flags = (_flags & ~mask) | value;
		}
		private function getFlag(mask:uint):uint
		{
			return _flags & mask;
		}
		
		/** @private */
		tlf_internal function get heightTW():int
		{ return Twips.to(_height); }
		/** @private */
		tlf_internal function get outerTargetWidthTW():int
		{ return Twips.to(_outerTargetWidth); }
		
		/** @private */
		tlf_internal function get ascentTW():int
		{ return Twips.to(_ascent); }
		/** @private */
		tlf_internal function get targetWidthTW():int
		{ return Twips.to(_targetWidth); }
		/** @private */
		tlf_internal function get textHeightTW():int
		{ return Twips.to(textHeight); }
		/** @private */
		tlf_internal function get lineOffsetTW():int
		{ return Twips.to(_lineOffset); }
		/** @private */
		tlf_internal function get lineExtentTW():int
		{ return Twips.to(_lineExtent); }
		
		/** @private */
		tlf_internal function get hasGraphicElement():Boolean
		{ return getFlag(GRAPHICELEMENT_MASK) != 0; }
		
		/** @private */
		tlf_internal function get hasNumberLine():Boolean
		{ return getFlag(NUMBERLINE_MASK) != 0; }
		
		/** @private */
		tlf_internal function get numberLinePosition():Number
		{ return Twips.from(_numberLinePosition); }
		/** @private */
		tlf_internal function set numberLinePosition(position:Number):void
		{ 
			CONFIG::debug { assert(Twips.from(Twips.to(position)) == position,"Bad numberLinePosition"); }
			_numberLinePosition = Twips.to(position); 
		}
		
		/**
		 * The height of the text line, which is equal to <code>ascent</code> plus <code>descent</code>. The 
		 * value is calculated based on the difference between the baselines that bound the line, either 
		 * ideographic top and bottom or ascent and descent depending on whether the baseline at y=0 
		 * is ideographic (for example, TextBaseline.IDEOGRAPHIC_TOP) or not. 
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 * @see flash.text.engine.TextBaseline TextBaseline
		 */
		
		public function get textHeight():Number 
		{ return _ascent+_descent; }	
		
		/** 
		 * The horizontal position of the line relative to its container, expressed as the offset in pixels from the 
		 * left of the container.
		 * <p><strong>Note: </strong>Although this property is technically <code>read-write</code>, 
		 * you should treat it as <code>read-only</code>. The setter exists only to satisfy the
		 * requirements of the IVerticalJustificationLine interface that defines both a getter and setter for this property.
		 * Use of the setter, though possible, will lead to unpredictable results.
		 * </p>
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 * @see #y
		 */
		public function get x():Number
		{ return _x; }
		
		/** 
		 * This comment is ignored, but the setter should not be used and exists only to satisfy
		 * the IVerticalJustificationLine interface.
		 * @see flashx.textLayout.compose.IVerticalJustificationLine 
		 * @private 
		 */
		public function set x(lineX:Number):void
		{ 
			_x = lineX; 
			// invalidate bounds
			_boundsLeftTW = 2;
			_boundsRightTW = 1;
		}
		
		/** @private */
		tlf_internal function get xTW():int
		{ return Twips.to(_x); }

		/** 
		 * The vertical position of the line relative to its container, expressed as the offset in pixels from the top 
		 * of the container.
		 * <p><strong>Note: </strong>Although this property is technically <code>read-write</code>, 
		 * you should treat it as <code>read-only</code>. The setter exists only to satisfy the
		 * requirements of the IVerticalJustificationLine interface that defines both a getter and setter for this property.
		 * Use of the setter, though possible, will lead to unpredictable results.
		 * </p>
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 * 
		 * @see #x
		 */
		public function get y():Number
		{ return _y;  }
		
		/** @private */
		tlf_internal function get yTW():int
		{ return Twips.to(_y); }

		/** This comment is ignored, but the setter should not be used and exists only to satisfy
		 * the IVerticalJustificationLine interface.
		 * @see flashx.textLayout.compose.IVerticalJustificationLine
		 * @private
		 */
		public function set y(lineY:Number):void
		{ 
			_y = lineY;
			// invalidate bounds
			_boundsLeftTW = 2;
			_boundsRightTW = 1;
		}
		
		/** @private */
		tlf_internal function setXYAndHeight(lineX:Number,lineY:Number,lineHeight:Number):void
		{
			_x = lineX;
			_y = lineY;
			_height = lineHeight;
			// invalidate bounds
			_boundsLeftTW = 2;
			_boundsRightTW = 1;
		}
		
		/** 
		 * One of the values from TextFlowLineLocation for specifying a line's location within a paragraph.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 *
	 	 * @see flashx.textLayout.elements.ParagraphElement
	 	 * @see TextFlowLineLocation
		 */
		
		public function get location():int
		{
			if (_para)
			{
				var lineStart:int = _absoluteStart - _para.getAbsoluteStart();
				
				// Initialize settings for location
				if (lineStart == 0)		// we're at the start of the paragraph
					return _textLength == _para.textLength ? TextFlowLineLocation.ONLY : TextFlowLineLocation.FIRST;
				if (lineStart + textLength == _para.textLength)	// we're at the end of the para
					return TextFlowLineLocation.LAST;
			}
			return TextFlowLineLocation.MIDDLE;
		}
		
		/** 
		 * The controller (ContainerController object) for the container in which the line has been placed.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 * 
		 * @see flashx.textLayout.container.ContainerController 
		 */
		
		public function get controller():ContainerController
		{ return _controller; }
		
		/** The number of the column in which the line has been placed, with the first column being 0.
		 *		
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 */
		
		public function get columnIndex():int
		{ return _columnIndex; }
		
		/** @private */
		tlf_internal function setController(cont:ContainerController,colNumber:int):void
		{ 
			_controller = cont as ContainerController;
			_columnIndex = colNumber;
		}
		
		/** The height of the line in pixels.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 *
		 */
		
		public function get height():Number
		{ return _height; }
		
		/** 
		 * @copy flash.text.engine.TextLine#ascent
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 */
		
		public function get ascent():Number
		{ return _ascent; }
		
		/** 
		 * @copy flash.text.engine.TextLine#descent
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 */
		
		public function get descent():Number
		{ return _descent; }
		
		/** 
		 * The line's offset in pixels from the appropriate container inset (as dictated by paragraph direction and container block progression), 
		 * prior to alignment of lines in the paragraph.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 */
		public function get lineOffset():Number
		{
			return _lineOffset;
		}
		
		
		/** 
		 * The paragraph (ParagraphElement) in which the line resides.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 * 
	 	 * @see flashx.textLayout.elements.ParagraphElement
	 	 */
	 	 
		public function get paragraph():ParagraphElement
		{ return _para; }
		
		/** 
		 * The location of the line as an absolute character position in the TextFlow object.
		 * 
		 * @return 	the character position in the text flow at which the line begins.
		 *
		 * @playerversion Flash 10
	 	 * @playerversion AIR 1.5
	 	 * @langversion 3.0
 		 *
 		 * @see flashx.textLayout.elements.TextFlow
		 */
		
		public function get absoluteStart():int
		{ return _absoluteStart; }
		/** @private */
		tlf_internal function setAbsoluteStart(val:int):void
		{ _absoluteStart = val; }
		
		/** 
		 * The number of characters to the next line, including trailing spaces. 
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 */
		
		public function get textLength():int
		{ return _textLength; }
		/** @private */
		tlf_internal function setTextLength(val:int):void
		{ 
			_textLength = val; 
			//	assert(_validity == TextLineValidity.INVALID, "not already damaged");
			damage(TextLineValidity.INVALID);
		}
		
		/** 
		 * The amount of space to leave before the line.
		 * <p>If the line is the first line of a paragraph that has a space-before applied, the line will have
		 * a <code>spaceBefore</code> value. If the line comes at the top of a column, <code>spaceBefore</code> is ignored. 
		 * Otherwise, the line follows another line in the column, and it is positioned vertically to insure that there is
		 * at least this much space left between this line and the last line of the preceding paragraph.</p> 
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 * @see flashx.textLayout.formats.TextLayoutFormat#paragraphSpaceBefore TextLayoutFormat.paragraphSpaceBefore
		 */
		
		public function get spaceBefore():Number
		{ 
		 	return (this.location & TextFlowLineLocation.FIRST)? _para.computedFormat.paragraphSpaceBefore : 0;
		}
		
		/** 
		 * The amount of space to leave after the line.
		 * <p>If the line is the last line of a paragraph that has a space-after, the line will have
		 * a <code>spaceAfter</code> value. If the line comes at the bottom of a column, then the <code>spaceAfter</code>
		 * is ignored. Otherwise, the line comes before another line in the column, and the following line must be positioned vertically to
		 * insure that there is at least this much space left between this last line of the paragraph and the first
		 * line of the following paragraph.</p> 
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 * @see flashx.textLayout.formats.TextLayoutFormat#paragraphSpaceAfter TextLayoutFormat.paragraphSpaceAfter
		 */
		
		public function get spaceAfter():Number
		{ 
			return ((this.location & TextFlowLineLocation.LAST) ? _para.computedFormat.paragraphSpaceAfter : 0); 			
		}
		
		/** @private 
		 * Target width not including paragraph indents */
		tlf_internal function get outerTargetWidth():Number
		{ return _outerTargetWidth; }
		
		/** @private */
		tlf_internal function set outerTargetWidth(val:Number):void
		{ _outerTargetWidth = val; }
		
		/** @private  
		 * Amount of space used to break the line
		 * <p>The target width is the amount of space allowed for the line, including the space required for indents.</p>
		 */
		tlf_internal function get targetWidth():Number
		{ return _targetWidth; }
		
		/** 
		 * Returns the bounds of the line as a rectangle.
		 *
		 * @return a rectangle that represents the boundaries of the line.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 */
		
		public function getBounds():Rectangle
		{	
			var textLine:TextLine = getTextLine(true);
			if (!textLine)
				return new Rectangle();
			
			// TODO: just use the textLine.x and textLine.y - after all getTextLine now sets them.
			// not going to change this right now though
			var bp:String = paragraph.getAncestorWithContainer().computedFormat.blockProgression;
			var shapeX:Number = this.x;
			var shapeY:Number = createShapeY(bp);
			if (bp == BlockProgression.TB)
				shapeY += descent-textLine.height;
			return new Rectangle(shapeX, shapeY, textLine.width, textLine.height);			
		}
		
		static private const _validities:Array = [ TextLineValidity.INVALID, TextLineValidity.POSSIBLY_INVALID, TextLineValidity.STATIC, TextLineValidity.VALID, FlowDamageType.GEOMETRY ];
		
		private function setValidity(value:String):void
		{
			CONFIG::debug { assert(_validities.indexOf(value) != -1,"Bad alignment passed to TextFlowLine"); }
			setFlag(_validities.indexOf(value),VALIDITY_MASK);			
		}
		/** The validity of the line. 
		 * <p>A line can be invalid if the text, the attributes applied to it, or the controller settings have
		 * changed since the line was created. An invalid line can still be displayed, and you can use it, but the values
		 * used will be the values at the time it was created. The line returned by <code>getTextLine()</code> also will be in an
		 * invalid state. </p>
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 *
	 	 * @see #getTextLine()
	 	 * @see flash.text.engine.TextLine#validity TextLine.validity
	 	 * @see FlowDamageType#GEOMETRY
		 */
		
		public function get validity():String
		{ 
			return _validities[getFlag(VALIDITY_MASK)];
		}
		
		/** 
		 * The width of the line if it was not justified. For unjustified text, this value is the same as <code>textLength</code>. 
		 * For justified text, this value is what the length would have been without justification, and <code>textLength</code> 
		 * represents the actual line width. For example, when the following String is justified and assigned a width of 500, it 
		 * has an actual width of 500 but an unjustified width of 268.9921875. 
		 *
		 * @internal TBD: add graphic of justified line
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 */
		
		public function get unjustifiedTextWidth():Number
		{ 
			// hack - outerTargetWidth holds value from the factory
			var textLine:TextLine = getTextLine(true);
			return textLine.unjustifiedTextWidth + (_outerTargetWidth - targetWidth); 
		}
		
		/** @private */
		tlf_internal function get lineExtent():Number
		{
			return _lineExtent;
		}
		
		/** @private */
		tlf_internal function set lineExtent(value:Number):void
		{
			_lineExtent = value;
		}
		
		/** @private */
		tlf_internal function get accumulatedLineExtent():Number
		{
			return _accumulatedLineExtent;
		}

		/** @private */
		tlf_internal function set accumulatedLineExtent(value:Number):void
		{
			_accumulatedLineExtent = value;
		}
		
		/** @private */
		tlf_internal function get accumulatedMinimumStart():Number
		{
			return _accumulatedMinimumStart;
		}
		
		/** @private */
		tlf_internal function set accumulatedMinimumStart(value:Number):void
		{
			_accumulatedMinimumStart = value;
		}
		
		static private const _alignments:Array = [ TextAlign.LEFT, TextAlign.CENTER, TextAlign.RIGHT ];
		
		/** @private */
		tlf_internal function get alignment():String
		{
			return _alignments[getFlag(ALIGNMENT_MASK)>>ALIGNMENT_SHIFT];
		}
		
		/** @private */
		tlf_internal function set alignment(value:String):void
		{
			CONFIG::debug { assert(_alignments.indexOf(value) != -1,"Bad alignment passed to TextFlowLine"); }
			setFlag(_alignments.indexOf(value)<<ALIGNMENT_SHIFT,ALIGNMENT_MASK);
		} 
		
		/** @private 
		 * True if the line needs composing. */
		tlf_internal function isDamaged():Boolean
		{ 
			return (validity != TextLineValidity.VALID)
		}
		
		/** @private
		 * Mark the line as valid */
		tlf_internal function clearDamage():void
		{ 
			CONFIG::debug { assert(validity == FlowDamageType.GEOMETRY, "can't clear damage other than geometry"); }
			setValidity(TextLineValidity.VALID); 
		}
		
		/** @private
		 * Mark the line as damaged */
		
		tlf_internal function damage(damageType:String):void
		{
			// trace("TextFlowLine.damage ", this.start.toString(), this.textLength.toString());
			var current:String = validity;
			if (current == damageType || current == TextLineValidity.INVALID)
				return;	// totally damaged
			setValidity(damageType);
		}
		
		/** @private */
		/**
		 * Check if the line is visible by comparing a set rectangle to the supplied
		 * rectangle (all values in Twips).
		 * @private
		 */
		tlf_internal function isLineVisible(wmode:String, x:int, y:int, w:int, h:int):Boolean
		{
			CONFIG::debug{ assert(hasLineBounds(),"Bad call to TextFlowLine.isLineVisible"); }
			
			if (wmode == BlockProgression.RL)
				return _boundsRightTW >= x && _boundsLeftTW < x + w;
			
			return _boundsRightTW >= y && _boundsLeftTW < y + h;
		}
		
		/** @private
		 * Set the text line bounds rectangle, all values in Twips.
		 * If left > right, the rectangle is considered not to be set.
		 * @private
		 */
		tlf_internal function cacheLineBounds(wmode:String, bndsx:Number, bndsy:Number, bndsw:Number, bndsh:Number):void
		{
			if (wmode == BlockProgression.RL)
			{
				_boundsLeftTW = Twips.to(bndsx);
				_boundsRightTW = Twips.to(bndsx + bndsw);				
			}
			else
			{
				_boundsLeftTW = Twips.to(bndsy);
				_boundsRightTW = Twips.to(bndsy + bndsh);
			}
		}
		
		/** @private
		 * Check if the text line bounds are set. If the stored left
		 * value is > the right value, then the rectangle is not set.
		 * @private
		 */
		tlf_internal function hasLineBounds():Boolean
		{
			return (_boundsLeftTW <= _boundsRightTW);
		}
				
		/** @private */
		CONFIG::debug public function toString():String
		{
			return "x:" + x + " y: " + y + " absoluteStart:" + absoluteStart + " textLength:" + textLength +  " location: " + location + " validity: " + validity;
		}
		
		/** 
		 * Indicates whether the <code>flash.text.engine.TextLine</code> object for this TextFlowLine exists.  
		 * The value is <code>true</code> if the TextLine object has <em>not</em> been garbage collected and 
		 * <code>false</code> if it has been.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 * @see flash.text.engine.TextLine TextLine
		 */
		
		public function get textLineExists():Boolean
		{
			return peekTextLine() != null;			
		}
		
		/** @private
		 * Returns the associated TextLine if there is one. Finds it by looking up in the TextBlock.
		 */
		tlf_internal function peekTextLine():TextLine
		{
			var textLine:TextLine;
			
			if (!paragraph)
				return null;
						
			// Look it up in the textBlock
			var textBlock:TextBlock = paragraph.peekTextBlock();
			if (textBlock)
			{
				for (textLine = textBlock.firstLine; textLine; textLine = textLine.nextLine)
				{
					if (textLine.userData == this) // found it
						return textLine;
				}
			}
			return null;
		}
		
		/** 
		 * Returns the <code>flash.text.engine.TextLine</code> object for this line, which might be recreated 
		 * if it does not exist due to garbage collection. Set <code>forceValid</code> to <code>true</code>
		 * to cause the TextLine to be regenerated. Returns null if the TextLine cannot be recreated.
		 *.
		 * @param forceValid	if true, the TextLine is regenerated, if it exists but is invalid.
		 *
		 * @return object for this line or <code>null</code> if the TextLine object cannot be 
		 * recreated.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 * @see flash.text.engine.TextLine TextLine
		 */
		
		public function getTextLine(forceValid:Boolean = false):TextLine
		{ 				
			var textLine:TextLine = peekTextLine();
			if (textLine && textLine.validity == FlowDamageType.GEOMETRY)
				createShape(paragraph.getTextFlow().computedFormat.blockProgression, textLine);
			else if (!textLine || (textLine.validity == TextLineValidity.INVALID && forceValid))
			{
			/*	if (!textLine)
				{
					trace("text line does not exist: regenerating");
					textLine = peekTextLine();
				}
				else
					trace("textline is invalid: regenerated"); */
				if (isDamaged() && validity != FlowDamageType.GEOMETRY)
					return null;
				
				textLine = getTextLineInternal();
			}
			
			return textLine;
		}

		private function getTextLineInternal():TextLine
		{			
			// Look it up in the textBlock
			var paraAbsStart:int = paragraph.getAbsoluteStart();
			
			// If we haven't found it yet, we need to regenerate it.
			// Regenerate the whole paragraph at once, up to the current position. 
			var textBlock:TextBlock = paragraph.getTextBlock();
			var currentLine:TextLine = textBlock.firstLine;
			var flowComposer:IFlowComposer = paragraph.getTextFlow().flowComposer;
			var lineIndex:int = flowComposer.findLineIndexAtPosition(paraAbsStart);
			var previousLine:TextLine = null;
			var textLine:TextLine;
			do
			{
				var line:TextFlowLine = flowComposer.getLineAt(lineIndex);
				CONFIG::debug { assert (line && line.paragraph == paragraph, "Expecting line in same paragraph"); }
				if (currentLine != null && currentLine.validity == TextLineValidity.VALID && (line != this || currentLine.userData == line))
				{
					textLine = currentLine;
					currentLine = currentLine.nextLine;
				}
				else
				{
					textLine = line.recreateTextLine(textBlock, previousLine);
					currentLine = null;
				}
				previousLine = textLine;
				++lineIndex;
			} while (line != this);			
			
			// Put it in the cache, so we can find it there next time
		//	textLineCache[this] = textLine;
			
			return textLine;
		}
		
		/** @private Regenerate the TextLine -- called when textLine has been gc'ed */
		tlf_internal function recreateTextLine(textBlock:TextBlock, previousLine:TextLine):TextLine
		{
			var textLine:TextLine;
			
			var textFlow:TextFlow = _para.getTextFlow();
			var bp:String = textFlow.computedFormat.blockProgression;
			var flowComposer:IFlowComposer = textFlow.flowComposer;
			var swfContext:ISWFContext = flowComposer.swfContext ? flowComposer.swfContext : BaseCompose.globalSWFContext;
			
			var numberLine:TextLine;
			var effLineOffset:Number = _lineOffset;
			if (hasNumberLine)
			{
				var boxStartTotalIndent:Number = this._lineOffset - _para.computedFormat.textIndent;
				numberLine = TextFlowLine.createNumberLine(_para.getParentByType(ListItemElement) as ListItemElement, _para, flowComposer.swfContext, boxStartTotalIndent);
				if (numberLine)
				{
					if (getNumberLineListStylePosition(numberLine) == ListStylePosition.INSIDE)
						effLineOffset += getNumberLineInsideLineWidth(numberLine);
				}
			}
			
			//	trace("Recreating line from", absoluteStart, "to", absoluteStart + textLength);
			textLine = TextLineRecycler.getLineForReuse();
			if (textLine)
			{
				CONFIG::debug { assert(textFlow.backgroundManager == null || textFlow.backgroundManager..getEntry(textLine) === undefined,"Bad TextLine in recycler cache"); }
				textLine = swfContext.callInContext(textBlock["recreateTextLine"], textBlock, [ textLine, previousLine, _targetWidth, effLineOffset, true ]);
			}
			else
				textLine = swfContext.callInContext(textBlock.createTextLine, textBlock, [ previousLine, _targetWidth, effLineOffset, true ]);
			
			textLine.x = this.x;
			CONFIG::debug { Debugging.traceFTEAssign(textLine,"x", this.x);  }
			textLine.y = createShapeY(bp);
			CONFIG::debug { Debugging.traceFTEAssign(textLine,"y", createShapeY(bp));  }
			textLine.doubleClickEnabled = true;
			textLine.userData = this;
			
			// Regenerate adornments (e.g., underline & strikethru)
			if (_adornCount > 0)
			{
				var paraStart:int = _para.getAbsoluteStart();
				var elem:FlowLeafElement = _para.findLeaf(this.absoluteStart - paraStart);
				var elemStart:int = elem.getAbsoluteStart();
				
				CONFIG::debug { assert(textLine.userData == this, "textLine doesn't point back to TextFlowLine"); }
				if (numberLine)
				{
					var listItemElement:ListItemElement = _para.getParentByType(ListItemElement) as ListItemElement;
					TextFlowLine.initializeNumberLinePosition(numberLine, listItemElement, _para, textLine.textWidth);	
				}
				
				createAdornments(_para.getAncestorWithContainer().computedFormat.blockProgression, elem, elemStart, textLine, numberLine);
				
				if (numberLine && getNumberLineListStylePosition(numberLine) == ListStylePosition.OUTSIDE)
				{
					if (bp == BlockProgression.TB)
						numberLine.x = this.numberLinePosition;
					else
						numberLine.y = this.numberLinePosition;
				}
			}
			
			return textLine;
		}
		
		/** @private */
		tlf_internal function createShape(bp:String, textLine:TextLine):void
		{
			var newX:Number = this.x;
			//if (int(newX*20) != int(textLine.x*20))
			{
				textLine.x = newX;
				CONFIG::debug { Debugging.traceFTEAssign(textLine,"x", newX);  }
			}
			var newY:Number = createShapeY(bp);
			//if (int(newY*20) != int(textLine.y*20))
			{
				textLine.y = newY;
				CONFIG::debug { Debugging.traceFTEAssign(textLine,"y", newY);  }
			}
		}
		
		private function createShapeY(bp:String):Number
		{ return bp == BlockProgression.RL ? y : y + _ascent; }
		
		/** @private 
		 * Scan through the format runs within the line, and draw any underline or strikethrough that might need it
		 */	
		tlf_internal function createAdornments(blockProgression:String,elem:FlowLeafElement,elemStart:int, textLine:TextLine, numberLine:TextLine):void
		{
			CONFIG::debug { assert(elemStart == elem.getAbsoluteStart(),"bad elemStart passed to createAdornments"); } 
			var endPos:int = _absoluteStart + _textLength;
			
			CONFIG::debug { assert(textLine.validity == TextLineValidity.VALID,"createAdornments: bad TextLine validity"); }
			/*if (textLine.validity != TextLineValidity.VALID)
			{
				// This can happen if we are scrolling through text and lines have been released, 
				// then scrolled back into view before they're gc'ed. Then we have an invalid line in the TextFlowLine
				// cache, so we create a valid text line to calculate where the adornment shapes should go. The 
				// actual shapes will be added to the invalid (original) TextLine in the TextFlowLine cache.
				textLine = getTextLineInternal();
			}*/
			
			//init adornments back to 0 - may be redoing the line in a new position
			// This can happen if there was damage earlier in the paragraph, so that the TextLine was damaged (because all lines in block were damaged)
			// but the TextFlowLine is still considered OK (didn't cause a line break change).
		//	CONFIG::debug { assert(_adornCount == 0 && _hasNumberLine == false,"createAdornments: adornments applied twice?"); }
			_adornCount = 0;
			
			if (numberLine)
			{
				_adornCount++;
				setFlag(NUMBERLINE_MASK,NUMBERLINE_MASK);
				textLine.addChild(numberLine);
				CONFIG::debug { Debugging.traceFTECall(null,textLine,"addChildNumberLine",numberLine); }	
				
				// handle background on the numberLine
				if (getNumberLineBackground(numberLine) != null)
				{
					var bgm:BackgroundManager = elem.getTextFlow().getBackgroundManager();
					if (bgm)
						bgm.addNumberLine(textLine,numberLine);
				}
			}
			else
				setFlag(0,NUMBERLINE_MASK);

			
			for (;;)
			{				
				_adornCount += elem.updateAdornments(textLine, blockProgression);
				
				var elemFormat:ITextLayoutFormat = elem.format;
				var imeStatus:* = elemFormat ? elemFormat.getStyle("imeStatus") : undefined;
				if(imeStatus)
				{
					elem.updateIMEAdornments(textLine, blockProgression, imeStatus as String);
				}
				elemStart += elem.textLength;
				if (elemStart >= endPos)
					break;
				elem = elem.getNextLeaf(_para);
				CONFIG::debug { assert(elem != null,"bad nextLeaf"); }
			}
		}

		/** @private */
		static tlf_internal function initializeNumberLinePosition(numberLine:TextLine,listItemElement:ListItemElement,curParaElement:ParagraphElement,totalWidth:Number):void
		{
			// use the listStylePosition on the ListItem (not the list)
			var listMarkerFormat:IListMarkerFormat = listItemElement.computedListMarkerFormat();
			var paragraphFormat:ITextLayoutFormat =  curParaElement.computedFormat;
			// only applies on outside list markers
			var listEndIndent:Number = listMarkerFormat.paragraphEndIndent === undefined || listItemElement.computedFormat.listStylePosition == ListStylePosition.INSIDE ? 0 
				: (listMarkerFormat.paragraphEndIndent == FormatValue.INHERIT ? paragraphFormat.paragraphEndIndent : listMarkerFormat.paragraphEndIndent);
			
			TextFlowLine.setListEndIndent(numberLine,listEndIndent);
			
			// no more work needed for OUTSIDE positioning - its all done in the applyTextAlign code
			if (listItemElement.computedFormat.listStylePosition == ListStylePosition.OUTSIDE)
			{
				numberLine.x = numberLine.y = 0;
				return;
			}
			
			var bp:String = curParaElement.getTextFlow().computedFormat.blockProgression;
			var numberLineWidth:Number = TextFlowLine.getNumberLineInsideLineWidth(numberLine);
			
			if (bp == BlockProgression.TB)
			{
				if (paragraphFormat.direction == Direction.LTR)
					numberLine.x = -numberLineWidth;
				else
					numberLine.x = totalWidth + numberLineWidth-numberLine.textWidth;		
				numberLine.y = 0;	// assumes same baseline as parent!!
			}
			else
			{
				if (paragraphFormat.direction == Direction.LTR)
					numberLine.y = -numberLineWidth;
				else
					numberLine.y = totalWidth + numberLineWidth-numberLine.textWidth;						
				numberLine.x = 0;	// assumes same baseline as parent!!
			}
		}
		
		static private var numberLineFactory:NumberLineFactory;
		
		/** @private Logic to generate and position the TextLine containing the numbering for a listElement's first line */
		static tlf_internal function createNumberLine(listItemElement:ListItemElement,curParaElement:ParagraphElement,swfContext:ISWFContext, totalStartIndent:Number):TextLine
		{
			CONFIG::debug { assert(swfContext != BaseCompose.globalSWFContext, "TextFlowLine.createNumberLine: don't pass globalswfcontext"); }
			if (numberLineFactory == null)
			{
				numberLineFactory = new NumberLineFactory();
				numberLineFactory.compositionBounds = new Rectangle(0,0,NaN,NaN);
			}
			numberLineFactory.swfContext = swfContext;
			
			var listMarkerFormat:IListMarkerFormat = listItemElement.computedListMarkerFormat();
			
			// use the listStylePosition on the ListItem (not the list)
			numberLineFactory.listStylePosition = listItemElement.computedFormat.listStylePosition;
			
			var listElement:ListElement = listItemElement.parent as ListElement;
			var paragraphFormat:TextLayoutFormat = new TextLayoutFormat(curParaElement.computedFormat);
			var boxStartIndent:Number = paragraphFormat.direction == Direction.LTR  ? listElement.getEffectivePaddingLeft() : listElement.getEffectivePaddingRight();
			// this just gets the first line but that's the only one we use.  could have used paragraphStartIndent or padding/margins.
			// do it this way so that negative indents are supported.  TODO revisit when box model work is complete
			paragraphFormat.apply(listMarkerFormat);
			paragraphFormat.textIndent += totalStartIndent;
			if (numberLineFactory.listStylePosition == ListStylePosition.OUTSIDE)
				paragraphFormat.textIndent -= boxStartIndent;
			numberLineFactory.paragraphFormat = paragraphFormat; // curParaElement.computedFormat;
			numberLineFactory.textFlowFormat = curParaElement.getTextFlow().computedFormat;
			
			// suppress the formatting of any links
			var firstLeaf:FlowLeafElement = curParaElement.getFirstLeaf();
			var parentLink:LinkElement = firstLeaf.getParentByType(LinkElement) as LinkElement;
			// record the topmost parent link
			var highestParentLinkLinkElement:LinkElement;
			var linkStateArray:Array = [];
			while (parentLink)
			{
				highestParentLinkLinkElement = parentLink;
				linkStateArray.push(parentLink.linkState);
				parentLink.chgLinkState(LinkState.SUPPRESSED);
				parentLink = parentLink.getParentByType(LinkElement) as LinkElement;
			}

			// spanFormat to use for the markers
			var spanFormat:TextLayoutFormat = new TextLayoutFormat(firstLeaf.computedFormat);
			
			// now restore the formatting of any links
			parentLink = firstLeaf.getParentByType(LinkElement) as LinkElement;
			while (parentLink)
			{
				linkStateArray.push(parentLink.linkState);
				parentLink.chgLinkState(linkStateArray.shift());
				parentLink = parentLink.getParentByType(LinkElement) as LinkElement;
			}
			
			// forces recompute of computedFormat of all leaf nodes of highestParentLinkLinkElement
			if (highestParentLinkLinkElement)
			{
				var leaf:FlowLeafElement = highestParentLinkLinkElement.getFirstLeaf();
				while (leaf)
				{
					leaf.computedFormat;
					leaf = leaf.getNextLeaf(highestParentLinkLinkElement);
				}
			}
			
			// finalize the spanFormat for the marker
			var markerFormat:TextLayoutFormat = new TextLayoutFormat(spanFormat);
			TextLayoutFormat.resetModifiedNoninheritedStyles(markerFormat);
			var holderStyles:Object = (listMarkerFormat as TextLayoutFormat).getStyles();
			for (var key:String in holderStyles)
			{
				// only copy TextLayoutFormat properties
				if (TextLayoutFormat.description[key] !== undefined)
				{
					var val:* = holderStyles[key];
					markerFormat[key] = (val !== FormatValue.INHERIT) ? val : spanFormat[key];
				}
			}
			numberLineFactory.markerFormat = markerFormat;
			numberLineFactory.text = listElement.computeListItemText(listItemElement,listMarkerFormat);
			
			// expect one or zero lines - technically with beforeContent and afterContent more than one line can be generated.  This could be more like a float!!
			// also need to expect a backgroundColor
			var rslt:Array = [];
			numberLineFactory.createTextLines(function (o:DisplayObject):void { rslt.push(o); });

			// position it relative to the parent line - later need to take inside/outside into account
			var numberLine:TextLine = rslt[0] as TextLine;
			if (numberLine)
			{				
				CONFIG::debug { assert(numberLine.validity == TextLineValidity.STATIC,"Invalid validity on numberLine"); }
				numberLine.mouseEnabled = false;
				numberLine.mouseChildren = false;
				setNumberLineBackground(numberLine,numberLineFactory.backgroundManager);
			}
			numberLineFactory.clearBackgroundManager();

			return numberLine;
		}
		
		/** @private 
		 * Scan through the format runs within the line, and figure out what the leading for the overall line is.
		 * The line's leading is equal to the maximum leading of any individual run within the line.
		 * The leading in an individual format run is calculated by looking at the leading attribute in the
		 * CharacterFormat. If it is set to a value, we just use that value. Otherwise, if it is set to AUTO,
		 * we calculate the leading based on the point size and the auto leading percentage from the ParagraphFormat.
		 */
		
		tlf_internal function getLineLeading(bp:String,elem:FlowLeafElement,elemStart:int):Number
		{
			CONFIG::debug { assert(elemStart == elem.getAbsoluteStart(),"bad elemStart passed to getLineLeading"); } 
			var endPos:int = _absoluteStart + _textLength;
			var totalLeading:Number = 0;
			CONFIG::debug { assert(elem.getAncestorWithContainer() != null,"element with no container"); }
			for (;;)
			{
				// If there's only one element in the line, and it has 0 leading, try resetting the leading using the default algorithm. Important
				// for lines that contain only a float (which can otherwise recurse infinitely), and TCY spans in vertical text.
				var elemLeading:Number = elem.getEffectiveLineHeight(bp);
				if (!elemLeading && elem.textLength == this.textLength)
					elemLeading = TextLayoutFormat.lineHeightProperty.computeActualPropertyValue(elem.computedFormat.lineHeight, elem.computedFormat.fontSize);
				totalLeading = Math.max(totalLeading, elemLeading);
				elemStart += elem.textLength;
				if (elemStart >= endPos)
					break;
				elem = elem.getNextLeaf(_para);
				CONFIG::debug { assert(elem != null,"bad nextLeaf"); }
			}
			return totalLeading;
		}
		
		/** @private 
		 * Scan through the format runs within the line, and figure out what the typographic ascent (i.e. ascent relative to the 
		 * Roman baseline) for the overall line is. Normally it is the distance between the Roman and Ascent baselines, 
		 * but it may be adjusted upwards by the width/height of the GraphicElement.
		 */
		tlf_internal function getLineTypographicAscent(elem:FlowLeafElement,elemStart:int,textLine:TextLine):Number
		{
			CONFIG::debug { assert(elemStart == elem.getAbsoluteStart(),"bad elemStart passed to getLineTypographicAscent"); } 
			return getTextLineTypographicAscent(textLine ? textLine: getTextLine(), elem, elemStart, absoluteStart+textLength);
		}
		
		/** @private 
		 * Scan through the format runs within the line, and figure out what the typographic ascent (i.e. ascent relative to the 
		 * Roman baseline) for the overall line is. Normally it is the distance between the Roman and Ascent baselines, 
		 * but it may be adjusted upwards by the width/height of the GraphicElement.
		 */
		static tlf_internal function getTextLineTypographicAscent(textLine:TextLine, elem:FlowLeafElement,elemStart:int, textLineEnd:int):Number
		{
			CONFIG::debug { assert(!elem || elemStart == elem.getAbsoluteStart(),"bad elemStart passed to getTextLineTypographicAscent"); } 
			var rslt:Number = textLine.getBaselinePosition(flash.text.engine.TextBaseline.ROMAN) - textLine.getBaselinePosition(flash.text.engine.TextBaseline.ASCENT);
			
			if (textLine.hasGraphicElement)
			{
				for (;;) 
				{
					if (elem is InlineGraphicElement)
						rslt = Math.max(rslt,InlineGraphicElement(elem).getTypographicAscent(textLine));
					elemStart += elem.textLength;
					if (elemStart >= textLineEnd)
						break;
					elem = elem.getNextLeaf();
					CONFIG::debug { assert(elem != null,"bad nextLeaf"); }
				}
			}
			return rslt;
		}
		
		/** @private 
		 * Get the "line box" for the line as defined by the CSS visual formatting model (http://www.w3.org/TR/CSS2/visuren.html)
		 * Essentially, the union of all "inline boxes" on the line.
		 * @return A rectangle representing the line box. Top and Bottom are relative to the Roman baseline. Left and Right are ignored.
		 * May return null, for example, if the line only contains a float.
		 */
		tlf_internal function getCSSLineBox(bp:String, elem:FlowLeafElement, elemStart:int, swfContext:ISWFContext, effectiveListMarkerFormat:ITextLayoutFormat=null, numberLine:TextLine=null):Rectangle
		{
			CONFIG::debug { assert(elem.getAncestorWithContainer() != null,"element with no container"); }
			CONFIG::debug { assert(elemStart == elem.getAbsoluteStart(),"bad elemStart passed to getCSSLineBox"); }
			
			var lineBox:Rectangle;
			
			var endPos:int = _absoluteStart + _textLength;
			var textLine:TextLine = getTextLine();
			
			for (;;)
			{
				addToLineBox(elem.getCSSInlineBox(bp, textLine, _para, swfContext));
				
				elemStart += elem.textLength;
				if (elemStart >= endPos)
					break;
				
				elem = elem.getNextLeaf(_para);
				CONFIG::debug { assert(elem != null,"bad nextLeaf"); }
			}
			
			if (effectiveListMarkerFormat && numberLine)
			{
				// Para not available for number line, but the methods below handle null para correctly.
				var para:ParagraphElement = null; 
				
				var ef:ElementFormat = FlowLeafElement.computeElementFormatHelper (effectiveListMarkerFormat, para, swfContext);
				var metrics:FontMetrics	= swfContext ? swfContext.callInContext(ef.getFontMetrics, ef, null, true) : ef.getFontMetrics();
				
				addToLineBox (FlowLeafElement.getCSSInlineBoxHelper(effectiveListMarkerFormat, metrics, numberLine, para));
			}
			
			function addToLineBox (inlineBox:Rectangle):void
			{
				if (inlineBox)
					lineBox = lineBox ? lineBox.union(inlineBox) : inlineBox;
			}
					
			return lineBox;
		}
		
		//helper method to determine which subset of line is underlined
		//I believe this will be replaced by the eventSink mechanism
		private function isTextlineSubsetOfSpan(element:FlowLeafElement): Boolean
		{ 
			var spanStart:int = element.getAbsoluteStart();
			var spanEnd:int = spanStart + element.textLength;
			
			var lineStart:int = this.absoluteStart;
			var lineEnd:int = this.absoluteStart + this._textLength;
			
			return spanStart <= lineStart && spanEnd >= lineEnd;
		}
		
		
		
		/** Create a rectangle for selection */
		static private function createSelectionRect(selObj:Shape, color:uint, x:Number, y:Number, width:Number, height:Number):DisplayObject
		{
			selObj.graphics.beginFill(color);
			var cmds:Vector.<int> = new Vector.<int>();
			var pathPoints:Vector.<Number> = new Vector.<Number>();
			
			//set the start point - topLeft
			cmds.push(GraphicsPathCommand.MOVE_TO);
			pathPoints.push(x);
			pathPoints.push(y);
			
			//line to topRight
			cmds.push(GraphicsPathCommand.LINE_TO);
			pathPoints.push(x + width);
			pathPoints.push(y);
			
			//line to botRight
			cmds.push(GraphicsPathCommand.LINE_TO);
			pathPoints.push(x + width);
			pathPoints.push(y + height);
			
			//line to botLeft
			cmds.push(GraphicsPathCommand.LINE_TO);
			pathPoints.push(x);
			pathPoints.push(y + height);
			
			//line to close the path - topLeft
			cmds.push(GraphicsPathCommand.LINE_TO);
			pathPoints.push(x);
			pathPoints.push(y);
			
			selObj.graphics.drawPath(cmds, pathPoints, flash.display.GraphicsPathWinding.NON_ZERO);
			
			return selObj;			
		}
		
		/** @private getSelectionShapesCacheEntry
		 * 
		 * creates and adds block selection(s) to the text container.  In most circumstances,
		 * this method will produce and add a single DisplayObject, but in certain circumstances,
		 * such as TCY in TTB text, will need to make multiple selection rectangles.
		 * 
		 * Examples:
		 * 1) horizontal - ABCDE
		 * 2) vertical - ABCDE
		 * 3) horizontal - ABcdE
		 * 4) vertical:		A
		 * 					B
		 * 				   cde
		 * 					F
		 * 
		 */
		private function getSelectionShapesCacheEntry(begIdx:int, endIdx:int, prevLine:TextFlowLine, nextLine:TextFlowLine, blockProgression:String):SelectionCache
		{
			if (isDamaged())
				return null;
			
			//get the absolute start of the paragraph.  Calculation is expensive, so just do this once.
			var paraAbsStart:int = _para.getAbsoluteStart();
			
			//if the indexes are identical and are equal to the start of the line, then
			//don't draw anything.  This prevents a bar being drawn on a following line when
			//selecting accross line boundaries
			//with exception for a selection that includes just the first character of an empty last line in the TextFlow
			if (begIdx == endIdx && paraAbsStart + begIdx == absoluteStart)
			{
				if (absoluteStart != _para.getTextFlow().textLength-1)
					return null;
				endIdx++;
			}
			
			//the cached selection bounds and rects
			var selectionCache:SelectionCache = _selectionBlockCache[this];
			if (selectionCache && selectionCache.begIdx == begIdx && selectionCache.endIdx == endIdx)
				return selectionCache;			
			
			var drawRects:Array = new Array();
			//an array to store any tcy rectangles which need separate processing and may not exist
			var tcyDrawRects:Array = new Array();
			
			if(selectionCache == null)
			{
				selectionCache = new SelectionCache();
				_selectionBlockCache[this] = selectionCache;
			}	
			else
			{
				selectionCache.clear();
			}
			selectionCache.begIdx = begIdx;
			selectionCache.endIdx = endIdx;
			
			var textLine:TextLine = getTextLine();
			var heightAndAdj:Array = getRomanSelectionHeightAndVerticalAdjustment(prevLine, nextLine);
			calculateSelectionBounds(textLine, drawRects, begIdx, endIdx, blockProgression, heightAndAdj);
			
			//iterate the blocks and create DisplayObjects to draw...
			for each(var drawRect:Rectangle in drawRects)
			{
				CONFIG::debug{ assert(selectionCache != null, "If we're caching, selectionArray should never be null!"); }
				//we have to make new rectangles or the convertLineRectToGlobal will alter the cached ones!
				selectionCache.pushSelectionBlock(drawRect);
			}
			
			
			//allow the atoms to be garbage collected.
			if (textLine)
				textLine.flushAtomData();
			
			return selectionCache;
		}
		
		
		/** @private - helper method to calculate all selection blocks within a line.*/
		tlf_internal function calculateSelectionBounds(textLine:TextLine, rectArray:Array, begIdx:int, endIdx:int, blockProgression:String, heightAndAdj:Array):void
		{
			//the direction of the text
			var direction:String = _para.computedFormat.direction;
			//get the absolute start of the paragraph.  Calculation is expensive, so just do this once.
			var paraAbsStart:int = _para.getAbsoluteStart();
			//the current index.  used to iterate to the next element
			var curIdx:int = begIdx;
			//the current FlowLeafElement as determined by curIdx
			var curElem:FlowLeafElement = null;
			//the hightest glyph.  Needed to normalize the rectangles we'll be building
			var largestRise:Number = 0;
			
			//blockRectArray holds each leaf's blocks which could be 1 or more
			var blockRectArray:Array = new Array();
			//floatRectArray holds the selection rects for any floats in the range.
			var floatRectArray:Array = null;
			//tcyDrawRects:Array
			var tcyDrawRects:Array = null;
			
			//do this loop and only afterwards perform the normalization and addition to the rectArr
			while(curIdx < endIdx)
			{
				curElem = _para.findLeaf(curIdx);
				//if we somehow got a 0 length element, then increment the index and continue
				if(curElem.textLength == 0)
				{
					++curIdx;
					continue;
				}
				else if(curElem is InlineGraphicElement && (curElem as InlineGraphicElement).computedFloat != Float.NONE)
				{
					if(floatRectArray == null)
						floatRectArray = new Array();
					
				//	var blockRect:Rectangle = (curElem as InlineGraphicElement).graphic.getBounds(textLine);
					var ilg:InlineGraphicElement = (curElem as InlineGraphicElement);
					var floatInfo:FloatCompositionData = controller.getFloatAtPosition(paraAbsStart + curIdx);
					if (floatInfo)
					{
						var blockRect:Rectangle = new Rectangle(floatInfo.x - textLine.x, floatInfo.y - textLine.y, ilg.elementWidth, ilg.elementHeight);
						floatRectArray.push(blockRect);
					}
					++curIdx;
					continue;
				}
				//the number of potential glyphs to hilite.  Could larger than needs be if we are only selecting part of it.
				var numCharsSelecting:int = curElem.textLength + curElem.getElementRelativeStart(_para) - curIdx;
				//the index of the last glyph to hilite.  If a partial selection, use endIdx
				var endPos:int = (numCharsSelecting + curIdx) > endIdx ? endIdx : (numCharsSelecting + curIdx);
				
				//if this is not a TCY in vertical, the blocks should all be running in the same direction
				if (blockProgression != BlockProgression.RL || 
					(textLine.getAtomTextRotation(textLine.getAtomIndexAtCharIndex(curIdx)) != TextRotation.ROTATE_0))
				{
					var leafBlockArray:Array = makeSelectionBlocks(textLine, curIdx, endPos, paraAbsStart, blockProgression, direction, heightAndAdj);
					//copy all the blocks into the blockRectArray - we'll normalize them later
					for(var leafBlockIter:int = 0; leafBlockIter < leafBlockArray.length; ++leafBlockIter)
					{
						blockRectArray.push(leafBlockArray[leafBlockIter]);
					}
				}
				else
				{
					var tcyBlock:FlowElement = curElem.getParentByType(TCYElement);
					CONFIG::debug{ assert(tcyBlock != null, "What kind of object is this that is ROTATE_0, but not TCY?");}
					
					//if this element is still encompassed by a SubParagraphGroupElementBase of some kind (either a link or a TCYBlock)
					//keep moving up to the parent.  Otherwise, the below code will go into an infinite loop.  bug 1905734
					var tcyParentRelativeEnd:int = tcyBlock.parentRelativeEnd;
					var subParBlock:SubParagraphGroupElementBase = tcyBlock.getParentByType(SubParagraphGroupElementBase) as SubParagraphGroupElementBase;
					while (subParBlock)
					{
						tcyParentRelativeEnd += subParBlock.parentRelativeStart;
						subParBlock = subParBlock.getParentByType(SubParagraphGroupElementBase) as SubParagraphGroupElementBase;
					}
					
					var largestTCYRise:Number = 0;
					var lastTCYIdx:int = endIdx < tcyParentRelativeEnd ? endIdx : tcyParentRelativeEnd;
					var tcyRects:Array = new Array();
					
					
					while(curIdx < lastTCYIdx)
					{
						curElem = _para.findLeaf(curIdx);
						numCharsSelecting = curElem.textLength + curElem.getElementRelativeStart(_para) - curIdx;
						endPos = numCharsSelecting + curIdx > endIdx ? endIdx : numCharsSelecting + curIdx;
						var tcyRectArray:Array =  makeSelectionBlocks(textLine, curIdx, endPos, paraAbsStart, blockProgression, direction, heightAndAdj);
						
						for(var tcyBlockIter:int = 0; tcyBlockIter < tcyRectArray.length; ++tcyBlockIter)
						{
							var tcyRect:Rectangle = tcyRectArray[tcyBlockIter];
							
							if(tcyRect.height > largestTCYRise)
							{
								largestTCYRise = tcyRect.height;
							}
							
							tcyRects.push(tcyRect);
						}
						curIdx = endPos;
					}
					
					if(!tcyDrawRects)
						tcyDrawRects = new Array();
					
					normalizeRects(tcyRects, tcyDrawRects, largestTCYRise, BlockProgression.TB, direction);
					continue;
					
				}
				
				//set the curIdx to the last char in the block
				curIdx = endPos;
			}
			
			
			
			//adding check for an empty set of draw rects.  If there are not recangles, skip this.
			//this can happen is there are ONLY TCY blocks and the whole line is selected.
			//Watson 2273832. - gak 02.09.09
			//if the whole line is selected
			if(blockRectArray.length > 0 && (paraAbsStart + begIdx) == absoluteStart && (paraAbsStart + endIdx) == (absoluteStart + textLength))
			{
				curElem = _para.findLeaf(begIdx);
				//if we have the entire line selected, but the first element is NOT the last, then
				//we will land up with a selection which is 1 character wider than it should be.
				if(((curElem.getAbsoluteStart() + curElem.textLength) < (absoluteStart + textLength)) && endPos >= 2)
				{
					//make sure that this is a white char and that we aren't deselecting the last
					//char in a line - esp important for scripts which don't use spaces ie Japanese
					var charCode:int = _para.getCharCodeAtPosition(endPos - 1);
					if(charCode != SpanElement.kParagraphTerminator.charCodeAt(0) && CharacterUtil.isWhitespace(charCode))
					{
						var lastElemBlockArray:Array = makeSelectionBlocks(textLine, endPos - 1, endPos - 1, paraAbsStart, blockProgression, direction, heightAndAdj);
						var lastRect:Rectangle = lastElemBlockArray[lastElemBlockArray.length - 1];
						var modifyRect:Rectangle = blockRectArray[blockRectArray.length - 1] as Rectangle;
						
						if (blockProgression != BlockProgression.RL)
						{
							//if they have the same width, simply remove the last block
							if(modifyRect.width == lastRect.width)
							{
								blockRectArray.pop();
							}
							else
							{
								modifyRect.width -= lastRect.width;
								
								//if this is RTL, we need to shift the selection block over by the amount
								//we reduced it.
								if(direction == Direction.RTL)
									modifyRect.left -= lastRect.width;
							}
						}
						else
						{
							//if they have the same height, simply remove the last block
							if(modifyRect.height == lastRect.height)
							{
								blockRectArray.pop();
							}
							else
							{
								modifyRect.height -= lastRect.height;
								
								//if this is RTL, we need to shift the selection block down by the amount
								//we reduced it.
								if(direction == Direction.RTL)
									modifyRect.top += lastRect.height;
							}
						}
					}
				}
			}
			
			normalizeRects(blockRectArray, rectArray, largestRise, blockProgression, direction);
			//add in the TCY Rects
			if(tcyDrawRects && tcyDrawRects.length > 0)
			{
				for(var tcyIter:int = 0; tcyIter < tcyDrawRects.length; ++tcyIter)
				{
					rectArray.push(tcyDrawRects[tcyIter]);
				}
			}
			
			//float selections do not normalize, put them into the rect array now
			if(floatRectArray)
			{
				for(var floatIter:int = 0; floatIter < floatRectArray.length; ++floatIter)
				{
					rectArray.push(floatRectArray[floatIter]);
				}
			}
			
		}
		
		private function createSelectionShapes(selObj:Shape, selFormat:SelectionFormat, container:DisplayObject, begIdx:int, endIdx:int, prevLine:TextFlowLine, nextLine:TextFlowLine):void
		{
			var contElement:ContainerFormattedElement = _para.getAncestorWithContainer();
			CONFIG::debug { assert(contElement != null,"para with no container"); }
			var blockProgression:String = contElement.computedFormat.blockProgression;
			
			var selCache:SelectionCache = getSelectionShapesCacheEntry(begIdx, endIdx, prevLine, nextLine, blockProgression);
			if (!selCache)
				return;
			
			//iterate the blocks and create DisplayObjects to draw...
			var drawRect:Rectangle;
			var color:uint = selFormat.rangeColor;
			
			if (_para && _para.getTextFlow()) {
				var selMgr:ISelectionManager = _para.getTextFlow().interactionManager;
				if (selMgr && (selMgr.anchorPosition == selMgr.activePosition))
					color = selFormat.pointColor;
			}
			
			for each (drawRect in selCache.selectionBlocks)
			{
				drawRect = drawRect.clone();
				convertLineRectToContainer(drawRect, true);
				createSelectionRect(selObj, color, drawRect.x, drawRect.y, drawRect.width, drawRect.height);
			}
		}
		
		/** @private 
		 * Get the height and vertical adjustment for the line's selection shape, assuming Western typographic rules
		 * where leading is included in selection.
		 * @return An array with two elements
		 * [0] height
		 * [1] vertical adjustment to counter 'align bottom' behavior. The remainder of the selection code assumes selection shape
		 * bottom is to be aligned with line descent. If this is not the case, vertical adjustment is set to an appropriate non-zero value. 
		 */
		tlf_internal function getRomanSelectionHeightAndVerticalAdjustment (prevLine:TextFlowLine, nextLine:TextFlowLine):Array
		{	
			var rectHeight:Number = 0;
			var verticalAdj:Number = 0; //  Default to 'align bottom'.
			
			//This code erroneously assumed that it would only be called with a SPACE justifier and that AUTO would be up.  That is incorrect
			//because some scripts, like Korean, use an up leading model and the EAST_ASIAN justifier.  New code just performs the check
			if(ParagraphElement.useUpLeadingDirection(_para.getEffectiveLeadingModel()))
			{			
				// "Space above, align bottom" 
				// 1) Space above as dictated by first baseline offset for the first line or line leading otherwise (both obtained from the 'height' data member)
				// 2) Selection rectangle must at least include all of the text area
				rectHeight = Math.max(height, textHeight);
				
				// 3) Selection rectangle's bottom aligned with line descent; verticalAdj remains 0
			}
			else
			{
				// TODO-9/4/08-Is this the right way to check for first/last lines? 
				var isFirstLine:Boolean = !prevLine || prevLine.controller != controller || prevLine.columnIndex != columnIndex;
				var isLastLine:Boolean  = !nextLine || nextLine.controller != controller || nextLine.columnIndex != columnIndex
					|| nextLine.paragraph.getEffectiveLeadingModel() == LeadingModel.ROMAN_UP;
				//I'm removing this line as it makes the assumption that AUTO leading dir is UP only for Roman text, which is incorrect.
				//Korean also uses UP leading but uses the EastAsian justifier. - gak 01.22.09
				//||(nextLine.paragraph.computedFormat.leadingDirection == LeadingDirection.AUTO && nextLine.paragraph.computedFormat.justificationRule == JustificationRule.SPACE); 
				
				if (isLastLine)
				{
					// There is no line after this one, or there is one which uses leading UP, so leading DOWN does not apply
					
					if (!isFirstLine)
					{
						// "Space above None, align bottom" (eqivalently, "Space below None, align top"): 
						// 1) Only the text area should be selected
						rectHeight = textHeight;
						
						// 2) Selection rectangle's bottom aligned with line descent; verticalAdj remains 0
					}
					else
					{
						// "Space above, align bottom" 
						// 1) Space above as dictated by first baseline offset 
						// 2) Selection rectangle must at least include all of the text area
						rectHeight = Math.max(height, textHeight);
						// 3) Selection rectangle's bottom aligned with line descent; verticalAdj remains 0
					}
				}
				else
				{
					// There is a line after this one, so leading DOWN applies
					
					if (!isFirstLine)
					{
						// "Space below, align top" 
						// 1) Space below as dictated by line leading (obtained from 'height' member of next line) 
						// 2) Selection rectangle must at least include all of the text area
						rectHeight = Math.max(nextLine.height,textHeight);
						
						// 3) Selection rectangle's top to be aligned with line ascent, so its bottom to be at rectHeight - textLine.ascent,
						// not textLine.descent, set verticalAdj accordingly 
						verticalAdj = rectHeight - textHeight; // same as rectHeight - textLine.ascent - textLine.descent
					}
					else
					{
						// Union of 
						// 1) first line, leading up: In this case, rectangle height is the larger of line height and text height,
						// and the rectangle is shifted down by descent amount to align bottoms. So, top of rectangle is at:
						var top:Number = _descent - Math.max(height,textHeight);
						
						// 2) interior line, leading down: In this case, rectangle height is the larger of line leading and text height,
						// and the rectangle is shifted up by ascent amount to align tops. So, bottom of rectangle is at:
						var bottom:Number = Math.max(nextLine.height,textHeight) - _ascent;
						
						rectHeight = bottom - top;
						
						// 3) Selection rectangle's bottom to be at 'bottom', not the line's descent; set verticalAdj accordingly
						verticalAdj = bottom - _descent;
					}
				}
			}
			
			//If we don't have a line above us, then we need to pad the line a bit as well as make it shift up.
			//If we don't, then it overlaps the line below too much OR clips the top of the glyphs.
			if(!prevLine || prevLine.columnIndex != this.columnIndex || prevLine.controller != this.controller)
			{
				//make it taller - this is kinda a fudge, but we have no info to determine a good top.
				//if we don't do this, the selection rectangle will clip to the top of the glyphs and even
				//let parts stick out a bit.  So, re-add the descent and offset the rect by 50% so that
				//it appears to balance the top and bottom.
				rectHeight += this.descent;
				verticalAdj = Math.floor(this.descent/2);
			}
			return [rectHeight, verticalAdj];
		}
		
		/** @private 
		 * 
		 * 
		 */
		private function makeSelectionBlocks(textLine:TextLine, begIdx:int, endIdx:int, paraAbsStart:int, blockProgression:String, direction:String, heightAndAdj:Array):Array
		{
			CONFIG::debug{ assert(begIdx <= endIdx, "Selection indexes are reversed!  How can this happen?!"); }
			
			var blockArray:Array = new Array();
			var blockRect:Rectangle = new Rectangle();
			var startElem:FlowLeafElement = _para.findLeaf(begIdx);
			var startMetrics:Rectangle = startElem.getComputedFontMetrics().emBox;
			
			if (!textLine)
				textLine = getTextLine(true);
			
			//++makeBlockPassCounter;
			//trace(makeBlockPassCounter + ") direction = " + direction + " blockProgression = " + blockProgression);
			
			// CNW: removed whole line optimization 5/18/10 - was yielding different results than code below and was no faster
			
			//trace(makeBlockPassCounter + ") begIdx = " + begIdx.toString() + " endIdx = " +  endIdx.toString());
			var begAtomIndex:int = textLine.getAtomIndexAtCharIndex(begIdx); 
			var endAtomIndex:int = adjustEndElementForBidi(textLine, begIdx, endIdx, begAtomIndex, direction);
			
			//trace(makeBlockPassCounter + ") begAtomIndex = " + begAtomIndex.toString() + " endAtomIndex = " +  endAtomIndex.toString());
			CONFIG::debug{ assert(begAtomIndex >= 0, "Invalid start index! begIdx = " + begIdx)};
			CONFIG::debug{ assert(endAtomIndex >= 0, "Invalid end index! begIdx = " + endIdx)};
			
			if(direction == Direction.RTL && textLine.getAtomBidiLevel(endAtomIndex)%2 != 0)
			{
				//if we are in RTL, anchoring the LTR text gets tricky.  Because the endElement is before the first
				//element - which is why we're in this code - the result can be a zero-width rectangle if the span of LTR
				//text breaks across line boundaries.  If that is the case, then the endAtomIndex value will be 0.  As
				//this is the less common case, assume that it isn't and make all other cases come first
				if (endAtomIndex == 0 && begIdx < endIdx-1)
				{
					//since the endAtomIndex is 0, meaning that the LTR spans lines,
					//we want to grab the glyph before the endIdx which represents the last LTR glyph for the selection. 
					//Make a recursive call into makeSelectionBlocks using and endIdx decremented by 1.
					blockArray = makeSelectionBlocks(textLine, begIdx, endIdx - 1, paraAbsStart, blockProgression, direction, heightAndAdj);
					var bidiBlock:Array = makeSelectionBlocks(textLine, endIdx - 1, endIdx - 1, paraAbsStart, blockProgression, direction, heightAndAdj)
					var bidiBlockIter:int = 0;
					while(bidiBlockIter < bidiBlock.length)
					{
						blockArray.push(bidiBlock[bidiBlockIter]);
						++bidiBlockIter;
					}
					return blockArray;
				}
			}
			
			var begIsBidi:Boolean = begAtomIndex != -1 ? isAtomBidi(textLine, begAtomIndex, direction) : false;
			var endIsBidi:Boolean = endAtomIndex != -1 ? isAtomBidi(textLine, endAtomIndex, direction) : false;
			
			//trace("begAtomIndex is bidi = " + begIsBidi.toString());
			//trace("endAtomIndex is bidi = " + endIsBidi.toString());	
			
			if(begIsBidi || endIsBidi)
			{	
				//this code needs to iterate over the glyphs starting at the begAtomIndex and going forward.
				//It doesn't matter is beg is bidi or not, we need to find a boundary, create a rect on it, then proceded.
				//use the value of begIsBidi for testing the consistency of the selection.
				
				//Example bidi text.  Note that the period comes at the left end of the line:
				//
				//	Bidi state:		f f t t t t t	(true/false)
				//	Element Index:0 1 2 3 4 5 6		(0 is the para terminator)
				//	Chars:			. t o _ b e
				//  Flow Index:	   6 0 1 2 3 4 (5) 	Note that these numbers represent the space between glyphs AND
				//					 5(f)			that index 5 is both the space after the e and before the period.
				//									but, the position 5 is not a valid cursor location.
				
				//the original code I implemented used the beg and endElement indexes however that fails because when the text
				//is mixed bidi/non-bidi, the indexes are only 1 char apart. This resulted in, for example, only the period in 
				//a line getting selected when the text was bidi.   Instead, we're going to use the begIdx and endIdx and 
				//recalculate the element indexes each time.  This is expensive, but I don't see an alternative. - gak 09.05.08
				var curIdx:int = begIdx;
				var incrementor:int = (begIdx != endIdx ? 1 : 0);
				
				//the indexes used to draw the seleciton.  activeStart/End represent the
				//beginning of the selection shape atoms, while cur is the one we are testing.
				var activeStartIndex:int = begAtomIndex;
				var activeEndIndex:int = begAtomIndex;
				var curElementIndex:int = begAtomIndex;
				
				//when activeEndIsBidi no longer matches the bidi setting for the activeStartIndex, we will create the shape
				var activeEndIsBidi:Boolean = begIsBidi;
				
				do
				{
					//increment the index
					curIdx += incrementor;
					//get the next atom index
					curElementIndex = textLine.getAtomIndexAtCharIndex(curIdx);
					
					//calculate the bidi level for the - kinda cludgy, but if the bidi-text wraps, curElementIndex == -1
					//so just set it to false if this is the case.  It will get ignored in the subsequent check and curIdx
					//will == endIdx as this is the last glyph in the line - which is why the next is -1 - gak 09.12.08
					var curIsBidi:Boolean = (curElementIndex != -1) ? isAtomBidi(textLine, curElementIndex, direction) : false;
					
					if(curElementIndex != -1 && curIsBidi != activeEndIsBidi)
					{
						blockRect = makeBlock(textLine, curIdx, activeStartIndex, activeEndIndex, startMetrics, blockProgression, direction, heightAndAdj);
						blockArray.push(blockRect);
						
						//shift the activeStart/End indexes to the current
						activeStartIndex = curElementIndex;
						activeEndIndex = curElementIndex;
						//update the bidi setting
						activeEndIsBidi = curIsBidi;
					}
					else
					{
						//we don't get another chance to make a block, so if this is the last char, make the block before we bail out.
						//we have to check both equality and equality plus the incrementor because if we don't, then we'll miss a
						//character in the selection.
						if(curIdx == endIdx)
						{
							blockRect = makeBlock(textLine, curIdx, activeStartIndex, activeEndIndex, startMetrics, blockProgression, direction, heightAndAdj);
							blockArray.push(blockRect);
						}
						
						activeEndIndex = curElementIndex;
					}
				}while(curIdx < endIdx)
				
			}
			else
			{
				var testILG:InlineGraphicElement = startElem as InlineGraphicElement;
				if(!testILG || testILG.effectiveFloat == Float.NONE || begIdx == endIdx )
				{
					blockRect = makeBlock(textLine, begIdx, begAtomIndex, endAtomIndex, startMetrics, blockProgression, direction, heightAndAdj);
					if (testILG && testILG.elementWidthWithMarginsAndPadding() != testILG.elementWidth)
					{	// Don't include margins or padding around inlines in the bounds
						var verticalText:Boolean = testILG.getTextFlow().computedFormat.blockProgression == BlockProgression.RL;
						var ilgFormat:ITextLayoutFormat = testILG.computedFormat;
						if (verticalText)
						{
							var paddingTop:Number = testILG.getEffectivePaddingTop();
							blockRect.top += paddingTop;	// don't include the left side indent in the selected area
							var paddingBottom:Number = testILG.getEffectivePaddingBottom();
							blockRect.bottom -= paddingBottom;
						}
						else
						{
							var paddingLeft:Number = testILG.getEffectivePaddingLeft();
							blockRect.left += paddingLeft;	// don't include the left side indent in the selected area
							var paddingRight:Number = testILG.getEffectivePaddingRight();
							blockRect.right -= paddingRight;
						}
					}
				}
				else
				{
					blockRect = testILG.graphic.getBounds(textLine);
				}
				
				blockArray.push(blockRect);
			}
			
			return blockArray;
		}	
		
		/** @private 
		 * 
		 * 
		 */
		private function makeBlock(textLine:TextLine, begTextIndex:int, begAtomIndex:int, endAtomIndex:int, startMetrics:Rectangle, blockProgression:String, direction:String, heightAndAdj:Array):Rectangle
		{
			var blockRect:Rectangle = new Rectangle();
			var globalStart:Point = new Point(0,0);
			var heightAndAdj:Array;
			
			if(begAtomIndex > endAtomIndex)
			{
				// swap the start and end
				var tempEndIdx:int = endAtomIndex;
				endAtomIndex = begAtomIndex;
				begAtomIndex = tempEndIdx;
			}
			if (!textLine)
				textLine = getTextLine(true);
			
			//now that we have elements and they are in the right order for drawing, get their rectangles
			var begCharRect:Rectangle = textLine.getAtomBounds(begAtomIndex);
			//trace(makeBlockPassCounter + ") begCharRect = " + begCharRect.toString());
			
			var endCharRect:Rectangle = textLine.getAtomBounds(endAtomIndex);
			//trace(makeBlockPassCounter + ") endCharRect = " + endCharRect.toString());
			
			//Calculate the justificationRule value
			var justRule:String = _para.getEffectiveJustificationRule();
			//If this is TTB text and NOT TCY, as indicated by TextRotation.rotate0...
			if(blockProgression == BlockProgression.RL && textLine.getAtomTextRotation(begAtomIndex) != TextRotation.ROTATE_0)
			{
				globalStart.y = begCharRect.y;
				blockRect.height = begAtomIndex != endAtomIndex ? endCharRect.bottom - begCharRect.top : begCharRect.height;
				
				//re-ordered this code.  EAST_ASIAN is more common in vertical and should be the first option.
				if(justRule == JustificationRule.EAST_ASIAN)
				{
					blockRect.width = begCharRect.width;
				}
				else
				{
					blockRect.width = heightAndAdj[0];
					globalStart.x -= heightAndAdj[1];
				}
			}
			else
			{
				//given bidi text alternations, the endCharRect could be left of the begCharRect,
				//use whichever is farther left.
				globalStart.x = Math.min(begCharRect.x,endCharRect.x);
				//if we're here and the BlockProgression is TTB, then we're TCY.  Less frequent case, so make non-TCY
				//the first option...
				//NB - Never use baseline adjustments for TCY.  They don't make sense here.(I think) - gak 06.03.08
				if(blockProgression == BlockProgression.RL)
					globalStart.y = begCharRect.y + (startMetrics.width /2); // TODO-9/5/8:Behavior for leading down TBD
				
				
				if(justRule != JustificationRule.EAST_ASIAN)
				{
					blockRect.height = heightAndAdj[0];
					if(blockProgression == BlockProgression.RL)
						globalStart.x -= heightAndAdj[1];
					else 
						globalStart.y += heightAndAdj[1];
					//changed the width from a default of 2 to use the begCharRect.width so that point seletion
					//can choose to use the right or left side of the glyph when drawing a caret Watson 1876415/1876953- gak 08.19.09
					blockRect.width = begAtomIndex != endAtomIndex ? Math.abs(endCharRect.right - begCharRect.left) : begCharRect.width;
				}
				else
				{
					blockRect.height =  begCharRect.height;
					
					//changed the width from a default of 2 to use the begCharRect.width so that point seletion
					//can choose to use the right or left side of the glyph when drawing a caret Watson 1876415/1876953- gak 08.19.09
					blockRect.width = begAtomIndex != endAtomIndex ? Math.abs(endCharRect.right - begCharRect.left) : begCharRect.width;
				}
			}
			
			blockRect.x = globalStart.x;
			blockRect.y = globalStart.y;
			if(blockProgression == BlockProgression.RL)
			{
				if(textLine.getAtomTextRotation(begAtomIndex) != TextRotation.ROTATE_0)
					blockRect.x -= textLine.descent;
				else //it's TCY
					blockRect.y -= (blockRect.height / 2)
			}
			else
			{
				blockRect.y += (textLine.descent - blockRect.height);
			}
			
			var tfl:TextFlowLine = textLine.userData as TextFlowLine;
			var curElem:FlowLeafElement = _para.findLeaf(begTextIndex);
			var rotation:String;
			if (!curElem)
			{
				if (begTextIndex < 0)
					curElem = _para.getFirstLeaf();
				else if (begTextIndex >= _para.textLength)
					curElem = _para.getLastLeaf();
				rotation = curElem ? curElem.computedFormat.textRotation : TextRotation.ROTATE_0;
			}  
			else
				rotation = curElem.computedFormat.textRotation;
			
			//handle rotation.  For horizontal text, rotations of 90 or 180 cause the text
			//to draw under the baseline in a cosistent location.  Vertical text is a bit more complicated
			//in that a 90 rotation puts it immediately to the left of the Em Box, whereas 180 is one quarter
			//of the way in the Em Box. Fix for Watson 1915930 - gak 02.17.09
			if(rotation == TextRotation.ROTATE_180 || 
				rotation == TextRotation.ROTATE_90)
			{
				if(blockProgression != BlockProgression.RL)
					blockRect.y += (blockRect.height / 2);
				else
				{
					if(curElem.getParentByType(TCYElement) == null)
					{	
						if(rotation == TextRotation.ROTATE_90)
							blockRect.x -= blockRect.width;
						else
							blockRect.x -= (blockRect.width * .75);
					}
					else
					{
						if(rotation == TextRotation.ROTATE_90)
							blockRect.y += blockRect.height;
						else
							blockRect.y += (blockRect.height * .75);
					}
				}
			}
			
			
			return blockRect;
		}
		
		/** @private
		 * 
		 * 
		 */
		tlf_internal function convertLineRectToContainer(rect:Rectangle, constrainShape:Boolean):void
		{
			var textLine:TextLine = getTextLine();
			
			/* var globalStart:Point = new Point(rect.x, rect.y);
			
			//convert to controller coordinates...
			////trace(makeBlockPassCounter + ") globalStart = " + globalStart.toString());
			globalStart = textLine.localToGlobal(globalStart);
			////trace(makeBlockPassCounter + ") localToGlobal.globalStart = " + globalStart.toString());
			globalStart = container.globalToLocal(globalStart);
			////trace(makeBlockPassCounter + ") globalToLocal.globalStart = " + globalStart.toString());
			rect.x = globalStart.x;
			rect.y = globalStart.y; */
			
			// this is much simpler and actually more accurate - localToGlobal/globalToLocal does some rounding
			rect.x += textLine.x;
			rect.y += textLine.y;
			
			if (constrainShape)
			{
				var tf:TextFlow = _para.getTextFlow();
				var columnRect:Rectangle = controller.columnState.getColumnAt(this.columnIndex);
				constrainRectToColumn(tf,rect,columnRect,controller.horizontalScrollPosition,controller.verticalScrollPosition,controller.compositionWidth,controller.compositionHeight);
			}
		}
		
		/** @private */
		static tlf_internal function constrainRectToColumn(tf:TextFlow,rect:Rectangle,columnRect:Rectangle,hScrollPos:Number,vScrollPos:Number,compositionWidth:Number,compositionHeight:Number):void
		{		
			if(tf.computedFormat.lineBreak == LineBreak.EXPLICIT)
				return;
			
			var bp:String = tf.computedFormat.blockProgression;
			var direction:String = tf.computedFormat.direction;
			
			if(bp == BlockProgression.TB && !isNaN(compositionWidth))
			{	
				if(direction == Direction.LTR)
				{
					//make sure is doesn't go past the end of the container
					if(rect.left > (columnRect.x + columnRect.width + hScrollPos))
						rect.left = (columnRect.x + columnRect.width + hScrollPos);
					
					//make sure that if this is a selection and not a point selection, that 
					//we don't go beyond the end of the container...
					if(rect.right > (columnRect.x + columnRect.width + hScrollPos))
						rect.right = (columnRect.x + columnRect.width + hScrollPos);
				}
				else
				{
					if(rect.right < (columnRect.x + hScrollPos))
						rect.right = (columnRect.x + hScrollPos);
					
					if(rect.left < (columnRect.x + hScrollPos))
						rect.left = (columnRect.x + hScrollPos);
				}
			}
			else if (bp == BlockProgression.RL && !isNaN(compositionHeight))
			{
				if(direction == Direction.LTR)
				{
					//make sure is doesn't go past the end of the container
					if(rect.top > (columnRect.y + columnRect.height + vScrollPos))
						rect.top = (columnRect.y + columnRect.height + vScrollPos);
					
					//make sure that if this is a selection and not a point selection, that 
					//we don't go beyond the end of the container...
					if(rect.bottom > (columnRect.y + columnRect.height + vScrollPos))
						rect.bottom = (columnRect.y + columnRect.height + vScrollPos);
				}
				else
				{
					if(rect.bottom < (columnRect.y + vScrollPos))
						rect.bottom = (columnRect.y + vScrollPos);
					
					if(rect.top < (columnRect.y + vScrollPos))
						rect.top = (columnRect.y + vScrollPos);
				}
			}
		}
		
		/** @private
		 * Helper method to hilight the portion of a block selection on this TextLine.  A selection display is created and added to the line's TextFrame with ContainerController addSelectionShape.
		 * @param begIdx absolute index of start of selection on this line.
		 * @param endIdx absolute index of end of selection on this line.
		 */
		tlf_internal function hiliteBlockSelection(selObj:Shape, selFormat:SelectionFormat, container:DisplayObject, begIdx:int,endIdx:int, prevLine:TextFlowLine, nextLine:TextFlowLine):void
		{
			// no container for overflow lines, or lines scrolled out 
			if (isDamaged() || !_controller)
				return;
			
			var textLine:TextLine = peekTextLine();
			if (!textLine || !textLine.parent)
				return;
			
			var paraStart:int = _para.getAbsoluteStart();
			begIdx -= paraStart;
			endIdx -= paraStart;
			
			createSelectionShapes(selObj, selFormat, container, begIdx, endIdx, prevLine, nextLine);
		} 
		
		/** @private
		 * Helper method to hilight a point selection on this TextLine.  x,y,w,h of the selection are calculated and ContainerController.drawPointSelection is called 
		 * @param idx absolute index of the point selection.
		 */
		tlf_internal function hilitePointSelection(selFormat:SelectionFormat, idx:int, container:DisplayObject, prevLine:TextFlowLine, nextLine:TextFlowLine):void
		{
			var rect:Rectangle = computePointSelectionRectangle(idx,container,prevLine,nextLine, true);
			if (rect)
				_controller.drawPointSelection(selFormat, rect.x, rect.y, rect.width, rect.height)
		}
		
		static private function setRectangleValues(rect:Rectangle,x:Number,y:Number,width:Number,height:Number):void
		{
			rect.x = x;
			rect.y = y;
			rect.width = width;
			rect.height = height;
		}
		
		static private const localZeroPoint:Point = new Point(0,0);
		static private const localOnePoint:Point = new Point(1,0);
		static private const rlLocalOnePoint:Point = new Point(0,1);
		
		/** @private */		
		tlf_internal function computePointSelectionRectangle(idx:int, container:DisplayObject, prevLine:TextFlowLine, nextLine:TextFlowLine, constrainSelRect:Boolean):Rectangle
		{
			if (isDamaged() || !_controller)
				return null;
			
			// no container for overflow lines, or lines scrolled out 
			var textLine:TextLine = peekTextLine();
			if (!textLine || !textLine.parent)
				return null;			
			// adjust to this paragraph's TextBlock
			idx -= _para.getAbsoluteStart();
			
			textLine = getTextLine(true);
			
			//endIdx will only differ if idx is altered when detecting TCY bounds
			var endIdx:int = idx;
			var elementIndex:int = textLine.getAtomIndexAtCharIndex(idx);
			CONFIG::debug{ assert(elementIndex != -1, "Invalid point selection index! idx = " + idx); }
			
			var isTCYBounds:Boolean = false;
			var paraLeadingTCY:Boolean = false;
			
			var contElement:ContainerFormattedElement = _para.getAncestorWithContainer();
			CONFIG::debug { assert(contElement != null,"para with no container"); }
			var blockProgression:String = contElement.computedFormat.blockProgression;
			var direction:String = _para.computedFormat.direction;
			
			//need to check for TCY.  TCY cannot take input into it's head, but can in it's tail.
			if(blockProgression == BlockProgression.RL)
			{
				if (idx == 0)
				{ 
					if(textLine.getAtomTextRotation(0) == TextRotation.ROTATE_0)
						paraLeadingTCY = true;
				}
				else
				{
					var prevElementIndex:int = textLine.getAtomIndexAtCharIndex(idx - 1);
					if(prevElementIndex != -1)
					{
						//if this elem is TCY, then we need to back up one space and use the right bounds
						if(textLine.getAtomTextRotation(elementIndex) == TextRotation.ROTATE_0 && 
							textLine.getAtomTextRotation(prevElementIndex) != TextRotation.ROTATE_0)
						{
							elementIndex = prevElementIndex;
							--idx;
							isTCYBounds = true;
						}
						else if(textLine.getAtomTextRotation(prevElementIndex) == TextRotation.ROTATE_0)
						{
							elementIndex = prevElementIndex;
							--idx;
							isTCYBounds = true;
						}
					}
				}
			}
			
			var heightAndAdj:Array = getRomanSelectionHeightAndVerticalAdjustment(prevLine, nextLine);
			var blockRectArray:Array = makeSelectionBlocks(textLine, idx, endIdx, _para.getAbsoluteStart(), blockProgression, direction, heightAndAdj);
			CONFIG::debug{ assert(blockRectArray.length == 1, "A point selection should return a single selection rectangle!"); }
			var rect:Rectangle = blockRectArray[0];
			
			convertLineRectToContainer(rect, constrainSelRect);
			
			var drawOnRight:Boolean = (direction == Direction.RTL);
			
			if((drawOnRight && textLine.getAtomBidiLevel(elementIndex) % 2 == 0) || 
				(!drawOnRight && textLine.getAtomBidiLevel(elementIndex) % 2 != 0))
			{
				drawOnRight = !drawOnRight;
			}
			
			// compute a width so that cursor is visually one pixel wide independent of scaling
			var zeroPoint:Point = container.localToGlobal(localZeroPoint);
			var cursorWidth:Number;
			
			if(blockProgression == BlockProgression.RL && textLine.getAtomTextRotation(elementIndex) != TextRotation.ROTATE_0)
			{
				var rlOnePoint:Point = container.localToGlobal(rlLocalOnePoint);
				cursorWidth = zeroPoint.y - rlOnePoint.y;
				cursorWidth = cursorWidth == 0 ? 1 : Math.abs(1.0/cursorWidth);
				// trace(zeroPoint,onePoint,cursorSize);
				if(!drawOnRight)
					setRectangleValues(rect, rect.x, !isTCYBounds ? rect.y : rect.y + rect.height,rect.width,cursorWidth);
				else
					setRectangleValues(rect, rect.x, !isTCYBounds ? rect.y + rect.height : rect.y ,rect.width,cursorWidth);
			}
			else
			{
				var onePoint:Point = container.localToGlobal(localOnePoint);
				cursorWidth = zeroPoint.x - onePoint.x;
				cursorWidth = cursorWidth == 0 ? 1 : Math.abs(1.0/cursorWidth);
				// trace(zeroPoint,onePoint,cursorSize);
				//choose to use the right or left side of the glyph based on Direction when drawing a caret Watson 1876415/1876953
				//if the direction is ltr, then the cursor should be on the left side
				if(!drawOnRight)
					setRectangleValues(rect, !isTCYBounds ? rect.x : rect.x + rect.width, rect.y, cursorWidth, rect.height);
				else //otherwise, it should be on the right, unless it is TCY
					setRectangleValues(rect, !isTCYBounds ? rect.x + rect.width : rect.x, rect.y, cursorWidth, rect.height);
			}
			
			//allow the atoms to be garbage collected.
			textLine.flushAtomData();
			
			return rect;
		}
		
		/** @private
		 * Three states.  Disjoint(0), Intersects(1), HeightContainedIn(2),  
		 */
		
		tlf_internal function selectionWillIntersectScrollRect(scrollRect:Rectangle, begIdx:int, endIdx:int, prevLine:TextFlowLine, nextLine:TextFlowLine):int
		{
			var contElement:ContainerFormattedElement = _para.getAncestorWithContainer();
			CONFIG::debug { assert(contElement != null,"para with no container"); }
			var blockProgression:String = contElement.computedFormat.blockProgression;
			var textLine:TextLine = getTextLine(true);
			
			if (begIdx == endIdx)
			{
				var pointSelRect:Rectangle = computePointSelectionRectangle(begIdx, DisplayObject(controller.container), prevLine, nextLine, false);
				if (pointSelRect)
				{
					if (scrollRect.containsRect(pointSelRect))
						return 2;
					if (scrollRect.intersects(pointSelRect))
						return 1;
				}
			}
			else
			{
				var paraStart:int = _para.getAbsoluteStart();
				var selCache:SelectionCache = this.getSelectionShapesCacheEntry(begIdx-paraStart,endIdx-paraStart,prevLine,nextLine,blockProgression);
				if (selCache)
				{
					//iterate the blocks and check for intersections
					var drawRect:Rectangle;
					for each (drawRect in selCache.selectionBlocks)
					{
						drawRect = drawRect.clone();
						// convertLineRectToContainer(container, drawRect);
						drawRect.x += textLine.x; 
						drawRect.y += textLine.y; 
						if (scrollRect.intersects(drawRect))
						{
							if(blockProgression == BlockProgression.RL)
							{
								// see if width is entirely contained in scrollRect
								if (drawRect.left >= scrollRect.left && drawRect.right <= scrollRect.right)
									return 2;
							}
							else
							{
								if (drawRect.top >= scrollRect.top && drawRect.bottom <= scrollRect.bottom)
									return 2;
							}
							return 1;
						}
					}
				}
			}
			return 0;
		}
		
		
		/** @private */
		CONFIG::debug private static function dumpAttribute(result:String, attributeName:String, attributeValue:Object):String
		{
			if (attributeValue)
			{
				result += " ";
				result += attributeName;
				result += "=\"";
				result += attributeValue.toString();
				result += "\""	
			}
			return result;		
		}
		
		/** @private
		 */
		private function normalizeRects(srcRects:Array, dstRects:Array, largestRise:Number, blockProgression:String, direction:String):void
		{
			//the last rectangle in the list with a potential to merge
			var lastRect:Rectangle = null;
			var rectIter:int = 0;
			while(rectIter < srcRects.length)
			{
				//get the current rect and advance the iterator
				var rect:Rectangle = srcRects[rectIter++];
				
				//apply a new height if needed.
				if(blockProgression == BlockProgression.RL)
				{
					if(rect.width < largestRise)
					{
						rect.width = largestRise;
					}
				}
				else
				{
					if(rect.height < largestRise)
					{
						rect.height = largestRise;
					}
				}
				//if the lastRect is null, no need to perform calculation
				if(lastRect == null)
				{
					lastRect = rect;
				}
				else
				{
					//TCY has already been excluded, so no need to worry about it here...
					if(blockProgression == BlockProgression.RL)
					{
						//trace(normalCounter + ") lastRect = " + lastRect.toString());
						//trace(normalCounter + ") rect = " + rect.toString());
						
						//merge it in to the last rect
						if(lastRect.y < rect.y && (lastRect.y + lastRect.height) >= rect.top && lastRect.x == rect.x)
						{
							lastRect.height += rect.height;
						}
						else if(rect.y < lastRect.y && lastRect.y <= rect.bottom && lastRect.x == rect.x)
						{
							lastRect.height += rect.height;
							lastRect.y = rect.y;
						}
						else
						{
							//we have a break in the rectangles and should push last rect onto the draw list before continuing
							dstRects.push(lastRect);
							lastRect = rect;
						}
					}
					else
					{
						if(lastRect.x < rect.x && (lastRect.x + lastRect.width) >= rect.left && lastRect.y == rect.y)
						{
							lastRect.width += rect.width;
						}
						else if(rect.x < lastRect.x && lastRect.x <= rect.right && lastRect.y == rect.y)
						{
							lastRect.width += rect.width;
							lastRect.x = rect.x;
						}
						else
						{
							//we have a break in the rectangles and should push last rect onto the draw list before continuing
							dstRects.push(lastRect);
							lastRect = rect;
						}
					}
				}
				//if this is the last rectangle, we haven't added it, do so now.
				if(rectIter == srcRects.length)
					dstRects.push(lastRect);
			}
		}
		
		/** @private */
		private function adjustEndElementForBidi(textLine:TextLine, begIdx:int, endIdx:int, begAtomIndex:int, direction:String):int
		{
			var endAtomIndex:int = begAtomIndex;
						
			if(endIdx != begIdx)
			{
				if(((direction == Direction.LTR && textLine.getAtomBidiLevel(begAtomIndex)%2 != 0)
					|| (direction == Direction.RTL && textLine.getAtomBidiLevel(begAtomIndex)%2 == 0))
					&& textLine.getAtomTextRotation(begAtomIndex) != TextRotation.ROTATE_0)
					endAtomIndex = textLine.getAtomIndexAtCharIndex(endIdx);
				else
				{
					endAtomIndex = textLine.getAtomIndexAtCharIndex(endIdx - 1);
				}
			}
			
			if(endAtomIndex == -1 && endIdx > 0)
			{
				return adjustEndElementForBidi(textLine, begIdx, endIdx - 1, begAtomIndex, direction);
			}
			return endAtomIndex;
		}
		
		/** @private */
		private function isAtomBidi(textLine:TextLine, elementIdx:int, direction:String):Boolean
		{
			if (!textLine)
				textLine = getTextLine(true);
			
			return (textLine.getAtomBidiLevel(elementIdx)%2 != 0 && direction == Direction.LTR) || (textLine.getAtomBidiLevel(elementIdx)%2 == 0 && direction == Direction.RTL);
		}
		
		/** @private */
		tlf_internal function get adornCount():int 
		{ return _adornCount; }
		
		/** @private */
		static tlf_internal function findNumberLine(textLine:TextLine):TextLine
		{
			// not always going to be a numberLine - listStyleType may be "none"
			// have to hunt for it because inlinegraphics get pushed at the beginning
			// risk here is that clients decorate TextLines with other TextLines.
			for (var idx:int = 0; idx < textLine.numChildren; idx++)
			{
				var numberLine:TextLine = textLine.getChildAt(idx) as TextLine;
				if (numberLine && (numberLine.userData is NumberLineUserData))
					break;
			}
			return numberLine;
		}
		
		/** @private */
		static tlf_internal function getNumberLineListStylePosition(numberLine:TextLine):String
		{ return (numberLine.userData as NumberLineUserData).listStylePosition; }
		/** @private */
		static tlf_internal function getNumberLineInsideLineWidth(numberLine:TextLine):Number
		{ return (numberLine.userData as NumberLineUserData).insideLineWidth; }
		/** @private */
		static tlf_internal function getNumberLineSpanFormat(numberLine:TextLine):ITextLayoutFormat
		{ return (numberLine.userData as NumberLineUserData).spanFormat; }
		/** @private */
		static tlf_internal function getNumberLineParagraphDirection(numberLine:TextLine):String
		{ return (numberLine.userData as NumberLineUserData).paragraphDirection; }
		/** @private */		
		static tlf_internal function setListEndIndent(numberLine:TextLine,listEndIndent:Number):void
		{ (numberLine.userData as NumberLineUserData).listEndIndent = listEndIndent; }
		/** @private */		
		static tlf_internal function getListEndIndent(numberLine:TextLine):Number
		{ return (numberLine.userData as NumberLineUserData).listEndIndent; }
		
		/** @private */		
		static tlf_internal function setNumberLineBackground(numberLine:TextLine,background:BackgroundManager):void
		{ (numberLine.userData as NumberLineUserData).backgroundManager = background; }
		/** @private */		
		static tlf_internal function getNumberLineBackground(numberLine:TextLine):BackgroundManager
		{ return (numberLine.userData as NumberLineUserData).backgroundManager; }


		/** @private */
		CONFIG::debug public function dumpToXML():String
		{
			var result:String = new String("<line");
			
			result = dumpAttribute(result, "absoluteStart", absoluteStart);
			result = dumpAttribute(result, "textLength", textLength);
			result = dumpAttribute(result, "height", height);
			result = dumpAttribute(result, "spaceBefore", spaceBefore);
			
			result = dumpAttribute(result, "spaceAfter", spaceAfter);
			result = dumpAttribute(result, "location", location);
			result = dumpAttribute(result, "x", x);
			result = dumpAttribute(result, "y", y);
			result = dumpAttribute(result, "targetWidth", targetWidth);
			result = dumpAttribute(result, "lineOffset", _lineOffset);
			result += ">\n";
			
			
			var textLine:TextLine = getTextLine(true);
			
			result += "<TextBlock>";
			result += textLine.textBlock.dump(); 
			result += "</TextBlock>";
			result += "<TextLine>"
			result += textLine.dump();
			result += "</TextLine>"
			
			result += "</line>";
			return result;
		}
		
	};
}

import flash.display.Shape;
import flash.geom.Rectangle;

import flashx.textLayout.edit.ISelectionManager;
import flashx.textLayout.formats.ITextLayoutFormat;

/** @private - I would have defined this as tlf_internal, but that is not an option, so
 * making it private.
 * 
 * The SelectionCache is a structure designed to hold a few key data points needed to quickly
 * reconstruct a selection on a line:
 * 
 * a) the beginning and end indicies of the selection on the line
 * b) the regular selection rectangles
 * c) the irregular selection rectangles, such as TCY selection rects in vertical text
 * 
 **/
final class SelectionCache
{
	private var _begIdx:int = -1;
	private var _endIdx:int = -1;
	
	private var _selectionBlocks:Array = null;
	
	public function SelectionCache()
	{
	}
	
	public function get begIdx():int 
	{ return _begIdx; }
	public function set begIdx(val:int):void	
	{ _begIdx = val; }
	
	public function get endIdx():int 
	{ return _endIdx; }
	public function set endIdx(val:int):void	
	{ _endIdx = val; }
	
	public function pushSelectionBlock(drawRect:Rectangle):void 
	{
		if(!_selectionBlocks)
			_selectionBlocks = new Array();
		
		_selectionBlocks.push(drawRect.clone());
	}
	
	public function get selectionBlocks():Array 
	{ return _selectionBlocks; }
	
	
	public function clear():void
	{
		_selectionBlocks = null;
		_begIdx = -1;
		_endIdx = -1;
	}
	
}

import flashx.textLayout.elements.BackgroundManager;

class NumberLineUserData
{
	public function NumberLineUserData(listStylePosition:String,insideLineWidth:Number,spanFormat:ITextLayoutFormat,paraDirection:String)
	{
		this.listStylePosition = listStylePosition;
		this.insideLineWidth = insideLineWidth;
		this.spanFormat = spanFormat;
		this.paragraphDirection = paraDirection;
	}
	public var listStylePosition:String;
	public var insideLineWidth:Number;
	public var spanFormat:ITextLayoutFormat;
	public var paragraphDirection:String;
	public var listEndIndent:Number;
	public var backgroundManager:BackgroundManager;
}

import flash.text.engine.TextBlock;
import flash.text.engine.TextLine;
import flash.text.engine.TextLineValidity;

import flashx.textLayout.debug.Debugging;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.factory.StringTextLineFactory;
import flashx.textLayout.formats.BlockProgression;
import flashx.textLayout.tlf_internal;
import flashx.textLayout.formats.TextLayoutFormat;

use namespace tlf_internal;

class NumberLineFactory extends StringTextLineFactory
{
	private var _listStylePosition:String;
	private var _markerFormat:ITextLayoutFormat;
	private var _backgroundManager:BackgroundManager;
	
	public function get listStylePosition():String
	{ return _listStylePosition; }
	public function set listStylePosition(val:String):void
	{ _listStylePosition = val; }
	
	public function get markerFormat():ITextLayoutFormat
	{ return _markerFormat; }
	public function set markerFormat(val:ITextLayoutFormat):void
	{
		_markerFormat = val;
		spanFormat = val;
	}
	
	public function get backgroundManager():BackgroundManager
	{ return _backgroundManager; }
	
	public function clearBackgroundManager():void
	{ _backgroundManager = null; }
	
	/** @private */
	static tlf_internal function calculateInsideNumberLineWidth(numberLine:TextLine,bp:String):Number
	{
		var minVal:Number = Number.MAX_VALUE;
		var maxVal:Number = Number.MIN_VALUE;
		var idx:int = 0;
		var rect:Rectangle;
		
		if (bp == BlockProgression.TB)
		{
			for (; idx < numberLine.atomCount; idx++)
			{
				//trace(idx,numberLine.getAtomTextBlockBeginIndex(idx),numberLine.getAtomBounds(idx));
				if (numberLine.getAtomTextBlockBeginIndex(idx) != numberLine.rawTextLength-1)
				{
					rect = numberLine.getAtomBounds(idx);
					minVal = Math.min(minVal,rect.x);
					maxVal = Math.max(maxVal,rect.right);
				}
			}
		}
		else
		{
			for (; idx < numberLine.atomCount; idx++)
			{
				//trace(idx,numberLine.getAtomTextBlockBeginIndex(idx),numberLine.getAtomBounds(idx));
				if (numberLine.getAtomTextBlockBeginIndex(idx) != numberLine.rawTextLength-1)
				{
					rect = numberLine.getAtomBounds(idx);
					minVal = Math.min(minVal,rect.top);
					maxVal = Math.max(maxVal,rect.bottom);
				}
			}
		}
		numberLine.flushAtomData();
		//trace("textWidth",numberLine.textWidth,maxVal-minVal);
		return maxVal > minVal ? maxVal-minVal : 0;
	}
	
	protected override function callbackWithTextLines(callback:Function,delx:Number,dely:Number):void
	{
		for each (var textLine:TextLine in _factoryComposer._lines)
		{
			textLine.userData = new NumberLineUserData(listStylePosition,calculateInsideNumberLineWidth(textLine,textFlowFormat.blockProgression),_markerFormat,paragraphFormat.direction);
			var textBlock:TextBlock = textLine.textBlock;
			if (textBlock)
			{
				CONFIG::debug { Debugging.traceFTECall(null,textBlock,"releaseLines",textBlock.firstLine, textBlock.lastLine); }	
				textBlock.releaseLines(textBlock.firstLine,textBlock.lastLine);
			}
			textLine.x += delx;
			textLine.y += dely;
			textLine.validity = TextLineValidity.STATIC;
			CONFIG::debug { Debugging.traceFTEAssign(textLine,"validity",TextLineValidity.STATIC); }
			callback(textLine);
		}
	}
	
	// save the backgroundManager for later use.  generate the background when the TextLine is placed
	tlf_internal override function processBackgroundColors(textFlow:TextFlow,callback:Function,x:Number,y:Number,constrainWidth:Number,constrainHeight:Number):*
	{
		_backgroundManager = textFlow.backgroundManager;
		textFlow.clearBackgroundManager();
		return;
	}
}