////////////////////////////////////////////////////////////////////////////////
//
// 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.container
{
	import flash.display.BlendMode;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.InteractiveObject;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.ContextMenuEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.FocusEvent;
	import flash.events.IMEEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.TextEvent;
	import flash.geom.Rectangle;
	import flash.text.engine.TextBlock;
	import flash.text.engine.TextLine;
	import flash.text.engine.TextLineValidity;
	import flash.ui.ContextMenu;
	import flash.ui.ContextMenuClipboardItems;
	import flash.ui.Mouse;
	import flash.ui.MouseCursor;
	import flash.utils.Dictionary;
	
	import flashx.textLayout.compose.BaseCompose;
	import flashx.textLayout.compose.ISWFContext;
	import flashx.textLayout.compose.SimpleCompose;
	import flashx.textLayout.compose.StandardFlowComposer;
	import flashx.textLayout.compose.TextFlowLine;
	import flashx.textLayout.compose.TextLineRecycler;
	import flashx.textLayout.debug.Debugging;
	import flashx.textLayout.debug.assert;
	import flashx.textLayout.edit.EditManager;
	import flashx.textLayout.edit.EditingMode;
	import flashx.textLayout.edit.IEditManager;
	import flashx.textLayout.edit.IInteractionEventHandler;
	import flashx.textLayout.edit.ISelectionManager;
	import flashx.textLayout.edit.SelectionFormat;
	import flashx.textLayout.edit.SelectionManager;
	import flashx.textLayout.elements.Configuration;
	import flashx.textLayout.elements.FlowElement;
	import flashx.textLayout.elements.FlowLeafElement;
	import flashx.textLayout.elements.IConfiguration;
	import flashx.textLayout.elements.ParagraphElement;
	import flashx.textLayout.elements.SpanElement;
	import flashx.textLayout.elements.TextFlow;
	import flashx.textLayout.events.CompositionCompleteEvent;
	import flashx.textLayout.events.DamageEvent;
	import flashx.textLayout.events.FlowOperationEvent;
	import flashx.textLayout.events.SelectionEvent;
	import flashx.textLayout.events.StatusChangeEvent;
	import flashx.textLayout.events.TextLayoutEvent;
	import flashx.textLayout.events.UpdateCompleteEvent;
	import flashx.textLayout.factory.StringTextLineFactory;
	import flashx.textLayout.factory.TextFlowTextLineFactory;
	import flashx.textLayout.factory.TextLineFactoryBase;
	import flashx.textLayout.formats.BlockProgression;
	import flashx.textLayout.formats.FormatValue;
	import flashx.textLayout.formats.ITextLayoutFormat;
	import flashx.textLayout.formats.TextLayoutFormat;
	import flashx.textLayout.property.Property;
	import flashx.textLayout.tlf_internal;
	import flashx.undo.IUndoManager;
	import flashx.undo.UndoManager;

	use namespace tlf_internal;
	/**
	 *
	 *  @eventType flashx.textLayout.events.FlowOperationEvent.FLOW_OPERATION_BEGIN
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	 
	[Event(name="flowOperationBegin", type="flashx.textLayout.events.FlowOperationEvent")]
	
	/**
	 * 
	 * @eventType flashx.textLayout.events.FlowOperationEvent.FLOW_OPERATION_END
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	 
	[Event(name="flowOperationEnd", type="flashx.textLayout.events.FlowOperationEvent")]
	
	
	/**
	 * 
	 * @eventType flashx.textLayout.events.FlowOperationEvent.FLOW_OPERATION_COMPLETE
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="flowOperationComplete", type="flashx.textLayout.events.FlowOperationEvent")]
	
	/** Dispatched whenever the selection is changed.  Primarily used to update selection-dependent user interface. 
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="selectionChange", type="flashx.textLayout.events.SelectionEvent")]
	
	/** Dispatched after every recompose. 
	*
	* @playerversion Flash 10
	* @playerversion AIR 1.5
	* @langversion 3.0
	*/
	
	[Event(name="compositionComplete", type="flashx.textLayout.events.CompositionCompleteEvent")]
	
	/** Dispatched when the mouse is pressed down over any link. 
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="mouseDown", type="flashx.textLayout.events.FlowElementMouseEvent")]
	
	/** Dispatched when the mouse is released over any link. 
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="mouseUp", type="flashx.textLayout.events.FlowElementMouseEvent")]
	
	/** Dispatched when the mouse passes over any link. 
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="mouseMove", type="flashx.textLayout.events.FlowElementMouseEvent")]	
	
	/** Dispatched when the mouse first enters any link. 
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="rollOver", type="flashx.textLayout.events.FlowElementMouseEvent")]
	
	/** Dispatched when the mouse goes out of any link. 
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="rollOut", type="flashx.textLayout.events.FlowElementMouseEvent")]	
	
	/** Dispatched when any link is clicked. 
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="click", type="flashx.textLayout.events.FlowElementMouseEvent")]
	
	/** Dispatched when a InlineGraphicElement is resized due to having width or height as auto or percent 
	 * and the graphic has finished loading. 
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="inlineGraphicStatusChanged", type="flashx.textLayout.events.StatusChangeEvent")]
	
	/** Dispatched by a TextFlow object after text is scrolled within a controller container.  
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="scroll", type="flashx.textLayout.events.TextLayoutEvent")]
	
	/** Dispatched by a TextFlow object each time it is damaged 
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="damage", type="flashx.textLayout.events.DamageEvent")]

	/** Dispatched by a TextFlow object each time a container has had new DisplayObjects added or updated as a result of composition.
	 *
	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 */
	
	[Event(name="updateComplete", type="flashx.textLayout.events.UpdateCompleteEvent")]
		
	[Exclude(name="getBaseSWFContext",kind="method")]
	
	[Exclude(name="callInContext",kind="method")]
	/** Manages text in a container. Assumes that it manages all children of the container. 
	 * Consider using TextContainerManager for better performance in cases where there is a 
	 * one container per TextFlow, and the TextFlow is not the main focus, is static text, or
	 * is infrequently selected. Good for text in form fields, for example.
	 * 
	 * @includeExample examples\TextContainerManager.as -noswf
	 *
 	 * @playerversion Flash 10
	 * @playerversion AIR 1.5
	 * @langversion 3.0
	 * 
	 * @see ContainerController
	 */			
	public class TextContainerManager extends EventDispatcher implements ISWFContext, IInteractionEventHandler, ISandboxSupport
	{		
		// all events that are listened for need to be in this list.
		static private const eventList:Array = [ 
			FlowOperationEvent.FLOW_OPERATION_BEGIN,
			FlowOperationEvent.FLOW_OPERATION_END,
			FlowOperationEvent.FLOW_OPERATION_COMPLETE,
			SelectionEvent.SELECTION_CHANGE,
			CompositionCompleteEvent.COMPOSITION_COMPLETE,
			MouseEvent.CLICK,		//from FlowElementMouseEvent
			MouseEvent.MOUSE_DOWN,	//from FlowElementMouseEvent
			MouseEvent.MOUSE_OUT,	//from FlowElementMouseEvent
			MouseEvent.MOUSE_UP,	//from FlowElementMouseEvent
			MouseEvent.MOUSE_OVER,	//from FlowElementMouseEvent
			MouseEvent.MOUSE_OUT,	//from FlowElementMouseEvent
			StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE,
		 	TextLayoutEvent.SCROLL,
			DamageEvent.DAMAGE,
			UpdateCompleteEvent.UPDATE_COMPLETE
		];
		
		/** Configuration to be used by the TextContainerManager.  This can only be set once and before the inputmanager is used.  */
		static private var _defaultConfiguration:IConfiguration = null;
		
		/** The default configuration for this TextContainerManager. Column and padding attributes
		 * are set to <code>FormatValue.INHERIT</code>.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 * 
	 	 * @see flashx.textLayout.elements.IConfiguration IConfiguration
	 	 * @see flashx.textLayout.formats.FormatValue#INHERIT FormatValue.INHERIT
		 */
		static public function get defaultConfiguration():IConfiguration
		{
			if (_defaultConfiguration == null)
				_defaultConfiguration = customizeConfiguration(null);
			return _defaultConfiguration; 
		}
		
		/** @private Make a configuration acceptable to the TCM */
		static tlf_internal function customizeConfiguration(config:IConfiguration):IConfiguration
		{
			var newConfig:Configuration;
			if (config)
			{
				if (config.flowComposerClass == TextLineFactoryBase.getDefaultFlowComposerClass())
					return config;
				newConfig = (config as Configuration).clone();
			}
			else
				newConfig = new Configuration();
			newConfig.flowComposerClass = TextLineFactoryBase.getDefaultFlowComposerClass();
			return newConfig;
		}
		
		static private var _inputManagerTextFlowFactory:TCMTextFlowTextLineFactory;
		static private function inputManagerTextFlowFactory():TCMTextFlowTextLineFactory
		{
			if (!_inputManagerTextFlowFactory)
				_inputManagerTextFlowFactory  = new TCMTextFlowTextLineFactory();
			return _inputManagerTextFlowFactory;
		}

		// dictionary of stringfactories so that they can share a configuration
		static private var stringFactoryDictionary:Dictionary;
		static private function inputManagerStringFactory(config:IConfiguration):StringTextLineFactory
		{
			if (!stringFactoryDictionary)
				stringFactoryDictionary = new Dictionary(true);
			var factory:StringTextLineFactory = stringFactoryDictionary[config];
			if (factory == null)
			{
				factory = new StringTextLineFactory(config);
				stringFactoryDictionary[config] = factory;
			}
			return factory;
		}
		
		/** @private Method to release all references to factories so they can be gc'ed, for example when Flex unloads a module. */
		static tlf_internal function releaseReferences():void
		{
			stringFactoryDictionary = null;
			_inputManagerTextFlowFactory = null;
		}
		
		/** Shared definition of the scrollPolicy property. @private */
		static tlf_internal const editingModePropertyDefinition:Property = Property.NewEnumStringProperty("editingMode", EditingMode.READ_WRITE, false, null, 
			EditingMode.READ_WRITE, EditingMode.READ_ONLY, EditingMode.READ_SELECT);	
		
		private var _container:Sprite;
		private var _compositionWidth:Number;
		private var _compositionHeight:Number;
		
		private var _text:String;
		private var _textDamaged:Boolean;				// indicates the _text property needs updating when sourceState is SOURCE_TEXTFLOW
		private var _lastSeparator:String;
		
		private var _hostFormat:ITextLayoutFormat;
		// textFlow format to be used by a string factory - combination of config.initialTextLayoutFormat and hostFormat
		private var _stringFactoryTextFlowFormat:ITextLayoutFormat;
		
		private var _contentTop:Number;
		private var _contentLeft:Number;
		private var _contentHeight:Number;
		private var _contentWidth:Number;
		
		private var _horizontalScrollPolicy:String;
		private var _verticalScrollPolicy:String;
		
		private var _swfContext:ISWFContext;
		private var _config:IConfiguration;
		
		/** @private */
		static tlf_internal const SOURCE_STRING:int = 0;
		/** @private */
		static tlf_internal const SOURCE_TEXTFLOW:int = 1;
		
		/** @private */
		static tlf_internal const COMPOSE_FACTORY:int = 0;
		/** @private */
		static tlf_internal const COMPOSE_COMPOSER:int = 1;
		
		/** @private */
		static tlf_internal const HANDLERS_NOTADDED:int  = 0;
		/** @private */
		static tlf_internal const HANDLERS_NONE:int      = 1;
		/** @private */
		static tlf_internal const HANDLERS_CREATION:int  = 2;
		/** @private */
		static tlf_internal const HANDLERS_ACTIVE:int    = 3;
		/** @private */
		static tlf_internal const HANDLERS_MOUSEWHEEL:int = 4;
		
		private var _sourceState:int;
		private var _composeState:int;
		private var _handlersState:int;
		// track hasFocus.  Depending on various factors focus and mouseDown can occur in different order
		private var _hasFocus:Boolean;
		private var _editingMode:String;
		private var _ibeamCursorSet:Boolean;
		private var _interactionCount:int;
		
		/** @private */
		tlf_internal function get sourceState():int
		{ return _sourceState; }
		/** @private */
		tlf_internal function get composeState():int
		{ return _composeState; }
		/** @private */
		tlf_internal function get handlersState():int
		{ return _handlersState; }
	
		// Tracks damage when sourceState is SOURCE_STRING. TODO - Might be worthwhile to always set and clear this
		private var _damaged:Boolean;			
		private var _textFlow:TextFlow;
		private var _needsRedraw:Boolean;
		
		/** Constructor function - creates a TextContainerManager instance.
		 *
		 * For best results:
		 * <ol>
		 *	<li>Start with TextContainerManager.defaultConfiguration and modify it</li>   
		 *	<li>Share the same Configuration among many InputManagers</li>
		 * </ol>
		 *
		 * @param container The DisplayObjectContainer in which to manage the text lines.
		 * @param config - The IConfiguration instance to use with this TextContainerManager instance. 
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0	 	
		 */
		public function TextContainerManager(container:Sprite,configuration:IConfiguration =  null)
		{
			_container = container;
			_compositionWidth = 100;
			_compositionHeight = 100;
			
			_config = configuration ? customizeConfiguration(configuration) : defaultConfiguration;				
			_config = (_config as Configuration).getImmutableClone();

			_horizontalScrollPolicy = _verticalScrollPolicy = String(ScrollPolicy.scrollPolicyPropertyDefinition.defaultValue);

			_damaged = true;
			_needsRedraw = false;
			_text = "";
			_textDamaged = false;
			
			_sourceState = SOURCE_STRING;
			_composeState = COMPOSE_FACTORY;
			_handlersState = HANDLERS_NOTADDED;
			_hasFocus = false;
			
			_editingMode = editingModePropertyDefinition.defaultValue as String;
			_ibeamCursorSet = false;
			_interactionCount = 0;
			
			if (_container is InteractiveObject)
			{
				_container.doubleClickEnabled = true;
				// so the textlines can be swapped on the first click and a double click still works
				_container.mouseChildren = false;
				_container.focusRect = false;
			}
		}

		/** Returns the container (DisplayObjectContainer) that holds the text that this TextContainerManager manages.
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 *
	 	 * @see ContainerController#container
	 	 */
	 	 
		public function get container():Sprite
		{ return _container; }
		
		/** Returns <code>true</code> if the content needs composing. 
		 * 
		 * @return	<code>true</code> if the content needs composing; <code>false</code> otherwise.
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 */
		public function isDamaged():Boolean
		{ return _composeState == COMPOSE_FACTORY ? _damaged : _textFlow.flowComposer.isDamaged(_textFlow.textLength); }
		
		/** Editing mode of this TextContainerManager. Modes are reading only, reading and selection permitted, 
		 * and editing (reading, selection, and writing)  permitted. Use the constant values of the EditingMode
		 * class to set this property. 
		 * <p>Default value is READ_WRITE.</p>
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 *
		 * @see flashx.textLayout.edit.EditingMode EditingMode
		 */
		public function get editingMode():String
		{ return _editingMode; }
		public function set editingMode(val:String):void
		{
			var newMode:String = editingModePropertyDefinition.setHelper(_editingMode, val) as String;
			
			if (newMode != _editingMode)
			{
				if (composeState == COMPOSE_COMPOSER)
				{
					_editingMode = newMode;
					invalidateInteractionManager();
				}
				else
				{
					removeActivationEventListeners();
					_editingMode = newMode;
					// there is no way to turn it on here if going from READ_ONLY to editable and mouse is over the inputmanager field
					if (_editingMode == EditingMode.READ_ONLY)
						removeIBeamCursor();
					addActivationEventListeners();
				}
			}
		}
		
		/**
		 * Returns the current text using a separator between paragraphs.
		 * The separator can be specified with the <code>separator</code>
		 * argument. The default value of the <code>separator</code> argument
		 * is the Unicode character <code>'PARAGRAPH SEPARATOR' (U+2029)</code>.
		 *
		 * <p>Calling the setter discards any attached TextFlow. Any selection is lost.</p>
		 * 
		 * @param separator String to set between paragraphs.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
	 	 */
		public function getText(separator:String = '\u2029'):String
		{
			if (_sourceState == SOURCE_STRING)
				return _text;

			if (_textDamaged || _lastSeparator != separator)
			{
				_text = "";
				var firstLeaf:FlowLeafElement = _textFlow.getFirstLeaf();
				if (firstLeaf != null)
				{
					var para:ParagraphElement = firstLeaf.getParagraph();
					while (para)
					{
						var nextPara:ParagraphElement = para.getNextParagraph();
						_text += para.getText();
						_text += nextPara ? separator : "";
						para = nextPara;
					}
				}
				_textDamaged = false;
				_lastSeparator = separator;
			}
			return _text;
		}
		/**
		 * Sets the <code>text</code> property to the specified String.
		 *
		 * Discards any attached TextFlow. Any selection is lost.
		 * 
		 * @param str the String to set
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
	 	 */
		public function setText(text:String):void
		{
			var hadPreviousSelection:Boolean = false;

			if (_sourceState == SOURCE_TEXTFLOW)
			{
				if (_textFlow.interactionManager && _textFlow.interactionManager.hasSelection())
					hadPreviousSelection = true;
				removeTextFlowListeners();
				if (_textFlow.flowComposer)
					_textFlow.flowComposer.removeAllControllers();
				_textFlow.unloadGraphics();
				_textFlow = null;
				_sourceState = SOURCE_STRING;
				_composeState = COMPOSE_FACTORY;
				if (_container is InteractiveObject)
					_container.mouseChildren = false;
				addActivationEventListeners();
			}
			_text = text ? text : ""; 
			_damaged = true;
			_textDamaged = false;
			
			// Generate a damage event 
			if (hasEventListener(DamageEvent.DAMAGE))
				dispatchEvent(new DamageEvent(DamageEvent.DAMAGE, false, false, null, 0, _text.length));
			
			// generate a selection changed event
			if (hadPreviousSelection && hasEventListener(SelectionEvent.SELECTION_CHANGE))
				dispatchEvent(new SelectionEvent(SelectionEvent.SELECTION_CHANGE, false, false, null));
			
			if (_hasFocus)
				requiredFocusInHandler(null);
		}
				
		/** Sets the format when display just a string.  If displaying a TextFlow this has no immediate effect.  The supplied ITextLayoutFormat is not copied.  Modifying it without calling this setter has indeterminate effects. */
		public function get hostFormat():ITextLayoutFormat
		{ return _hostFormat; }
		public function set hostFormat(val:ITextLayoutFormat):void
		{
			_hostFormat = val;
			_stringFactoryTextFlowFormat = null;
			
			if (_sourceState == SOURCE_TEXTFLOW)
				_textFlow.hostFormat = _hostFormat;
			if (_composeState == COMPOSE_FACTORY)
				_damaged = true;
		}
		
		/** Returns the horizontal extent allowed for text inside the container. The value is specified in pixels.
		 * 
		 * <p>After setting this property, the text in the container is damaged and requires composing.</p>
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0  
	 	 */
		public function get compositionWidth():Number
		{ return _compositionWidth; }
		public function set compositionWidth(val:Number):void
		{
			if (_compositionWidth == val || (isNaN(_compositionWidth) && isNaN(val)))
				return;
			_compositionWidth = val; 
			if (_composeState == COMPOSE_COMPOSER)
			{
				getController().setCompositionSize(_compositionWidth,_compositionHeight);
			}
			else
			{
				_damaged = true; 
			}
		}
	
		/** Returns the vertical extent allowed for text inside the container. The value is specified in pixels. 
		 * <p>After setting this property, the text in the container is damaged and requires composing.</p>
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 */
	 	 
		public function get compositionHeight():Number
		{ return _compositionHeight; }
		public function set compositionHeight(val:Number):void
		{
			if (_compositionHeight == val || (isNaN(_compositionHeight) && isNaN(val)))
				return;
			_compositionHeight = val; 
			if (_composeState == COMPOSE_COMPOSER)
			{
				getController().setCompositionSize(_compositionWidth,_compositionHeight);
			}
			else
			{
				_damaged = true; 
			}
		}
		
		/** The Configuration object for this TextContainerManager. 
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 *
	 	 * @see flashx.textLayout.IConfiguration IConfiguration
	 	 */
		public function get configuration():IConfiguration
		{ return _config; }
			
		/** Creates a rectangle that shows where the last call to either the <code>compose()</code> 
		 * method or the <code>updateContainer()</code> method placed the text.
		 *
		 * @return  the bounds of the content
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 *
	 	 * @see #compose()
	 	 * @see #updateContainer()
		 */
		public function getContentBounds():Rectangle
		{
			if (_composeState == COMPOSE_FACTORY)
				return new Rectangle(_contentLeft, _contentTop, _contentWidth, _contentHeight);
			var controller:ContainerController = getController();
			return controller.getContentBounds();
		}	
		
		/** The current TextFlow. Converts this to a full TextFlow representation if it 
		 * isn't already one. 
		 *
		 * @return 	the current TextFlow object
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 */
		public function getTextFlow():TextFlow
		{
			if (_sourceState != SOURCE_TEXTFLOW)
			{
				var wasDamaged:Boolean = isDamaged();
				convertToTextFlow();
				if (!wasDamaged)
					updateContainer();
			}
			return _textFlow;
		}

		/** Sets a TextFlow into this TextContainerManager replacing any existing TextFlow and discarding the 
		 * current text. 
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 */
		public function setTextFlow(textFlow:TextFlow):void
		{
			if (textFlow == _textFlow)
				return;
			
			if (textFlow == null)
			{
				setText(null);
				return;
			}

			// Remove the old textFlow from its TextContainerManager, if it has one
			if (textFlow.flowComposer && textFlow.flowComposer.numControllers > 0 && textFlow.flowComposer.getControllerAt(0) is TMContainerController)
			{
				var controller:TMContainerController = textFlow.flowComposer.getControllerAt(0) as TMContainerController;
				if (controller.textContainerManager && controller.textContainerManager.getTextFlow() == textFlow)
					controller.textContainerManager.setTextFlow(null);
			}  

			if (_sourceState == SOURCE_TEXTFLOW)
			{
				removeTextFlowListeners();
				if (_textFlow.flowComposer)
					_textFlow.flowComposer.removeAllControllers();
				_textFlow.unloadGraphics();
				_textFlow = null;
			}
				
			_textFlow = textFlow;
			// damages the entire flow
			_textFlow.hostFormat = hostFormat;
			_sourceState = SOURCE_TEXTFLOW;
			_composeState = textFlow.interactionManager || textFlow.mustUseComposer() ? COMPOSE_COMPOSER : COMPOSE_FACTORY;
			_textDamaged = true;
			addTextFlowListeners();
			
			if (_composeState == COMPOSE_COMPOSER)
			{
				// Possible issue - this clear call could be delayed until updateToController
				_container.mouseChildren = true;
				clearContainerChildren(true);
				clearComposedLines();
				_textFlow.flowComposer = new StandardFlowComposer();
				_textFlow.flowComposer.swfContext = _swfContext;
				_textFlow.flowComposer.addController(new TMContainerController(_container,_compositionWidth,_compositionHeight,this));
				
				invalidateInteractionManager();
				
				// always start with an empty selection
				if (_textFlow.interactionManager)
					_textFlow.interactionManager.selectRange(-1,-1);
			}
			else
				_damaged = true;
			
			if (_hasFocus)
				requiredFocusInHandler(null);
			
			addActivationEventListeners();
		}
		
		/** 
		 * Controls whether the factory generates all text lines or stops when the container bounds are filled.
		 * 
		 * @copy flashx.textLayout.container.ContainerController#horizontalScrollPolicy 
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
	 	 */
	 	public function get horizontalScrollPolicy():String
		{
			return _horizontalScrollPolicy;
		}
		public function set horizontalScrollPolicy(scrollPolicy:String):void
		{
			_horizontalScrollPolicy = ScrollPolicy.scrollPolicyPropertyDefinition.setHelper(_horizontalScrollPolicy, scrollPolicy) as String;
			if (_composeState == COMPOSE_COMPOSER)
				getController().horizontalScrollPolicy = _horizontalScrollPolicy;
			else
				_damaged = true;
		}
		
		/** 
		 * Controls whether the factory generates all text lines or stops when the container bounds are filled.
		 * 
		 * @copy flashx.textLayout.container.ContainerController#verticalScrollPolicy 
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
	 	 */
	 	 
		public function get verticalScrollPolicy():String
		{
			return _verticalScrollPolicy;
		}
		public function set verticalScrollPolicy(scrollPolicy:String):void
		{
			_verticalScrollPolicy = ScrollPolicy.scrollPolicyPropertyDefinition.setHelper(_verticalScrollPolicy, scrollPolicy) as String;
			if (_composeState == COMPOSE_COMPOSER)
				getController().verticalScrollPolicy = _verticalScrollPolicy;
			else
				_damaged = true;
		}
			
		/** 
		 * @copy flashx.textLayout.container.ContainerController#horizontalScrollPosition
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
	 	 */
		public function get horizontalScrollPosition():Number
		{ return _composeState == COMPOSE_COMPOSER ? getController().horizontalScrollPosition : 0; }
		public function set horizontalScrollPosition(val:Number):void
		{ 
			if (val == 0 && _composeState == COMPOSE_FACTORY)
				return;
			if (_composeState != COMPOSE_COMPOSER)
				convertToTextFlowWithComposer();
			getController().horizontalScrollPosition = val;
		}
		
		/** 
		 * @copy flashx.textLayout.container.ContainerController#verticalScrollPosition 
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
	 	 */
		public function get verticalScrollPosition():Number
		{ return _composeState == COMPOSE_COMPOSER ? getController().verticalScrollPosition : 0; }
		public function set verticalScrollPosition(val:Number):void
		{
			if (val == 0 && _composeState == COMPOSE_FACTORY)
				return;
			if (_composeState != COMPOSE_COMPOSER)
				convertToTextFlowWithComposer();
			getController().verticalScrollPosition = val;
		}

		/** 
		* @copy flashx.textLayout.container.ContainerController#getScrollDelta() 
		*
		* @playerversion Flash 10
		* @playerversion AIR 1.5
		* @langversion 3.0
	 	*/
		public function getScrollDelta(numLines:int):Number
		{
			if (_composeState != COMPOSE_COMPOSER)
				convertToTextFlowWithComposer();
			return getController().getScrollDelta(numLines);
		}
		
		/** 
		* @copy flashx.textLayout.container.ContainerController#scrollToRange() 
		*
		* @playerversion Flash 10
		* @playerversion AIR 1.5
		* @langversion 3.0
	 	*/
	 	public function scrollToRange(activePosition:int,anchorPosition:int):void
	 	{
			if (_composeState != COMPOSE_COMPOSER)
				convertToTextFlowWithComposer();
			getController().scrollToRange(activePosition,anchorPosition);	 		
	 	}

		/** 
		* Optional ISWFContext instance used to make FTE calls as needed in the proper swf context. 
		*
		* 
		* @see flashx.textLayout.compose.ISWFContext
		* 
		* @playerversion Flash 10
		* @playerversion AIR 1.5
	 	* @langversion 3.0
	 	*/
 	
		public function get swfContext():ISWFContext
		{ return _swfContext; }
		public function set swfContext(context:ISWFContext):void
		{ 
			_swfContext = context;
			if (_composeState == COMPOSE_COMPOSER)
				_textFlow.flowComposer.swfContext = _swfContext;
			else
				_damaged = true;
		}
		
		/** @private - TextContainerManager wraps an underlying swfcontext - tell it to FlowComposerBase so it can avoid extra invalidation */
		public function getBaseSWFContext():ISWFContext
		{ return _swfContext; }
				
		/** @private - this is part of a performance optimziation for reusing existing TextLines in place iff recreateTextLine is available. */
	    public function callInContext(fn:Function, thisArg:Object, argsArray:Array, returns:Boolean=true):*
		{
			var textBlock:TextBlock = thisArg as TextBlock;
			if (textBlock && _expectedFactoryCompose == TextLineFactoryBase._factoryComposer)
			{
			 	if (fn == textBlock.createTextLine)
					return createTextLine(textBlock,argsArray);
				if (Configuration.playerEnablesArgoFeatures && fn == thisArg["recreateTextLine"])
					return recreateTextLine(textBlock,argsArray);
			}

			var swfContext:ISWFContext = _swfContext ? _swfContext : BaseCompose.globalSWFContext;
	        if (returns)
	            return swfContext.callInContext(fn,thisArg,argsArray,returns);
			swfContext.callInContext(fn,thisArg,argsArray,returns);
		}
		
		public function resetLine(textLine:TextLine):void
		{
			// this line is being reset
			if (textLine == _composedLines[_composeRecycledInPlaceLines-1])
				_composeRecycledInPlaceLines--;
		}
		
		/** 
		 * Uses the <code>textBlock</code> parameter, and calls the <code>TextBlock.createTextLine()</code> method on it 
		 * using the remaining parameters.
		 * WARNING: modifies argsArray
		 *  
		 * @copy flash.text.engine.TextBlock
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 */
		private function createTextLine(textBlock:TextBlock, argsArray:Array):TextLine
		{
			var swfContext:ISWFContext = _swfContext ? _swfContext : BaseCompose.globalSWFContext;
			CONFIG::debug { assert(Configuration.playerEnablesArgoFeatures,"Bad call to createTextLine"); }

			if (_composeRecycledInPlaceLines < _composedLines.length && _expectedFactoryCompose == TextLineFactoryBase._factoryComposer)
			{
				var textLine:TextLine = _composedLines[_composeRecycledInPlaceLines++];

				argsArray.splice(0,0,textLine);
				return swfContext.callInContext(textBlock["recreateTextLine"],textBlock,argsArray);
			}

			return swfContext.callInContext(textBlock.createTextLine,textBlock,argsArray);
		}

		/** 
		 * Uses the <code>textBlock</code> parameter, and calls the <code>FlowComposerBase.recreateTextLine()</code> method on it 
		 * using the remaining parameters.
		 *
		 * @param textBlock The TextBlock to which the TextLine belongs.
		 * @param textLine  The TextLine to be recreated.
		 * @param previousLine Specifies the previously broken line after 
		 *	which breaking is to commence. Can be null when breaking the first line.  
		 * @param width Specifies the desired width of the line in pixels. The 
		 * 	actual width may be less.  
		 * @param lineOffset An optional parameter which specifies the difference in 
		 *	pixels between the origin of the line and the origin of the tab stops. This can be used when lines are not aligned, 
		 * 	but it is desirable for their tabs to be so. 
		 * @param fitSomething An optional parameter that instructs the runtime to fit at least one 
		 * 	character into the text line, no matter what width has been specified (even if width is zero or negative, which 
		 * 	would otherwise result in an exception being thrown).  
		 * @return The recreated TextLine instance.
		 *  
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 */
		private function recreateTextLine(textBlock:TextBlock, argsArray:Array):TextLine
		{
			var swfContext:ISWFContext = _swfContext ? _swfContext : BaseCompose.globalSWFContext;
			CONFIG::debug { assert(Configuration.playerEnablesArgoFeatures,"Bad call to createTextLine"); }

			if (_composeRecycledInPlaceLines < _composedLines.length)
			{
				CONFIG::debug {assert(argsArray[0] != _composedLines[_composeRecycledInPlaceLines],"Bad args"); }
				TextLineRecycler.addLineForReuse(argsArray[0]);	// not going to use this one
				argsArray[0] = _composedLines[_composeRecycledInPlaceLines++];
			}
			return swfContext.callInContext(textBlock["recreateTextLine"],textBlock,argsArray);
		}

		
		/** Returns the current ISelectionManager instance. Converts to TextFlow instance and creates one if necessary. 
		 *
		 * @return  the interaction manager for this TextContainerManager instance.
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 *
	 	 * @see flashx.textLayout.edit.ISelectionManager ISelectionManager
	 	 */
		public function beginInteraction():ISelectionManager
		{
			++_interactionCount;
			if (_composeState != COMPOSE_COMPOSER)
				convertToTextFlowWithComposer();
			return _textFlow.interactionManager;
		}
		
		/** Terminates interaction. 
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 * @see flashx.textLayout.edit.ISelectionManager ISelectionManager
	 	 */
		
		public function endInteraction():void
		{
			--_interactionCount;
		}
		
		/** Call this if you are editing, and want to reset the undo manager used for editing.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 */
		public function invalidateUndoManager():void
		{
			if (_editingMode == EditingMode.READ_WRITE)
				invalidateInteractionManager(true);
		}
		
		
		/** Call this if you change the selection formats (SelectionFormat) and want the interactionManager 
		 * to update. 
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
	 	 */
		public function invalidateSelectionFormats():void
		{
			invalidateInteractionManager();
		}
		
		/** The interactionManager is invalid - update it. Clients should call this if they change the 
		 * selectionFormats.  Its called automatically if editingMode is changed. */
		private function invalidateInteractionManager(alwaysRecreateManager:Boolean = false):void
		{
			if (_composeState == COMPOSE_COMPOSER)
			{
				var interactionManager:ISelectionManager = _textFlow.interactionManager;
				var activePos:int = interactionManager ? interactionManager.activePosition : -1
				var anchorPos:int = interactionManager ? interactionManager.anchorPosition : -1;

				if (_editingMode == EditingMode.READ_ONLY)
				{
					if (interactionManager)
						_textFlow.interactionManager = null;
				}
				else if (_editingMode == EditingMode.READ_WRITE)
				{
					if (alwaysRecreateManager || interactionManager == null || interactionManager.editingMode == EditingMode.READ_SELECT)
					{
						_textFlow.interactionManager = createEditManager(getUndoManager());
						if (_textFlow.interactionManager is SelectionManager)
							SelectionManager(_textFlow.interactionManager).cloneSelectionFormatState(interactionManager);
					}
				}
				else if (_editingMode == EditingMode.READ_SELECT)
				{
					if (alwaysRecreateManager || interactionManager == null || interactionManager.editingMode == EditingMode.READ_WRITE)
					{
						_textFlow.interactionManager = createSelectionManager();
						if (_textFlow.interactionManager is SelectionManager)
							SelectionManager(_textFlow.interactionManager).cloneSelectionFormatState(interactionManager);
					}
				}
				
				interactionManager = _textFlow.interactionManager;
				if (interactionManager)
				{
					interactionManager.unfocusedSelectionFormat  = getUnfocusedSelectionFormat();
					interactionManager.focusedSelectionFormat    = getFocusedSelectionFormat();
					interactionManager.inactiveSelectionFormat = getInactiveSelectionFormat();
					interactionManager.selectRange(anchorPos,activePos);
				}				
			}
		}
		
		/**Create a selection manager to use for selection. Override this method if you have a custom SelectionManager that you
		 * want to use in place of the default.
		 *
		 * @return	a new SelectionManager instance.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
		 */
		protected function createSelectionManager():ISelectionManager
		{
			return new SelectionManager();
		}
		
		/**Create an edit manager to use for editing. Override this method if you have a custom EditManager that you
		 * want to use in place of the default.
		 *
		 * @param  an IUndoManager instance for the EditManager being created.
		 * @return	the editing manager for this TextContainerManager instance.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
		 */
		protected function createEditManager(undoManager:flashx.undo.IUndoManager):IEditManager
		{
			return new EditManager(undoManager);
		}
		
		private function getController():TMContainerController
		{ return _textFlow.flowComposer.getControllerAt(0) as TMContainerController; }

		/** @private */
		tlf_internal var _composedLines:Array = [];
		
		/** Return the TextLine at the index from array of composed lines.
		 *
		 * @param index	Finds the line at this index position in the text.
		 *
		 * @return 	the TextLine that occurs at the specified index.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
		 */
		public function getLineAt(index:int):TextLine
		{ 
			// note: this method is not reliable for damaged text
			if (_composeState == COMPOSE_FACTORY)
			{
				// watch out for the empty TCM optimization and make a TextLine
				if (_sourceState == SOURCE_STRING && _text.length == 0 && !_damaged && _composedLines.length == 0)
				{					
					if (_needsRedraw)
						compose();
					else
						updateContainer();
					CONFIG::debug { assert(_composeState == COMPOSE_FACTORY,"no longer a factory??"); }
				}
				return _composedLines[index];
			}
			var tfl:TextFlowLine = _textFlow.flowComposer.getLineAt(index);
			return tfl ? tfl.getTextLine(true) : null;
		}
		
		/** @copy flashx.textLayout.compose.IFlowComposer#numLines 
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 */
		public function get numLines():int
		{ 
			// note: this method is not reliable for damaged text
			if (_composeState == COMPOSE_COMPOSER)
				return _textFlow.flowComposer.numLines;
			// watch out for possibly optimized zero length text
			if (_sourceState == SOURCE_STRING && _text.length == 0)
				return 1;
			return _composedLines.length; 
		}
		
		/** @private 
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 */
		tlf_internal function getActualNumLines():int
		{ 
			if (_composeState != COMPOSE_COMPOSER)
				convertToTextFlowWithComposer();
			// composes all lines
			_textFlow.flowComposer.composeToPosition();
			return _textFlow.flowComposer.numLines;
		}
		
		/** @private */
		tlf_internal function clearComposedLines():void
		{
			if (_composedLines)
				_composedLines.length = 0;
		}
		
		private function populateComposedLines(displayObject:DisplayObject):void
		{
			_composedLines.push(displayObject);
		}
		
		// TODO FOR ARGO - think about moving these variables into a separate helper class 
		private var _composeRecycledInPlaceLines:int;
		private var _composePushedLines:int;
		private function populateAndRecycleComposedLines(object:DisplayObject):void
		{
			var textLine:TextLine = object as TextLine;
			if (textLine)
			{
				CONFIG::debug { assert(_composePushedLines >= _composedLines.length || _composedLines[_composePushedLines] == textLine,"mismatched recycled textline"); }
				if (_composePushedLines >= _composedLines.length)
					_composedLines.push(textLine);
			}
			else	// this is the background color and goes at the head of the list
				_composedLines.splice(0,0,object);
			_composePushedLines++;
		}		
		
		static private var _expectedFactoryCompose:SimpleCompose;

		
		/** Composes the container text; calls either the factory or <code>updateAllControllers()</code>.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 */
		public function compose():void
		{
			if (_composeState == COMPOSE_COMPOSER)
				_textFlow.flowComposer.compose();
			else if (_damaged)
			{
				if (_sourceState == SOURCE_TEXTFLOW && _textFlow.mustUseComposer())
				{
					convertToTextFlowWithComposer(false);
					_textFlow.flowComposer.compose();
					return;
				}
				else
				{
					var callback:Function;
					if (Configuration.playerEnablesArgoFeatures)
					{
						// while the first thing in the array is not a TextLine its the background color OR its some floats OR something else from the last compose - remove it
						for (;;)
						{
							var firstObj:Object = _composedLines[0];
							if (firstObj == null || (firstObj is TextLine))
								break;
							_composedLines.splice(0,1);
						}
						_composeRecycledInPlaceLines = 0;
						_composePushedLines = 0;
						callback = populateAndRecycleComposedLines;
					}
					else
					{
						clearComposedLines();
						callback = populateComposedLines;
					}
					var inputManagerFactory:TextLineFactoryBase = (_sourceState == SOURCE_STRING) ? inputManagerStringFactory(_config) : inputManagerTextFlowFactory();
					inputManagerFactory.verticalScrollPolicy = _verticalScrollPolicy;
					inputManagerFactory.horizontalScrollPolicy = _horizontalScrollPolicy;
					inputManagerFactory.compositionBounds = new Rectangle(0,0,_compositionWidth,_compositionHeight);
					inputManagerFactory.swfContext = Configuration.playerEnablesArgoFeatures ? this : _swfContext;
					
					// compose can recurse for composing.  this sets up the swfContext so it doesn't recycle a line into a numberline
					_expectedFactoryCompose = TextLineFactoryBase.peekFactoryCompose();
					if (_sourceState == SOURCE_STRING)
					{
						var stringFactory:StringTextLineFactory = inputManagerFactory as StringTextLineFactory;
						if (!_stringFactoryTextFlowFormat)
						{
							if (_hostFormat == null)
								_stringFactoryTextFlowFormat = _config.textFlowInitialFormat;
							else
							{
								// mini cascade of initialFormat onto the hostFormat
								var format:TextLayoutFormat = new TextLayoutFormat(_hostFormat);
								TextLayoutFormat.resetModifiedNoninheritedStyles(format);
								var holderStyles:Object = (_config.textFlowInitialFormat as TextLayoutFormat).getStyles();
								for (var key:String in holderStyles)
								{
									var val:* = holderStyles[key];
									format[key] = (val !== FormatValue.INHERIT) ? val : _hostFormat[key];
								}
								_stringFactoryTextFlowFormat = format;
							}
						}
						if (!TextLayoutFormat.isEqual(stringFactory.textFlowFormat,_stringFactoryTextFlowFormat))
							stringFactory.textFlowFormat = _stringFactoryTextFlowFormat;
						stringFactory.text = _text;
						stringFactory.createTextLines(callback);
					}
					else
					{
						var factory:TCMTextFlowTextLineFactory = inputManagerFactory as TCMTextFlowTextLineFactory;
						factory.tcm = this;
						factory.createTextLines(callback,_textFlow);
						factory.tcm = null;
					}
					inputManagerFactory.swfContext = null;	// release any references to the swfContext
					_expectedFactoryCompose = null;
							
					if (Configuration.playerEnablesArgoFeatures)
						_composedLines.length = _composePushedLines;

					var bounds:Rectangle = inputManagerFactory.getContentBounds();
					
					_contentLeft   = bounds.x;
					_contentTop    = bounds.y;
					_contentWidth  = bounds.width;
					_contentHeight = bounds.height;
					_damaged = false;
					
					// generate a compositionComplete event.  Note that the last composed position isn't known
					if (hasEventListener(CompositionCompleteEvent.COMPOSITION_COMPLETE))
						dispatchEvent(new CompositionCompleteEvent(CompositionCompleteEvent.COMPOSITION_COMPLETE,false,false,_textFlow,0,-1));						
				}
				_needsRedraw = true;
			}

		}
		
		/** Updates the display; calls either the factory or updateAllControllers().
		 * 
		 * @return true if anything changed.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 */
		public function updateContainer():Boolean
		{
			if (_composeState == COMPOSE_COMPOSER)
				return _textFlow.flowComposer.updateAllControllers();
			
			compose();
			
			// depending on edits the Flow may now have a composer
			if (_composeState == COMPOSE_COMPOSER)
			{
				_textFlow.flowComposer.updateAllControllers();
				return true;
			}
				
			if (!_needsRedraw)
				return false;
			
			factoryUpdateContainerChildren();									
			drawBackgroundAndSetScrollRect(0,0);
					
			if (_handlersState == HANDLERS_NOTADDED)
				addActivationEventListeners();
	
			// generate a updateComplete event.  Note that the controller isn't known
			if (hasEventListener(UpdateCompleteEvent.UPDATE_COMPLETE))
				dispatchEvent(new UpdateCompleteEvent(UpdateCompleteEvent.UPDATE_COMPLETE,false,false,null));	
						
			_needsRedraw = false;
			return true;	// things changed
		}
		
		/** @private */
		tlf_internal function factoryUpdateContainerChildren():void
		{
			var textObject:DisplayObject; 	// scratch - TextLines and background shapes
			if (Configuration.playerEnablesArgoFeatures)
			{
				// while the first child in the container is a Shape - its the background color OR a Float OR something else - lose it
				while (_container.numChildren != 0)
				{
					textObject = _container.getChildAt(0);
					if (textObject is TextLine)
						break;
					_container.removeChildAt(0);
				}
				
				// add leading children _composedLines that are not TextLines into the Container
				for (var idx:int = 0; idx < _composedLines.length; idx++)
				{
					textObject = _composedLines[idx];
					if (textObject is TextLine)
						break;
					_container.addChildAt(textObject,idx);
				}
				
				// expect the leading lines are reused
				while (_container.numChildren < _composedLines.length)
					_container.addChild(_composedLines[_container.numChildren]);
				// recycle any trailing lines
				while (_container.numChildren > _composedLines.length)
				{
					var textLine:TextLine = _container.getChildAt(_composedLines.length) as TextLine;
					_container.removeChildAt(_composedLines.length);
					if (textLine)
					{
						// lines were rebroken but aren't being displayed
						if (textLine.validity == TextLineValidity.VALID)
							textLine.textBlock.releaseLines(textLine,textLine.textBlock.lastLine);
						textLine.userData = null;
						TextLineRecycler.addLineForReuse(textLine);
					}
				}
			}
			else
			{
				clearContainerChildren(false);
				
				for each (textObject in _composedLines)
					_container.addChild(textObject);
				
				clearComposedLines();
			}
		}
		
		private function addActivationEventListeners():void
		{	
			var newState:int =  HANDLERS_NONE;
			
			if (_composeState == COMPOSE_FACTORY)
			{
				if (_editingMode == EditingMode.READ_ONLY)
					newState = HANDLERS_MOUSEWHEEL;
				else
					newState = _handlersState == HANDLERS_NOTADDED ? HANDLERS_CREATION : HANDLERS_ACTIVE;
			}
			
			if (newState == _handlersState)
				return;
			
			removeActivationEventListeners();
				
			if (newState == HANDLERS_CREATION)
			{
				_container.addEventListener(FocusEvent.FOCUS_IN, requiredFocusInHandler);				
				_container.addEventListener(MouseEvent.MOUSE_OVER, requiredMouseOverHandler);
			}
			else if (newState == HANDLERS_ACTIVE)
			{
				_container.addEventListener(FocusEvent.FOCUS_IN, requiredFocusInHandler);				
				_container.addEventListener(MouseEvent.MOUSE_OVER, requiredMouseOverHandler);
				_container.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
				_container.addEventListener(MouseEvent.MOUSE_OUT,  mouseOutHandler);
				_container.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
			//	_container.addEventListener(IMEEvent.IME_START_COMPOSITION, imeStartCompositionHandler);
			// attach by literal event name to avoid Argo dependency
				_container.addEventListener("imeStartComposition", imeStartCompositionHandler);
				
				// If TCM's getContextMenu returns null assume client has control of the contextMenu
				if (getContextMenu() != null)
					_container.contextMenu = _contextMenu;
				if (_container.contextMenu)
					_container.contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT, menuSelectHandler);
		            
		        _container.addEventListener(Event.SELECT_ALL, editHandler);
			}
			else if (newState == HANDLERS_MOUSEWHEEL)
				_container.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);				
			
			_handlersState = newState;
		}
		
		// The ContextMenu to be used.  The idea is that this is undefined until createContextMenu is called and then 
		// createContextMenu is only called once and the result is shared with the ContainerController when it gets created
		private var _contextMenu:*;
		
		/** @private  Returns the already created contextMenu.  If not created yet create it.  */
		tlf_internal function getContextMenu():ContextMenu
		{
			if (_contextMenu === undefined)
				_contextMenu = createContextMenu();
			return _contextMenu;
		}

		private function removeActivationEventListeners():void
		{
			if (_handlersState == HANDLERS_CREATION)
			{
				CONFIG::debug { assert(_composeState != COMPOSE_COMPOSER,"Bad call to removeActivationEventListeners"); }
				_container.removeEventListener(FocusEvent.FOCUS_IN, requiredFocusInHandler);				
				_container.removeEventListener(MouseEvent.MOUSE_OVER, requiredMouseOverHandler);
			}
			else if (_handlersState == HANDLERS_ACTIVE)
			{
				CONFIG::debug { assert(_composeState != COMPOSE_COMPOSER,"Bad call to removeActivationEventListeners"); }
				_container.removeEventListener(FocusEvent.FOCUS_IN, requiredFocusInHandler);				
				_container.removeEventListener(MouseEvent.MOUSE_OVER, requiredMouseOverHandler);
				_container.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
				_container.removeEventListener(MouseEvent.MOUSE_OUT,  mouseOutHandler);
				_container.removeEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
			//	_container.removeEventListener(IMEEvent.IME_START_COMPOSITION, imeStartCompositionHandler);
			// detach by literal event name to avoid Argo dependency
				_container.removeEventListener("imeStartComposition", imeStartCompositionHandler);
				// if _contextMenu is set then this TCM created the contextMenu and is manging it
				if (_container.contextMenu)
					_container.contextMenu.removeEventListener(ContextMenuEvent.MENU_SELECT, menuSelectHandler);
				// otherwise client is managing it
				if (_contextMenu)	
					_container.contextMenu = null;

		        _container.removeEventListener(Event.SELECT_ALL, editHandler);
			}
			else if (_handlersState == HANDLERS_MOUSEWHEEL)
			{
				CONFIG::debug { assert(_composeState != COMPOSE_COMPOSER,"Bad call to removeActivationEventListeners"); }
				_container.removeEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);		
			}

			_handlersState = HANDLERS_NOTADDED;
		}
		
		private function addTextFlowListeners():void
		{
			for each (var event:String in eventList)			
				_textFlow.addEventListener(event,dispatchEvent);
		}
		
		private function removeTextFlowListeners():void
		{
			for each (var event:String in eventList)			
				_textFlow.removeEventListener(event,dispatchEvent);
			_handlersState = HANDLERS_NONE;
		}
		
		/**
		 * @copy flash.events.IEventDispatcher#dispatchEvent()
		 * @private
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
 		 */
		public override function dispatchEvent(event:Event):Boolean
		{
			if (event.type == DamageEvent.DAMAGE)
			{
				_textDamaged = true;
				if (_composeState == COMPOSE_FACTORY)
					_damaged = true;
			}
			else if (event.type == FlowOperationEvent.FLOW_OPERATION_BEGIN)
			{
				if (_container.mouseChildren == false)
					_container.mouseChildren = true;
			}
			var result:Boolean = super.dispatchEvent(event);
			if (!result)
				event.preventDefault();
			return result;
		}
		
		/** @private */
		tlf_internal function clearContainerChildren(recycle:Boolean):void
		{
			while(_container.numChildren)
			{
				var textLine:TextLine = _container.getChildAt(0) as TextLine;
				_container.removeChildAt(0);
				if (textLine)
				{
					// releasing all textLines so release each still connected textBlock
					if (textLine.validity != TextLineValidity.INVALID && textLine.validity != TextLineValidity.STATIC)
					{
						var textBlock:TextBlock = textLine.textBlock;
						CONFIG::debug { Debugging.traceFTECall(null,textBlock,"releaseLines",textBlock.firstLine, textBlock.lastLine); }	
						textBlock.releaseLines(textBlock.firstLine,textBlock.lastLine);
					}					
					if (recycle)
					{
						textLine.userData = null;	// clear any userData
						TextLineRecycler.addLineForReuse(textLine);
					}
				}
			}
		}
		
		private function convertToTextFlow():void
		{
			CONFIG::debug { assert(_sourceState != SOURCE_TEXTFLOW,"bad call to convertToTextFlow"); }
									
			_textFlow = new TextFlow(_config);
			_textFlow.hostFormat = _hostFormat;
			if(_swfContext)
			{
				_textFlow.flowComposer.swfContext = _swfContext;
			}
	
			var p:ParagraphElement = new ParagraphElement();
			_textFlow.addChild(p)
			var s:SpanElement = new SpanElement();
			s.text = _text;
			p.addChild(s);
			_sourceState = SOURCE_TEXTFLOW;
			addTextFlowListeners();			
		}
				
		/** @private */
		tlf_internal function convertToTextFlowWithComposer(callUpdateContainer:Boolean = true):void
		{
			removeActivationEventListeners();
			
			if (_sourceState != SOURCE_TEXTFLOW)
				convertToTextFlow();
			
			if (_composeState != COMPOSE_COMPOSER)
			{
				clearContainerChildren(true);
				clearComposedLines();
				var controller:TMContainerController = new TMContainerController(_container,_compositionWidth,_compositionHeight,this);
				_textFlow.flowComposer = new StandardFlowComposer();
				_textFlow.flowComposer.addController(controller);
				_textFlow.flowComposer.swfContext = _swfContext;
				_composeState = COMPOSE_COMPOSER;
				
				invalidateInteractionManager();
				if (callUpdateContainer)
					updateContainer();
			}
		}
		
		private function get effectiveBlockProgression():String
		{
			if (_textFlow)
				return _textFlow.computedFormat.blockProgression;
			return _hostFormat && _hostFormat.blockProgression && _hostFormat.blockProgression != FormatValue.INHERIT ? _hostFormat.blockProgression : BlockProgression.TB;
		}
		
		/* CONFIG::debug private static function doTrace(msg:String):void
		{ trace(msg); } */
		
		private function removeIBeamCursor():void
		{
			if (_ibeamCursorSet)
			{
				Mouse.cursor = MouseCursor.AUTO;
				_ibeamCursorSet = false;
			}
		}
		
		private var _hasScrollRect:Boolean = false;
		
		/** 
		 * Specifies whether this container has attached a ScrollRect object. Value is <code>true</code>
		 * or <code>false</code>. A display object is cropped to the size defined by the scroll rectangle, and 
		 * it scrolls within the rectangle when you change the x and y properties of the scrollRect object. 
		 *
		 * <p>This property enables a client to test for a ScrollRect object without accessing 
		 * the DisplayObject.scrollRect property, which can have side effects in some cases.</p> 
		 *
		 * @return true if the controller has attached a ScrollRect instance.
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 *
		 * @see #drawBackgroundAndSetScrollRect 
		 */
		private function get hasScrollRect():Boolean
		{ return _hasScrollRect; }
		private function set hasScrollRect(value:Boolean):void
		{ _hasScrollRect = value; }
		
		/**   
		 * Returns <code>true</code> if it has filled in the container's scrollRect property.  
		 * This method enables you to test whether <code>scrollRect</code> is set without actually accessing the <code>scrollRect</code> property 
		 * which can possibly create a  performance issue. 
		 * <p>Override this method to draw a background or a border.  Overriding this method can be tricky as the scrollRect <bold>must</bold> 
		 * be set as specified.</p>
		 * 
		 * @param scrollX The starting horizontal position of the scroll rectangle.
		 * @param scrollY The starting vertical position of the scroll rectangle.
		 * 
		 * @return 	<code>true</code> if it has created the <code>scrollRect</code> object.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
	 	 * @langversion 3.0
	 	 */
		public function drawBackgroundAndSetScrollRect(scrollX:Number,scrollY:Number):Boolean
		{
			var cont:Sprite = this.container;

 			var contentWidth:Number;
			var contentHeight:Number;
			if (_composeState == COMPOSE_FACTORY)
			{
				contentWidth = _contentWidth;
				contentHeight = _contentHeight;
			}
			else
			{
				var controller:ContainerController = getController();
				contentWidth = controller.contentWidth;
				contentHeight = controller.contentHeight;
			}
			var width:Number;
			if (isNaN(compositionWidth))
			{
				var contentLeft:Number = (_composeState == COMPOSE_FACTORY) ? _contentLeft : controller.contentLeft;
				width = contentLeft + contentWidth - scrollX;
			}
			else
				width = compositionWidth;
			var height:Number;
			if (isNaN(compositionHeight))
			{ 
				var contentTop:Number = (_composeState == COMPOSE_FACTORY) ? _contentTop : controller.contentTop;
				height = contentTop + contentHeight - scrollY;
			}
			else
				height = compositionHeight;
			
			if (scrollX == 0 && scrollY == 0 && contentWidth <= width && contentHeight <= height)
			{
				// skip the scrollRect
				if (_hasScrollRect)
				{
					cont.scrollRect = null;
					_hasScrollRect = false;					
				}
			}
			else
			{
				cont.scrollRect = new Rectangle(scrollX, scrollY, width, height);
				_hasScrollRect = true;
				
				// adjust to the values actually in the scrollRect
				scrollX = cont.scrollRect.x;
				scrollY = cont.scrollRect.y;
				width = cont.scrollRect.width;
				height = cont.scrollRect.height;
			}
			
	        // NOTE: must draw a background for interaction support - even it if is 100% transparent
	        var s:Sprite = cont as Sprite;
	        if (s)
	        {
				s.graphics.clear();
				s.graphics.beginFill(0, 0); 
		       	s.graphics.drawRect(scrollX,scrollY,width,height);
		        s.graphics.endFill();
		    }
	
	        return _hasScrollRect;
		}
		
		/** Returns the focusedSelectionFormat - by default get it from the configuration.
		 * This can be overridden in the subclass to supply a different SelectionFormat
		 */
		protected function getFocusedSelectionFormat():SelectionFormat
		{
			return _config.focusedSelectionFormat;
		}
		
		/** Returns the inactiveSelectionFormat - by default get it from the configuration 
		 * This can be overridden in the subclass to supply a different SelectionFormat
		 */
		protected function getInactiveSelectionFormat():SelectionFormat
		{
			return _config.inactiveSelectionFormat;
		}
		
		/** Returns the unfocusedSelectionFormat - by default get it from the configuration 
		 * You can override this method in the subclass to supply a different SelectionFormat.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 */
		protected function getUnfocusedSelectionFormat():SelectionFormat
		{
			return _config.unfocusedSelectionFormat;
		}
		
		/** 
		 * Returns the undo manager to use. By default, creates a unique undo manager. 
		 * You can override this method in the subclass if you want to customize the undo manager
		 * (for example, to use a shared undo manager for multiple TextContainerManager instances).
		 *
		 * @return 	new IUndoManager instance.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
	 	 */
		 
		protected function getUndoManager():IUndoManager
		{
			return new UndoManager();
		}
						
		/** Creates a ContextMenu for the TextContainerManager. Use the methods of the ContextMenu 
		 *  class to add items to the menu. 
		 * <p>You can override this method to define a custom context menu.</p>
		 *
		 * @return 	the created context menu.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 * @see flash.ui.ContextMenu ContextMenu
		 */
		protected function createContextMenu():ContextMenu
		{
			return ContainerController.createDefaultContextMenu();
		}
		/** @copy flashx.textLayout.container.ContainerController#editHandler()
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
 		 * @langversion 3.0
 		 *
 		 * @see flash.events.Event Event
		 */	
		public function editHandler(event:Event):void
		{
			if (_composeState == COMPOSE_FACTORY)
			{
				convertToTextFlowWithComposer();
				getController().editHandler(event);
				_textFlow.interactionManager.setFocus();
			}
			else
				getController().editHandler(event);
		}
		
		/** @copy flashx.textLayout.container.ContainerController#keyDownHandler() 
		*
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.KeyboardEvent#KEY_DOWN KeyboardEvent.KEY_DOWN
		*/	
		public function keyDownHandler(event:KeyboardEvent):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().keyDownHandler(event);
		}
		
		/** @copy flashx.textLayout.container.ContainerController#keyUpHandler().
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.KeyboardEvent#KEY_UP KeyboardEvent.KEY_UP
		*/	
		public function keyUpHandler(event:KeyboardEvent):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().keyUpHandler(event);
		}

		/** @copy flashx.textLayout.container.ContainerController#keyFocusChangeHandler().
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
 	 	 * @langversion 3.0
		 * 	@param	event	the FocusChange event
		 */	
		public function keyFocusChangeHandler(event:FocusEvent):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().keyFocusChangeHandler(event);
		}
		
		/** @copy flashx.textLayout.container.ContainerController#textInputHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.TextEvent#TEXT_INPUT TextEvent.TEXT_INPUT
		*/
		public function textInputHandler(event:TextEvent):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().textInputHandler(event);
		}

		/** Processes the <code>IME_START_COMPOSITION</code> event when the client manages events.
		 *
		 * @param event  The IMEEvent object.
		 *
		 * @playerversion Flash 10.1
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 * 
		 *
		 * @see flash.events.IMEEvent#IME_START_COMPOSITION IMEEvent.IME_START_COMPOSITION
		 */
		public function imeStartCompositionHandler(event:IMEEvent):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().imeStartCompositionHandler(event);
		}
		
		/** Processes the <code>SOFT_KEYBOARD_ACTIVATING</code> event when the client manages events.
		 *
		 * @param event  The SoftKeyboardEvent object.
		 *
		 * @playerversion Flash 10.2
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 * 
		 *
		 * @see flash.events.SoftKeyboardEvent#SOFT_KEYBOARD_ACTIVATING SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATING
		 */
		public function softKeyboardActivatingHandler(event:Event):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().softKeyboardActivatingHandler(event);
		}
		
		/** @copy flashx.textLayout.container.ContainerController#mouseDownHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.MouseEvent#MOUSE_DOWN MouseEvent.MOUSE_DOWN
		*/	
		public function mouseDownHandler(event:MouseEvent):void
		{
			// doTrace("IM:mouseDownHandler");
			// before the mouseDown do a mouseOver
			if (_composeState == COMPOSE_FACTORY)
			{
				CONFIG::debug { assert(event.currentTarget == this.container,"TextContainerManager:mouseDownHandler - unexpected currentTarget"); }
				convertToTextFlowWithComposer();
				getController().requiredFocusInHandler(null);
				getController().requiredMouseOverHandler(event.target != container ? new RemappedMouseEvent(event) : event);
				if (_hasFocus)
					getController().requiredFocusInHandler(null);
				getController().requiredMouseDownHandler(event);
			}
			else
				getController().mouseDownHandler(event);
		}

		/** @copy flashx.textLayout.container.ContainerController#mouseMoveHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.MouseEvent#MOUSE_MOVE MouseEvent.MOUSE_MOVE
		*/	
		public function mouseMoveHandler(event:MouseEvent):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().mouseMoveHandler(event);
		}
		
		/** @copy flashx.textLayout.container.ContainerController#mouseUpHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.MouseEvent#MOUSE_UP MouseEvent.MOUSE_UP
		*/	
		public function mouseUpHandler(event:MouseEvent):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().mouseUpHandler(event);
		}
		
		/** @copy flashx.textLayout.container.ContainerController#mouseDoubleClickHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.MouseEvent#DOUBLE_CLICK MouseEvent.DOUBLE_CLICK
		*/	
		public function mouseDoubleClickHandler(event:MouseEvent):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().mouseDoubleClickHandler(event);
		}

		/** @private Process a mouseOver event.
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
		*/			
		tlf_internal final function requiredMouseOverHandler(event:MouseEvent):void
		{
			if (_composeState == COMPOSE_FACTORY)
				mouseOverHandler(event);
			if (_composeState == COMPOSE_COMPOSER)
				getController().requiredMouseOverHandler(event);
		}
		

		/** Process a mouseOver event.
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.MouseEvent#MOUSE_OVER MouseEvent.MOUSE_OVER
		*/			
		public function mouseOverHandler(event:MouseEvent):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().mouseOverHandler(event);
			else
			{
				// doTrace("IM:mouseOverHandler");
				if (effectiveBlockProgression != BlockProgression.RL)
				{
					Mouse.cursor = MouseCursor.IBEAM;
					_ibeamCursorSet = true;
				}	
				addActivationEventListeners();
			}
		}
		/** @copy flashx.textLayout.container.ContainerController#mouseOutHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.MouseEvent#MOUSE_OUT MouseEvent.MOUSE_OUT
		*/					
		public function mouseOutHandler(event:MouseEvent):void
		{
			// doTrace("IM:mouseOutHandler");
			if (_composeState == COMPOSE_FACTORY)
				removeIBeamCursor();
			else
				getController().mouseOutHandler(event);
		}		
		/** @copy flashx.textLayout.container.ContainerController#focusInHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.FocusEvent#FOCUS_IN FocusEvent.FOCUS_IN
		*/

		
		/** Process a focusIn event.
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
		*/
		public function focusInHandler(event:FocusEvent):void
		{
			_hasFocus = true;
			if (_composeState == COMPOSE_COMPOSER)
				getController().focusInHandler(event);
		}
		
		/** @private hook to get at requiredFocusOutHandler as needed */
		tlf_internal function requiredFocusOutHandler(event:FocusEvent):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().requiredFocusOutHandler(event);
		}
		/** @copy flashx.textLayout.container.ContainerController#focusOutHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.FocusEvent#FOCUS_OUT FocusEvent.FOCUS_OUT
		*/
		public function focusOutHandler(event:FocusEvent):void
		{
			_hasFocus = false;
			if (_composeState == COMPOSE_COMPOSER)
				getController().focusOutHandler(event);
		}

		/** @copy flashx.textLayout.container.ContainerController#activateHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.Event#ACTIVATE Event.ACTIVATE
		*/				
		public function activateHandler(event:Event):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().activateHandler(event);
		}		
		/** @copy flashx.textLayout.container.ContainerController#deactivateHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		* @see flash.events.Event#DEACTIVATE Event.DEACTIVATE
		*/				
		public function deactivateHandler(event:Event):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().deactivateHandler(event);
		}
		
		/** @copy flashx.textLayout.container.ContainerController#focusChangeHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		*
 		* @see flash.events.FocusEvent#KEY_FOCUS_CHANGE FocusEvent.KEY_FOCUS_CHANGE
		* @see flash.events.FocusEvent#MOUSE_FOCUS_CHANGE FocusEvent.MOUSE_FOCUS_CHANGE
		*/				
		public function focusChangeHandler(event:FocusEvent):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().focusChangeHandler(event);
		}
		
		/** @copy flashx.textLayout.container.ContainerController#menuSelectHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		*
 		* @see flash.events.ContextMenuEvent#MENU_SELECT ContextMenuEvent.MENU_SELECT
		*/				
		public function menuSelectHandler(event:ContextMenuEvent):void
		{
			if (_composeState == COMPOSE_FACTORY)
			{
				// there is no selection
				var contextMenu:ContextMenu = _container.contextMenu as ContextMenu;
				if (contextMenu)
				{
					var cbItems:ContextMenuClipboardItems = contextMenu.clipboardItems;
					cbItems.selectAll = _editingMode != EditingMode.READ_ONLY;
					cbItems.clear = false;
					cbItems.copy = false;
					cbItems.cut = false;
					cbItems.paste = false;
				}
			}
			else
				getController().menuSelectHandler(event);			
		}
		
		/** @copy flashx.textLayout.container.ContainerController#mouseWheelHandler()
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
 		*
 		* @see flash.events.MouseEvent#MOUSE_WHEEL MouseEvent.MOUSE_WHEEL
		*/				
		public function mouseWheelHandler(event:MouseEvent):void
		{
			if (event.isDefaultPrevented())
				return;

			if (_composeState == COMPOSE_FACTORY)
			{
				convertToTextFlowWithComposer();
				getController().requiredMouseOverHandler(event);
			}
			getController().mouseWheelHandler(event);
		}

		
		/** @private required FocusIn handler.  Clients override focusInHandler
		* @playerversion Flash 10
		* @playerversion AIR 1.5
 		* @langversion 3.0
		*/
		tlf_internal final function requiredFocusInHandler(event:FocusEvent):void
		{			
			// doTrace("IM:focusInHandler");
			if (_composeState == COMPOSE_FACTORY)
			{
				addActivationEventListeners();
				focusInHandler(event);
			}			
			if (_composeState == COMPOSE_COMPOSER)
				getController().requiredFocusInHandler(event);
		}
		
		/** 
		 * Called to request clients to begin the forwarding of mouseup and mousemove events from outside a security sandbox.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 */
		public function beginMouseCapture():void
		{ }
		/** 
		 * Called to inform clients that the the forwarding of mouseup and mousemove events from outside a security sandbox is no longer needed.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 */
		public function endMouseCapture():void
		{ }
		/** Client call to forward a mouseUp event from outside a security sandbox.  Coordinates of the mouse up are not needed.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 */
		public function mouseUpSomewhere(e:Event):void
		{
			if (_composeState == COMPOSE_COMPOSER)
				getController().mouseUpSomewhere(e);
		}
		/** Client call to forward a mouseMove event from outside a security sandbox.  Coordinates of the mouse move are not needed.
		 *
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 *
		 */		
		public function mouseMoveSomewhere(e:Event):void
		{ 
			if (_composeState == COMPOSE_COMPOSER)
				getController().mouseUpSomewhere(e);
		}
	
		/** @private
		 * Gets the index at which the first text line must appear in its parent.
		 * The default implementation of this method, which may be overriden, returns the child index 
		 * of the first <code>flash.text.engine.TextLine</code> child of <code>container</code>
		 * if one exists, and that of the last child of <code>container</code> otherwise. 
		 * 
		 * @return the index at which the first text line must appear in its parent.
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 * 
		 * @see flash.text.engine.TextLine
		 * @see #container
		 */	
		tlf_internal function getFirstTextLineChildIndex():int
		{			
			// skip past any non-TextLine children below the text in the container,
			// This also means that in a container devoid of text, we will always
			// populate the text starting at index container.numChildren, which is intentional.
			var firstTextLine:int;
			for(firstTextLine = 0; firstTextLine<_container.numChildren; ++firstTextLine)
			{
				if(_container.getChildAt(firstTextLine) is TextLine)
				{
					break;
				}
			}
			return firstTextLine;
		}
		
		/** @private
		 * Adds a <code>flash.text.engine.TextLine</code> object as a descendant of <code>container</code>.
		 * The default implementation of this method, which may be overriden, adds the object
		 * as a direct child of <code>container</code> at the specified index.
		 * 
		 * @param textLine the <code>flash.text.engine.TextLine</code> object to add
		 * @param index insertion index of the text line in its parent 
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 * 
		 * @see flash.text.engine.TextLine
		 * @see #container
		 * 
		 */	
		tlf_internal function addTextLine(textLine:TextLine, index:int):void
		{ 
			_container.addChildAt(textLine, index);
		}
		
		/** @private
		 * Removes a <code>flash.text.engine.TextLine</code> object from its parent. 
		 * The default implementation of this method, which may be overriden, removes the object
		 * from <code>container</code> if it is a direct child of the latter.
		 * 
		 * This method may be called even if the object is not a descendant of <code>container</code>.
		 * Any implementation of this method must ensure that no action is taken in this case.
		 * 
		 * @param textLine the <code>flash.text.engine.TextLine</code> object to remove 
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 * 
		 * @see flash.text.engine.TextLine
		 * @see #container
		 * 
		 */	
		tlf_internal function removeTextLine(textLine:TextLine):void
		{
			if (_container.contains(textLine))
				_container.removeChild(textLine);
		}
		
		/** @private
		 * Adds a <code>flash.display.Shape</code> object on which background shapes (such as background color) are drawn.
		 * The default implementation of this method, which may be overriden, adds the object to <code>container</code>
		 * just before the first <code>flash.text.engine.TextLine</code> child, if one exists, and after the last exisiting
		 * child otherwise. 
		 * 
		 * @param shape <code>flash.display.Shape</code> object to add
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 * 
		 * @see flash.display.Shape
		 * @see flash.text.engine.TextLine
		 * @see #container
		 * 
		 */
		tlf_internal function addBackgroundShape(shape:Shape):void	// No PMD
		{
			_container.addChildAt(shape, getFirstTextLineChildIndex());
		}
		
		/** @private
		 * Removes a <code>flash.display.Shape</code> object on which background shapes (such as background color) are drawn.
		 * The default implementation of this method, which may be overriden, removes the object from its <code>parent</code>.
		 * 
		 * @param shape <code>flash.display.Shape</code> object to remove
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 * 
		 * @see flash.display.Shape
		 * @see flash.text.engine.TextLine
		 * @see #container
		 * 
		 */
		tlf_internal function removeBackgroundShape(shape:Shape):void	
		{
			if (shape.parent)
				shape.parent.removeChild(shape);
		}
		
		/** @private
		 * Adds a <code>flash.display.DisplayObjectContainer</code> object to which selection shapes (such as block selection highlight, cursor etc.) are added.
		 * The default implementation of this method, which may be overriden, has the following behavior:
		 * The object is added just before first <code>flash.text.engine.TextLine</code> child of <code>container</code> if one exists 
		 * and the object is opaque and has normal blend mode. 
		 * In all other cases, it is added as the last child of <code>container</code>.
		 * 
		 * @param selectionContainer <code>flash.display.DisplayObjectContainer</code> object to add
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 * 
		 * @see flash.display.DisplayObjectContainer
		 * @see flash.text.engine.TextLine
		 * @see #container
		 */
		tlf_internal function addSelectionContainer(selectionContainer:DisplayObjectContainer):void
		{
			if (selectionContainer.blendMode == BlendMode.NORMAL && selectionContainer.alpha == 1)
			{
				// don't put selection behind background color or existing content in container, put it behind first text line
				_container.addChildAt(selectionContainer, getFirstTextLineChildIndex());
			}
			else
				_container.addChild(selectionContainer);
		}
		
		/** @private
		 * Removes the <code>flash.display.DisplayObjectContainer</code> object which contains selection shapes (such as block selection highlight, cursor etc.).
		 * The default implementation of this method, which may be overriden, removes the object from its parent if one exists.
		 * 
		 * @param selectionContainer <code>flash.display.DisplayObjectContainer</code> object to remove
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 1.5
		 * @langversion 3.0
		 * 
		 * @see flash.display.DisplayObjectContainer
		 * @see #container
		 * 
		 */
		tlf_internal function removeSelectionContainer(selectionContainer:DisplayObjectContainer):void
		{	
			selectionContainer.parent.removeChild(selectionContainer);
		}
		
		/** @private
		 * Adds a <code>flash.display.DisplayObject</code> object as a descendant of <code>parent</code>.
		 * The default implementation of this method, which may be overriden, adds the object
		 * as a direct child of <code>parent</code> at the specified index. This is called to add 
		 * InlineGraphicElements to the TextLine or container.
		 * 
		 * @param parent the <code>flash.display.DisplayObjectContainer</code> object to add the inlineGraphicElement to
		 * @param inlineGraphicElement the <code>flash.display.DisplayObject</code> object to add
		 * @param index insertion index of the float in its parent 
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 2.0
		 * @langversion 3.0
		 * 
		 * @see flash.display.DisplayObjectContainer
		 * @see flash.display.DisplayObject
		 * @see #container
		 * 
		 */	
		tlf_internal function addInlineGraphicElement(parent:DisplayObjectContainer, inlineGraphicElement:DisplayObject, index:int):void
		{
			parent.addChildAt(inlineGraphicElement, index);
		}
		
		/** @private
		 * Removes a <code>flash.display.DisplayObject</code> object from its parent. 
		 * The default implementation of this method, which may be overriden, removes the object
		 * from <code>container</code> if it is a direct child of the latter.
		 * 
		 * This method may be called even if the object is not a descendant of <code>parent</code>.
		 * Any implementation of this method must ensure that no action is taken in this case.
		 * 
		 * @param float the <code>flash.display.DisplayObject</code> object to remove 
		 * 
		 * @playerversion Flash 10
		 * @playerversion AIR 2.0
		 * @langversion 3.0
		 * 
		 * @see flash.display.DisplayObjectContainer
		 * @see flash.display.DisplayObject
		 * @see #container
		 * 
		 */	
		tlf_internal function removeInlineGraphicElement(parent:DisplayObjectContainer, inlineGraphicElement:DisplayObject):void
		{
			if (inlineGraphicElement.parent == parent)
				parent.removeChild(inlineGraphicElement);
		}
	
	}
	
}

import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.engine.TextLine;
import flash.ui.ContextMenu;

import flashx.textLayout.compose.IFlowComposer;
import flashx.textLayout.compose.SimpleCompose;
import flashx.textLayout.container.ContainerController;
import flashx.textLayout.container.ScrollPolicy;
import flashx.textLayout.container.TextContainerManager;
import flashx.textLayout.edit.IInteractionEventHandler;
import flashx.textLayout.elements.ContainerFormattedElement;
import flashx.textLayout.factory.FactoryDisplayComposer;
import flashx.textLayout.factory.TextFlowTextLineFactory;
import flashx.textLayout.factory.TextLineFactoryBase;
import flashx.textLayout.formats.BackgroundColor;
import flashx.textLayout.formats.BlockProgression;
import flashx.textLayout.tlf_internal;

use namespace tlf_internal;

class TMContainerController extends ContainerController
{
	private var _inputManager:TextContainerManager;
	
	public function TMContainerController(container:Sprite, compositionWidth:Number, compositionHeight:Number, tm:TextContainerManager)
	{
		super(container,compositionWidth,compositionHeight);
		_inputManager = tm;
		verticalScrollPolicy = tm.verticalScrollPolicy;
		horizontalScrollPolicy = tm.horizontalScrollPolicy;
	}

	public function get textContainerManager():TextContainerManager { return _inputManager; }
	
	/** Reroute to the TextContainerManger's override.  Reuse the one that's already been created. */
	protected override function createContextMenu():ContextMenu
	{ return _inputManager.getContextMenu(); }

	/** @private */
	protected override function get attachTransparentBackground():Boolean
	{ return false; }
	
	/** @private */
	tlf_internal function doUpdateVisibleRectangle():void
	{ updateVisibleRectangle(); }
	
	/** @private. Override clones and enhances parent class functionality. */
	protected override function updateVisibleRectangle() :void
	{
		var xpos:Number;
		var ypos:Number;
		// see the adjustLines boolean in ContainerController.fillShapeChildren - this logic clones that and allows for skipping the scrollRect
		xpos = horizontalScrollPosition;
		if (effectiveBlockProgression == BlockProgression.RL && (verticalScrollPolicy != ScrollPolicy.OFF || horizontalScrollPolicy != ScrollPolicy.OFF))
			xpos -= !isNaN(compositionWidth) ? compositionWidth : contentWidth;

		ypos = verticalScrollPosition;
			
		_hasScrollRect = _inputManager.drawBackgroundAndSetScrollRect(xpos,ypos);
	}
		
	/** @private */
	tlf_internal override function getInteractionHandler():IInteractionEventHandler
	{ return _inputManager; }
	
	
	/** @private */
	tlf_internal override function attachContextMenu():void
	{ 
		if (_inputManager.getContextMenu() != null)
			super.attachContextMenu();
	}
	
	/** @private */
	tlf_internal override function removeContextMenu():void
	{ 				
		// otherwise client is managing it
		if (_inputManager.getContextMenu())	
			super.removeContextMenu();
	}
	
	// ////////////////////////////////////////////////////////////////////////////
	// push all these methods for manipulating the object list to the _inputmanager
	// ////////////////////////////////////////////////////////////////////////////
	protected override function getFirstTextLineChildIndex():int
	{ return _inputManager.getFirstTextLineChildIndex(); }
	protected override function addTextLine(textLine:TextLine, index:int):void
	{ _inputManager.addTextLine(textLine,index); }
	protected override function removeTextLine(textLine:TextLine):void
	{ _inputManager.removeTextLine(textLine); }
	protected override function addBackgroundShape(shape:Shape):void
	{ _inputManager.addBackgroundShape(shape); }
	protected override function removeBackgroundShape(shape:Shape):void
	{ _inputManager.removeBackgroundShape(shape); }
	protected override function addSelectionContainer(selectionContainer:DisplayObjectContainer):void
	{ _inputManager.addSelectionContainer(selectionContainer); }
	protected override function removeSelectionContainer(selectionContainer:DisplayObjectContainer):void
	{ _inputManager.removeSelectionContainer(selectionContainer); }
	protected override function addInlineGraphicElement(parent:DisplayObjectContainer, inlineGraphicElement:DisplayObject, index:int):void
	{ _inputManager.addInlineGraphicElement(parent,inlineGraphicElement,index); }
	protected override function removeInlineGraphicElement(parent:DisplayObjectContainer, inlineGraphicElement:DisplayObject):void
	{ _inputManager.removeInlineGraphicElement(parent,inlineGraphicElement); }
}

// remap the mouse event for processing inside TLF.  This is just the initial click.  Make it as if the event was on the container and not the textline
class RemappedMouseEvent extends MouseEvent
{
	private var _event:MouseEvent;
	
	public function RemappedMouseEvent(event:MouseEvent,cloning:Boolean = false)
	{
		var containerPoint:Point;
		if (!cloning)
		{
			containerPoint = DisplayObject(event.target).localToGlobal(new Point(event.localX, event.localY));
			containerPoint = DisplayObject(event.currentTarget).globalToLocal(containerPoint);
		}
		else
			containerPoint = new Point();

		/* event.commandKey,event.controlKey,event.clickCount are also supported in AIR.  IMHO they are a nonissue for the initial click */
		super(event.type,event.bubbles,event.cancelable,containerPoint.x,containerPoint.y,event.relatedObject,event.ctrlKey,event.altKey,event.shiftKey,event.buttonDown,event.delta);
		
		_event = event;
	}

	// override methods/getters for things we couldn't set in the base class	

	public override function get target():Object
	{ return _event.currentTarget; }
	
	public override function get currentTarget():Object
	{ return _event.currentTarget; }
	
	public override function get eventPhase():uint
	{ return _event.eventPhase; }
	
	public override function get isRelatedObjectInaccessible():Boolean
	{ return _event.isRelatedObjectInaccessible; }
	
	public override function get stageX():Number
	{ return _event.stageX; }
	
	public override function get stageY():Number
	{ return _event.stageY; }
	
	public override function clone():Event
	{ 
		var rslt:RemappedMouseEvent = new RemappedMouseEvent(_event,true); 
		rslt.localX = localX;
		rslt.localY = localY;
		return rslt;
	}
	
	public override function updateAfterEvent():void
	{ _event.updateAfterEvent(); }
	
	public override function isDefaultPrevented():Boolean
	{ return _event.isDefaultPrevented(); }
	
	public override function preventDefault():void
	{ _event.preventDefault(); }
	
	public override function stopImmediatePropagation():void
	{ _event.stopImmediatePropagation(); }
	
	public override function stopPropagation():void
	{ _event.stopPropagation(); }
}

class TCMTextFlowTextLineFactory extends TextFlowTextLineFactory
{
	private var _tcm:TextContainerManager;
	
	public function TCMTextFlowTextLineFactory()
	{ super(); }
	
	/** @private */
	tlf_internal override function createFlowComposer():IFlowComposer
	{ return new TCMFactoryDisplayComposer(_tcm); }
	
	public function get tcm():TextContainerManager
	{ return _tcm; }
	public function set tcm(val:TextContainerManager):void
	{ _tcm = val; }
}


class TCMFactoryDisplayComposer extends FactoryDisplayComposer
{
	tlf_internal var _tcm:TextContainerManager;
	public function TCMFactoryDisplayComposer(tcm:TextContainerManager)
	{ _tcm = tcm; }
	
	tlf_internal override function callTheComposer(absoluteEndPosition:int, controllerEndIndex:int):ContainerController
	{
		// always do a full compose
		clearCompositionResults();
		
		var state:SimpleCompose = TextLineFactoryBase._factoryComposer;
		state.resetLineHandler = _tcm.resetLine;
		state.composeTextFlow(textFlow, -1, -1);
		state.releaseAnyReferences()
		return getControllerAt(0);
	}
}