flex-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ha...@apache.org
Subject [33/42] flex-asjs git commit: And here’s TLF…
Date Thu, 16 Mar 2017 13:37:53 GMT
http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/fd08d137/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/container/ContainerController.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/container/ContainerController.as b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/container/ContainerController.as
new file mode 100644
index 0000000..3460da5
--- /dev/null
+++ b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/container/ContainerController.as
@@ -0,0 +1,5222 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.flex.textLayout.container 
+{
+	import org.apache.flex.textLayout.elements.GlobalSettings;
+	import org.apache.flex.textLayout.elements.IInlineGraphicElement;
+	import org.apache.flex.textLayout.compose.ITextFlowTableBlock;
+	import org.apache.flex.textLayout.elements.ITableRowElement;
+	import org.apache.flex.textLayout.utils.CreateTLFUtil;
+	import org.apache.flex.textLayout.elements.ITableCellElement;
+	import org.apache.flex.textLayout.elements.IParagraphElement;
+	import org.apache.flex.textLayout.elements.IFlowLeafElement;
+	import org.apache.flex.core.IChild;
+	import org.apache.flex.core.IParentIUIBase;
+	import org.apache.flex.core.IUIBase;
+	import org.apache.flex.events.Event;
+	import org.apache.flex.events.IEventDispatcher;
+	import org.apache.flex.events.KeyboardEvent;
+	import org.apache.flex.events.MouseEvent;
+	import org.apache.flex.geom.Matrix;
+	import org.apache.flex.geom.Point;
+	import org.apache.flex.geom.Rectangle;
+	import org.apache.flex.graphics.ICompoundGraphic;
+	import org.apache.flex.graphics.IRect;
+	import org.apache.flex.graphics.SolidColor;
+	import org.apache.flex.text.engine.ITextBlock;
+	import org.apache.flex.text.engine.ITextLine;
+	import org.apache.flex.text.events.IMEEvent;
+	import org.apache.flex.text.events.TextEvent;
+	import org.apache.flex.textLayout.compose.FloatCompositionData;
+	import org.apache.flex.textLayout.compose.FlowDamageType;
+	import org.apache.flex.textLayout.compose.IFlowComposer;
+	import org.apache.flex.textLayout.compose.ITextFlowLine;
+	import org.apache.flex.textLayout.compose.TextLineRecycler;
+	import org.apache.flex.textLayout.debug.Debugging;
+	import org.apache.flex.textLayout.debug.assert;
+	import org.apache.flex.textLayout.dummy.BoundsUtil;
+	import org.apache.flex.textLayout.dummy.ContextMenu;
+	import org.apache.flex.textLayout.edit.EditingMode;
+	import org.apache.flex.textLayout.edit.IInteractionEventHandler;
+	import org.apache.flex.textLayout.edit.ISelectionManager;
+	import org.apache.flex.textLayout.edit.SelectionFormat;
+	import org.apache.flex.textLayout.elements.IBackgroundManager;
+	import org.apache.flex.textLayout.elements.CellCoordinates;
+	import org.apache.flex.textLayout.elements.IContainerFormattedElement;
+	import org.apache.flex.textLayout.elements.TableBlockContainer;
+	import org.apache.flex.textLayout.elements.ITextFlow;
+	import org.apache.flex.textLayout.events.ActivateEvent;
+	import org.apache.flex.textLayout.events.ContextMenuEvent;
+	import org.apache.flex.textLayout.events.EditEvent;
+	import org.apache.flex.textLayout.events.FlowElementMouseEventManager;
+	import org.apache.flex.textLayout.events.FocusEvent;
+	import org.apache.flex.textLayout.events.ModelChange;
+	import org.apache.flex.textLayout.events.ScrollEvent;
+	import org.apache.flex.textLayout.events.ScrollEventDirection;
+	import org.apache.flex.textLayout.events.SelectionEvent;
+	import org.apache.flex.textLayout.events.TextLayoutEvent;
+	import org.apache.flex.textLayout.events.UpdateCompleteEvent;
+	import org.apache.flex.textLayout.formats.BlockProgression;
+	import org.apache.flex.textLayout.formats.Float;
+	import org.apache.flex.textLayout.formats.FormatValue;
+	import org.apache.flex.textLayout.formats.ITextLayoutFormat;
+	import org.apache.flex.textLayout.formats.TextLayoutFormat;
+	import org.apache.flex.textLayout.formats.TextLayoutFormatBase;
+
+	import org.apache.flex.textLayout.utils.Twips;
+	import org.apache.flex.utils.DisplayUtils;
+	import org.apache.flex.utils.ObjectMap;
+	import org.apache.flex.utils.PointUtils;
+	import org.apache.flex.utils.Timer;
+
+
+
+	/**
+	 * The ContainerController class defines the relationship between a TextFlow object and a container.
+	 * A TextFlow may have one or more rectangular areas that can hold text; the text is said to be flowing
+	 * through the containers. Each container is a Sprite that is the parent DisplayObject for the TextLines.
+	 * Each container has a ContainerController that manages the container; the controller holds the target 
+	 * width and height for the text area, populates the container with TextLines, and handles scrolling. A
+	 * controller also has a format associated with it that allows some formatting attributes to be applied 
+	 * to the text in the container. This allows, for instance, a TextFlow to have one container where the
+	 * text appears in a single column, and a second container in the same TextFlow with two column text. Not
+	 * all formatting attributes that can be applied to the container will affect the text; only the ones that
+	 * affect container-level layout. The diagram below illustrates the relationship between the TextFlow,
+	 * its flowComposer, and the display list.
+	 *
+	 * <p><img src="../../../images/textLayout_multiController.gif" alt="IContainerController"></img></p>
+	 *
+	 * @playerversion Flash 10
+	 * @playerversion AIR 1.5
+	 * @langversion 3.0
+	 *
+	 * @see org.apache.flex.textLayout.compose.IFlowComposer
+	 * @see org.apache.flex.textLayout.elements.TextFlow
+	 * @see org.apache.flex.text.engine.ITextLine
+	 */
+	public class ContainerController extends TextLayoutFormatBase implements IInteractionEventHandler, ISandboxSupport, IContainerController
+	{		
+		
+		private var _textFlowCache:ITextFlow;
+		private var _rootElement:IContainerFormattedElement;
+		
+		private var _absoluteStart:int;
+		private var _textLength:int;
+		
+		private var _container:IParentIUIBase;
+		private var _mouseEventManager:FlowElementMouseEventManager;
+		
+		// note must be protected - subclass sets or gets this variable but can't be public
+		/** computed container attributes.  @private */
+		protected var _computedFormat:TextLayoutFormat;
+		
+		// Generated column information
+		// Generated column information
+		private var _columnState:ColumnState;
+		
+		/** Container size to be composed */
+		private var _compositionWidth:Number = 0;
+		private var _compositionHeight:Number = 0;
+		private var _measureWidth:Boolean; // true if we're measuring (isNaN(compositionWidth) optimization so we don't call isNaN too much
+		private var _measureHeight:Boolean; // true if we're measuring (isNaN(compositionHeight) optimization so we don't call isNaN too much
+		
+		/* Text bounds after composition */
+		private var _contentLeft:Number;
+		private var _contentTop:Number;
+		private var _contentWidth:Number;
+		private var _contentHeight:Number;
+		
+		private var _uncomposedTextLength:int;	// 0 if composition was complete when contentHeight, etc registered, greater than one otherwise
+		private var _finalParcelStart:int;
+		
+		// Scroll policy -- determines whether scrolling is enabled or not
+		private var _horizontalScrollPolicy:String;
+		private var _verticalScrollPolicy:String;
+		
+		// x, y location of the text in the container relative to the underlying scrollable area
+		private var _xScroll:Number;
+		private var _yScroll:Number;
+		
+		/** Are event listeners attached to the container */
+		private var _minListenersAttached:Boolean = false;
+		private var _allListenersAttached:Boolean = false;
+		private var _selectListenersAttached:Boolean = false;
+		public var _mouseWheelListenerAttached:Boolean = false;
+		
+		/** @private */
+		public function get allListenersAttached():Boolean
+		{ return _allListenersAttached; }
+		
+		/** Are the displayed shapes out of date? */
+		private var _shapesInvalid:Boolean = false;
+		
+		private var _backgroundShape:IUIBase;
+		
+		private var _scrollTimer:Timer = null;
+		
+		/**
+		 * @private use this boolean to determine if container.scrollRect is set.  Accessing scrollRect when null changes the rendering behavior of flash player.	
+		 */
+		protected var _hasScrollRect:Boolean;
+		
+		private var _linesInView:Array;	// lines that were in view according to the previous compose(). Empty if the lines have already been posted to the display list.
+		private var _updateStart:int;
+		private var _tableBlocksInView:Array; // // table blocks that were in view according to the previous compose(). Empty if the lines have already been posted to the display list.
+		
+		private var _composedFloats:Array;  // floats that were composed into the controller -- array of FloatCompositionData
+		private var _floatsInContainer:Array;  // floats are currently in view -- array of DisplayObject
+		
+		/** Interactive Objects **/
+		private var _interactiveObjects:ObjectMap = new ObjectMap(true);
+		private var _oldInteractiveObjects:Array = new Array();
+		
+		public function get interactiveObjects():ObjectMap
+		{
+			return _interactiveObjects ;
+		}
+		
+		public function get oldInteractiveObjects():Array
+		{
+			return _oldInteractiveObjects ;
+		}
+		
+		/** 
+		 * @private
+		 * 
+		 * <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.
+		 */
+		public function get hasScrollRect():Boolean
+		{ return _hasScrollRect; }
+		
+		CONFIG::debug
+		{
+			protected var id:String;
+			private static var contCount:int = 0;
+		}
+		
+		private var _shapeChildren:Array;
+				
+		private var _containerRoot:IParentIUIBase;
+		
+		/* Controller have a non-zero default width and height so that if you construct a text example with a container and don't
+		* specify width and height you will still see some text so that you can then have a clue what to do to correct its appearance.
+		*/
+		
+		/** 
+		 * Constructor - creates a ContainerController instance. The ContainerController has a default <code>compositionWidth</code>
+		 * and <code>compositionHeight</code> so that some text appears in the container if you don't specify its width
+		 * height.
+		 *
+		 * @param container The DisplayObjectContainer in which to manage the text lines.
+		 * @param compositionWidth The initial width for composing text in the container.
+		 * @param compositionHeight The initial height for composing text in the container.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function ContainerController(container:IParentIUIBase,compositionWidth:Number=100,compositionHeight:Number=100)
+		{
+			initialize(container,compositionWidth,compositionHeight);
+		}
+		
+		private function initialize(container:IParentIUIBase,compositionWidth:Number,compositionHeight:Number):void
+		{
+			_container = container;
+			_containerRoot =  null;
+			
+			_textLength = 0;
+			_absoluteStart = -1;
+			
+			_columnState = new ColumnState(null/*blockProgression*/, null/*columnDirection*/, null/*controller*/, 0/*compositionWidth*/, 0/*compositionHeight*/);
+			//_visibleRect = new Rectangle();
+			_xScroll = _yScroll = 0;
+			_contentWidth = _contentHeight = 0;
+			_uncomposedTextLength = 0;
+			
+			// We have to set the flag so that we will get double click events. This
+			// is a change to the container we are given, but a minor one.
+
+//TODO doubleClickEnabled
+//			_container.doubleClickEnabled = true;
+			
+			_horizontalScrollPolicy = _verticalScrollPolicy = String(ScrollPolicy.scrollPolicyPropertyDefinition.defaultValue);
+			_hasScrollRect = false;
+			
+			CONFIG::debug { id = contCount.toString(); ++contCount; }
+			
+			_shapeChildren = [ ];
+			_linesInView = [ ];
+			_tableBlocksInView = [];
+			
+			setCompositionSize(compositionWidth, compositionHeight);
+			format = _containerControllerInitialFormat;
+		}
+		
+		/** @private */
+		public function get effectiveBlockProgression():String
+		{
+			return _rootElement ? _rootElement.computedFormat.blockProgression : BlockProgression.TB;
+		}
+
+//TODO we probably need platform specific methods to attach events to the root		
+		/** @private  Determine containerRoot in case the stage is not accessible. Normally the root is the stage. */
+//		public function getContainerRoot():DisplayObject
+//		{
+//			// safe to test for stage existence
+//			if (_containerRoot == null && _container && _container.stage)
+//			{
+//				// if the stage is accessible lets use it.
+//				// trace("BEFORE COMPUTING CONTAINERROOT");
+//				try
+//				{
+//					var x:int = _container.stage.numChildren;
+//					_containerRoot = _container.stage;
+//				}
+//				catch(e:Error)
+//				{
+//					// TODO: some way to find the highest level accessible root???
+//					_containerRoot = _container.root;
+//				}
+//				// trace("AFTER COMPUTING CONTAINERROOT");
+//			}
+//			return _containerRoot;
+//		}
+		
+		/** 
+		 * Returns the flow composer object that composes and highlights text into the container that this 
+		 * controller manages. 
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 * @see org.apache.flex.textLayout.compose.IFlowComposer
+		 */
+		
+		public function get flowComposer():IFlowComposer
+		{ return textFlow ? textFlow.flowComposer : null; }
+		
+		/** @private */
+		public function get shapesInvalid():Boolean
+		{ return _shapesInvalid; }
+		/** @private */
+		public function set shapesInvalid(val:Boolean):void
+		{ _shapesInvalid = val;	}
+		
+		/** 
+		 * Returns a ColumnState object, which describes the number and characteristics of columns in
+		 * the container. These values are updated when the text is recomposed, either as a result
+		 * of <code>IFlowComposer.compose()</code> or <code>IFlowComposer.updateAllControllers()</code>.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see ColumnState
+		 */
+		
+		public function get columnState():ColumnState
+		{
+			if (_rootElement == null)
+				return null;
+			
+			if (_computedFormat == null)
+				calculateComputedFormat();
+			
+			_columnState.computeColumns();
+			
+			return _columnState; 
+		}
+		
+		/** 
+		 * Returns the container display object that holds the text lines for this ContainerController instance. 
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see #ContainerController()
+		 */
+		
+		public function get container():IParentIUIBase
+		{ return _container; }
+		
+		/** 
+		 * Returns the horizontal extent allowed for text inside the container. The value is specified in pixels.
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see #setCompositionSize()
+		 */
+		
+		public function get compositionWidth():Number
+		{ return _compositionWidth; }
+		
+		/** 
+		 * Returns the vertical extent allowed for text inside the container. The value is specified in pixels.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see #setCompositionSize()
+		 */
+		
+		public function get compositionHeight():Number
+		{ return _compositionHeight; }
+		
+		/** @private */
+		public function get measureWidth():Boolean
+		{ return _measureWidth; }
+		
+		/** @private */
+		public function get measureHeight():Boolean
+		{ return _measureHeight; }
+		
+		/** 
+		 * Sets the width and height allowed for text in the container. Width and height can be specified in pixels or <code>NaN</code> can be used for either value.  <code>NaN</code> indicates measure that value. 
+		 * This can be used to find the widest line and/or the total height of all the content.  When NaN is specified as the width lines are broken with a maximum width of <code>ITextLine.MAX_LINE_WIDTH</code>. 
+		 * When <code>NaN</code> is specified as the height the container is assumed to have unlimited height.  The actual measured values can be read back in <code>getContentBounds</code>.  
+		 * When the computed <code>blockProgression</code> property of <code>TextFlow</code>
+		 * is <code>BlockProgression.RL</code> the meanings of width and height are exchanged.
+		 *
+		 * @param w The width in pixels that's available for text in the container.  <code>NaN</code> indicates no specified width.  
+		 * @param h The height in pixels that's available for text in the container.   <code>NaN</code> indicates no specified height.  
+		 *
+		 * 
+		 * @see org.apache.flex.text.engine.ITextLine#MAX_LINE_WIDTH
+		 * @see org.apache.flex.textLayout.formats.BlockProgression
+		 * @see #getContentBounds()
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function setCompositionSize(w:Number, h:Number):void
+		{
+		//	trace("setCompositionSize(" + w + ", " + h + ")");
+			
+			// note: NaN == NaN is always false
+			var widthChanged:Boolean  =  !(_compositionWidth == w || (isNaN(_compositionWidth) && isNaN(w)));
+			var heightChanged:Boolean =  !(_compositionHeight == h || (isNaN(_compositionHeight) && isNaN(h)));
+			
+			if (widthChanged || heightChanged)
+			{
+				_compositionHeight = h;
+				_measureHeight = isNaN(_compositionHeight);
+				_compositionWidth = w;
+				_measureWidth = isNaN(_compositionWidth);
+				// otherwise the reset will happen when the cascade is done
+				if (_computedFormat)
+					resetColumnState();
+				// Invalidate all the lines, forcing FTE rebuild if they changed in the logical width direction
+				if (effectiveBlockProgression == BlockProgression.TB ? widthChanged : heightChanged)
+				{
+					if (textFlow && _textLength)
+						textFlow.damage(absoluteStart, _textLength, "invalid", false);
+				}
+				else
+					invalidateContents();		 // don't need to rebuild FTE lines, just reflow them
+				attachTransparentBackgroundForHit(false);
+			}
+		}
+		
+		/** 
+		 * Returns the TextFlow object whose content appears in the container. Either the <code>textFlow</code> and  
+		 * <code>rootElement</code> values are the same, or this is the root element's TextFlow object. For example,
+		 * if the container's root element is a DivElement, the value would be the TextFlow object to which the
+		 * DivElement belongs.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+	 	 * @langversion 3.0
+	 	 * 
+	 	 * @see org.apache.flex.textLayout.elements.TextFlow TextFlow
+	 	 */		 
+		public function get textFlow():ITextFlow
+		{ 
+			if (!_textFlowCache && _rootElement)
+				_textFlowCache = _rootElement.getTextFlow();
+			return _textFlowCache;
+		}
+		
+		// Reserve possibility for future use as a IContainerFormattedElement within the TextFlow.
+		
+		/** 
+		 * Returns the root element that appears in the container. The root element could be a DivElement or TextFlow
+		 * instance, for example.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 * @see org.apache.flex.textLayout.elements.IContainerFormattedElement
+		 * @see org.apache.flex.textLayout.elements.DivElement
+		 * @see org.apache.flex.textLayout.elements.TextFlow
+		 */
+		
+		public function get rootElement():IContainerFormattedElement
+		{ return _rootElement; }
+		
+		/** Protected method used when updating the rootElement. 
+		 * @param value new container to be controlled
+		 * 
+		 * @private
+		 */
+		public function setRootElement(value:IContainerFormattedElement):void
+		{
+			if (_rootElement != value)
+			{
+				if (_mouseEventManager)
+					_mouseEventManager.stopHitTests();
+				if (!value)
+					_mouseEventManager = null;
+				else if (!_mouseEventManager)
+				{
+					// Currently, the manager listens to all events itself.
+					// TODO: forward at least mouseOver and mouseDown events without
+					// causing side effects
+					_mouseEventManager = new FlowElementMouseEventManager(container, null);
+					//				[MouseEvent.MOUSE_DOWN, MouseEvent.MOUSE_UP, MouseEvent.MOUSE_MOVE, MouseEvent.MOUSE_DOWN, MouseEvent.MOUSE_OUT,
+					//				 KeyboardEvent.KEY_DOWN, KeyboardEvent.KEY_UP]);
+				}
+
+				clearCompositionResults();
+				detachContainer();
+				_rootElement = value;
+				_textFlowCache = null;
+				_textLength = 0;
+				_absoluteStart = -1;
+				attachContainer();
+				if (_rootElement)
+					formatChanged();
+
+				if (GlobalSettings.playerEnablesSpicyFeatures)
+					_container["needsSoftKeyboard"] = (interactionManager && interactionManager.editingMode == EditingMode.READ_WRITE);
+			}
+		}
+		
+		/** 
+		 * @copy org.apache.flex.textLayout.elements.TextFlow#interactionManager
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 * @see org.apache.flex.textLayout.elements.TextFlow#interactionManager
+		 */
+		
+		public function get interactionManager():ISelectionManager
+		{
+			return textFlow ? textFlow.interactionManager : null;
+		}
+		
+		
+		/** @private */
+		public function get uncomposedTextLength():int
+		{ return _uncomposedTextLength; }
+
+		/** @private */
+		public function get finalParcelStart():int
+		{ return _finalParcelStart; }
+		
+		/** @private */
+		public function set finalParcelStart(val:int):void
+		{ _finalParcelStart = val; }
+		
+		//--------------------------------------------------------------------------
+		//
+		//  Start and length
+		//
+		//--------------------------------------------------------------------------
+		
+		/** 
+		 * Returns the first character in the container. If this is not the first container in the flow,
+		 * this value is updated when the text is composed, that is when the IFlowComposer's <code>compose()</code> or 
+		 * <code>updateAllControllers()</code> methods are called.
+		 * 
+		 * @see org.apache.flex.textLayout.compose.IFlowComposer
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function get absoluteStart():int
+		{
+			if (_absoluteStart != -1)
+				return _absoluteStart;
+			
+			var rslt:int = 0;
+			var composer:IFlowComposer = flowComposer;
+			if (composer)
+			{
+				var stopIdx:int = composer.getControllerIndex(this);
+				if (stopIdx != 0)
+				{
+					var prevController:IContainerController = composer.getControllerAt(stopIdx-1);
+					rslt = prevController.absoluteStart + prevController.textLength;
+				}
+			}
+			_absoluteStart = rslt;
+			
+			return rslt;
+		}
+		
+		/** Returns the total number of characters in the container. This can include text that is not currently in view,
+		 * if the container is scrollable. This value is updated when the text is composed (when the IFlowComposer's <code>compose()</code> 
+		 * or <code>updateAllControllers()</code> methods are called).
+		 * 
+		 * @see org.apache.flex.textLayout.compose.IFlowComposer
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function get textLength():int
+		{
+			return _textLength;
+		}
+		/**
+		 *  @private
+		 * @flexjsignorecoercion org.apache.flex.textLayout.container.ContainerController
+		 */
+		public function setTextLengthOnly(numChars:int):void
+		{ 
+			if (_textLength != numChars)
+			{
+				_textLength = numChars; 
+				_uncomposedTextLength = 0;
+				// all following containers must have absoluteStart invalidated
+				if (_absoluteStart != -1)
+				{
+					var composer:IFlowComposer = flowComposer;
+					if (composer)
+					{
+						var idx:int = composer.getControllerIndex(this)+1;
+						while (idx < flowComposer.numControllers)
+						{
+							var controller:ContainerController = composer.getControllerAt(idx++) as ContainerController;
+							if (controller._absoluteStart == -1)
+								break;
+							controller._absoluteStart = -1;
+							controller._uncomposedTextLength = 0;
+						}
+					}
+				}
+			}
+		}
+		
+		/** @private */
+		public function setTextLength(numChars:int):void
+		{
+			CONFIG::debug { assert(numChars >= 0,"bad set textLength"); }
+			
+			// If its a scrollable container, and it is the last one, then it gets all the characters even though we might not have composed them all
+			var uncomposedTextLength:int = 0;
+			if (textFlow)
+			{
+				var verticalText:Boolean = effectiveBlockProgression == BlockProgression.RL;
+				var flowComposer:IFlowComposer = textFlow.flowComposer;
+				if (numChars != 0 && flowComposer.getControllerIndex(this) == flowComposer.numControllers - 1 &&
+					((!verticalText && _verticalScrollPolicy != ScrollPolicy.OFF)||
+						(verticalText && _horizontalScrollPolicy != ScrollPolicy.OFF)))
+				{
+					var containerAbsoluteStart:int = absoluteStart;
+					CONFIG::debug { assert(textFlow.textLength >= containerAbsoluteStart,"ContainerController.setTextLength bad absoluteStart"); }
+					uncomposedTextLength = textFlow.textLength-(numChars+containerAbsoluteStart);
+					// _composeCompleteRatio = (textFlow.textLength-containerAbsoluteStart) == numChars ? 1 : 1.1;
+					// var scaledContentHeight:Number = _composeCompleteRatio * _contentHeight;
+					// trace("composeCompleteRatio:",_composeCompleteRatio,"composedContentHeight",_contentHeight,"scaledContentHeight",scaledContentHeight,"textLength",textFlow.textLength,"numChars",numChars);
+					// include all remaining characters in this container when scroll enabled
+					numChars = textFlow.textLength - containerAbsoluteStart;
+				}
+			}
+			
+			// this call clears uncomposedTextLength - set it immediately afterwards
+			setTextLengthOnly(numChars); 	
+			_uncomposedTextLength = uncomposedTextLength;
+
+			CONFIG::debug
+			{
+				if (Debugging.debugOn && textFlow)
+					assert(Math.min(textFlow.textLength, absoluteStart)+_textLength <= textFlow.textLength, "container textLength may not extend past end of root element!");
+			}			
+		}
+		
+		/** 
+		 * Determines whether the container has text that requires composing. 
+		 *
+		 * @return 	true if the container requires composing.
+		 *
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function isDamaged():Boolean
+		{
+			return flowComposer.isPotentiallyDamaged(absoluteStart + _textLength);
+		}
+		
+		/** called whenever the container attributes are changed.  Mark computed attributes and columnstate as out of date. 
+		 * @private
+		 */
+		public override function formatChanged(notifyModelChanged:Boolean = true):void
+		{
+			//has no meaning in ContainerController, but we're inheriting from TextLayoutFormatBase
+			// The associated container, if there is one, inherits its container
+			// attributes from here. So we need to tell it that these attributes
+			// have changed.
+			_computedFormat = null;
+			invalidateContents();
+		}
+		
+		/** This gets called when an element has changed its style selection related attributes. This may happen because an
+		 * ancestor element changed it attributes.
+		 * @private 
+		 */		
+		public override function styleSelectorChanged():void
+		{
+			modelChanged(ModelChange.STYLE_SELECTOR_CHANGED,this,0,this._textLength);
+			_computedFormat = null;
+		}
+		
+		/** @private */
+		public function modelChanged(changeType:String, element:ContainerController, changeStart:int, changeLen:int, needNormalize:Boolean = true, bumpGeneration:Boolean = true):void
+		{
+			var tf:ITextFlow = _rootElement.getTextFlow();
+			if (tf)
+				tf.processModelChanged(changeType, element, absoluteStart+changeStart, changeLen, needNormalize, bumpGeneration);
+		}
+		
+		/** @private */
+		public function gatherVisibleLines(wmode:String, createShape:Boolean):void
+		{
+			CONFIG::debug { assert(_linesInView.length == 0,"gatherVisibleLines: bad _linesInView"); }
+			if (_textLength != 0)
+			{
+				// Similar computations also done in BaseCompose.getControllerVisibleBounds
+				var width:Number = _measureWidth ? _contentWidth : _compositionWidth;
+				var height:Number = _measureHeight ? _contentHeight : _compositionHeight;
+				var adjustX:Number = (wmode == BlockProgression.RL) ? _xScroll - width : _xScroll;
+				var adjustY:Number = _yScroll;
+				var scrollAdjustXTW:int = Twips.roundTo(adjustX);
+				var scrollAdjustYTW:int = Twips.roundTo(adjustY);
+				var scrollAdjustWidthTW:int = Twips.to(width);
+				var scrollAdjustHeightTW:int = Twips.to(height);
+				
+				var flowComposer:IFlowComposer = this.flowComposer;
+	
+				// Iterate over the lines in the container, setting the x and y positions and 
+				// adding them to the list to go into the container. Keep track of the width 
+				// and height of the actual text in the container.
+				var firstLine:int = flowComposer.findLineIndexAtPosition(absoluteStart);
+				var lastLine:int = flowComposer.findLineIndexAtPosition(absoluteStart + _textLength - 1);
+				
+				var curLine:ITextFlowLine;
+				var textLine:ITextLine;
+				var lineIndex:int;
+				var testRslt:*;
+				
+				//Use binary search when there is one single column
+				if(columnCount == 1)
+				{
+					// First just test the firstLine - normal unscrolled case
+					var testPos:int = firstLine;
+					curLine = flowComposer.getLineAt(testPos++);
+					while(curLine && curLine is ITextFlowTableBlock)
+						curLine = flowComposer.getLineAt(testPos++);
+					
+					testRslt = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null);
+					textLine = testRslt as ITextLine;
+					firstLine++;	// its been tested
+					if (textLine)
+					{
+						if (createShape)
+							curLine.createShape(wmode, textLine);
+						_linesInView.push(textLine);
+					}
+					else
+					{
+						var hi:int = lastLine;
+						while (firstLine <= hi)
+						{
+							var mid:int = (firstLine+hi)/2;
+							CONFIG::debug { assert(mid != 0,"ContainerController:gatherVisibleLines: bad mid"); }
+							curLine = flowComposer.getLineAt(mid);
+							testRslt = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null);
+							
+							if (testRslt && testRslt is ITextLine)
+							{
+								textLine = testRslt as ITextLine;
+								// note that we tested firstLine above so going to mid-1 is always valid
+								var tempLine:ITextFlowLine = flowComposer.getLineAt(mid-1);
+								if (!(testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, tempLine, null) is ITextLine))
+								{
+									// Got the start
+									if (createShape)
+										curLine.createShape(wmode, textLine);
+									_linesInView.push(textLine);
+									firstLine = mid+1;
+									break;
+								}
+								testRslt = -1;	// past the start
+							}
+							// need to deal with TextFlowTableBlocks
+							
+							if (testRslt < 0 || testRslt == 2)
+								hi = mid-1;
+							else
+								firstLine = mid+1;
+						}
+					}
+					
+					for (lineIndex = firstLine; lineIndex <= lastLine; lineIndex++)
+					{
+						curLine = flowComposer.getLineAt(lineIndex);
+						testRslt = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null);
+						
+						if(testRslt is TableBlockContainer)
+							continue;
+						textLine = testRslt as ITextLine;
+						if (!textLine)
+							break;
+		
+						if (createShape)
+							curLine.createShape(wmode, textLine);
+						_linesInView.push(textLine);
+					}
+				}
+				else //multiple columns
+				{
+					for (lineIndex = firstLine; lineIndex <= lastLine; lineIndex++)
+					{
+						curLine = flowComposer.getLineAt(lineIndex);	
+						if (curLine == null || curLine.controller != this)
+							continue;
+						
+						textLine = oldTestLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null);
+						if (textLine)
+						{
+							if (createShape)
+								curLine.createShape(wmode, textLine);
+							_linesInView.push(textLine);
+						}
+					}
+				}
+			}
+			_updateStart = absoluteStart;	// we collected all lines from the start of the container
+		}
+		
+		/** determines the shapechildren in the container and applies VJ. @private */
+		public function fillShapeChildren():void
+		{ 
+			if (_textLength == 0)
+				return;	// none				
+			
+			var wmode:String = effectiveBlockProgression;
+			
+			if (_linesInView.length == 0)		// no preexisting concpetion of what lines are in view: recalculate
+				gatherVisibleLines(wmode, true);
+			
+			// If scrolling is turned off, and flow is vertical, then we need to adjust the positions of all the lines. With
+			// scrolling turned on, we don't need to do this because the adjustment is done in the Player when the scrollRect
+			// is set up correctly. But with the scrollRect, we also get clipping, and if scrolling is turned off we want to
+			// have the clipping turned off as well. So in this case we do the adjustment manually so the scrollRect can be null.
+			// NOTE: similar adjustments are made in TextContainerManager
+			var adjustLines:Boolean = (wmode == BlockProgression.RL) &&
+				(_horizontalScrollPolicy == ScrollPolicy.OFF && 
+					_verticalScrollPolicy == ScrollPolicy.OFF);
+			
+			if (adjustLines)
+			{			
+				var width:Number = _measureWidth ? _contentWidth : _compositionWidth;
+//				var height:Number = _measureHeight ? _contentHeight : _compositionHeight;
+				var adjustX:Number = _xScroll - width;		// vertical text: blockProgression is rl
+				var adjustY:Number = _yScroll;
+				
+				// Iterate over the lines in the container, setting the x and y positions. Keep track of the width 
+				// and height of the actual text in the container.
+				if (adjustX != 0 || adjustY != 0)
+				{
+					for each (var textLine:ITextLine in _linesInView)
+					{
+						if (!textLine)
+							continue;
+						
+						if (adjustLines)
+						{
+							textLine.x -= adjustX;
+							textLine.y -= adjustY;
+						}
+					}
+					_contentLeft -= adjustX;
+					_contentTop  -= adjustY;
+				}
+			}
+			
+			
+		}
+		
+		//--------------------------------------------------------------------------
+		//
+		//  Scrolling
+		//
+		//--------------------------------------------------------------------------
+		
+		/** 
+		 * Specifies the horizontal scrolling policy, which you can set by assigning one of the constants of
+		 * the ScrollPolicy class: ON, OFF, or AUTO.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 * @see ScrollPolicy
+		 */
+		
+		public function get horizontalScrollPolicy():String
+		{
+			return _horizontalScrollPolicy;
+		}
+		public function set horizontalScrollPolicy(scrollPolicy:String):void
+		{
+			var newScrollPolicy:String = ScrollPolicy.scrollPolicyPropertyDefinition.setHelper(_horizontalScrollPolicy, scrollPolicy) as String;
+			
+			if (newScrollPolicy != _horizontalScrollPolicy)
+			{
+				_horizontalScrollPolicy = newScrollPolicy;
+				if (_horizontalScrollPolicy == ScrollPolicy.OFF)
+					horizontalScrollPosition = 0;
+				formatChanged();	// scroll policy affects composition
+			}
+		}
+		
+		/** @private */
+		public function checkScrollBounds():void
+		{
+			var newHeight:Number;
+			var visibleHeight:Number;
+			var measuring:Boolean;
+			
+			// If we've either grown the content past the composition bounds in the logical vertical direction, 
+			// or shrunk it down under the composition bounds, signal a scrolling change
+			// If we're measuring we never scroll.
+			if (effectiveBlockProgression == BlockProgression.RL)
+			{
+				newHeight = _contentWidth;
+				visibleHeight = compositionWidth;
+				measuring = _measureWidth;
+			}
+			else
+			{
+				newHeight = _contentHeight;
+				visibleHeight = compositionHeight;
+				measuring = _measureHeight;
+			}
+
+			// Called when the bounds have changed and they now exceed the composition area, to see if we need to attach a mouse wheel listener for scrolling
+			if (textFlow && !_minListenersAttached)
+			{
+				var needToScroll:Boolean = !measuring && newHeight > visibleHeight;
+				if (needToScroll != _mouseWheelListenerAttached)
+				{
+					if (_mouseWheelListenerAttached)
+						removeMouseWheelListener();
+					else
+						addMouseWheelListener();
+				}
+			}
+			
+		}
+		
+		/** Specifies the vertical scrolling policy, which you can set by assigning one of the constants of the ScrollPolicy
+		 * class: ON, OFF, or, AUTO.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see ScrollPolicy
+		 */
+		
+		public function get verticalScrollPolicy():String
+		{
+			return _verticalScrollPolicy;
+		}
+		public function set verticalScrollPolicy(scrollPolicy:String):void
+		{
+			var newScrollPolicy:String = ScrollPolicy.scrollPolicyPropertyDefinition.setHelper(_verticalScrollPolicy, scrollPolicy) as String;
+			if (newScrollPolicy != _verticalScrollPolicy)
+			{
+				_verticalScrollPolicy = newScrollPolicy;
+				if (_verticalScrollPolicy == ScrollPolicy.OFF)
+					verticalScrollPosition = 0;
+				formatChanged();	// scroll policy affects composition
+			}
+		}
+		
+		/** Specifies the current horizontal scroll location on the stage. The value specifies the number of
+		 * pixels from the left.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function get horizontalScrollPosition():Number
+		{
+			return _xScroll;
+		}
+		
+		public function set horizontalScrollPosition(x:Number):void
+		{
+			if (!_rootElement)
+				return;
+			
+			if (_horizontalScrollPolicy == ScrollPolicy.OFF)
+			{
+				_xScroll = 0;
+				return;
+			}
+			var oldScroll:Number = _xScroll;
+			var newScroll:Number = computeHorizontalScrollPosition(x,true);
+			
+			if (newScroll != oldScroll)
+			{	
+				_shapesInvalid = true;
+				_xScroll = newScroll;
+				updateForScroll(ScrollEventDirection.HORIZONTAL, newScroll - oldScroll);
+			}
+		}
+		
+		static private function pinValue(value:Number, minimum:Number, maximum:Number):Number
+		{
+			return Math.min(Math.max(value, minimum), maximum);						
+		}
+		
+		private function computeHorizontalScrollPosition(x:Number,okToCompose:Boolean):Number
+		{
+			var wmode:String = effectiveBlockProgression;
+			var curEstimatedWidth:Number = contentWidth;
+			var newScroll:Number = 0;
+			
+			if (curEstimatedWidth > _compositionWidth && !_measureWidth)
+			{
+				// Pin the lower and upper bounds of _x. If we're doing vertical text, then the right edge is 0 and the left edge is negative
+				// We may not have composed all the way to the indicated position. If not, force composition so that we can be sure we're at
+				// a legal position.
+				if (wmode == BlockProgression.RL)
+				{
+					newScroll = pinValue(x, _contentLeft + _compositionWidth, _contentLeft + curEstimatedWidth);
+					if (okToCompose && _uncomposedTextLength != 0 && newScroll != _xScroll)
+					{
+						// in order to compose have to set _xScroll
+						_xScroll = x;
+						if (_xScroll > _contentLeft + _contentWidth)
+							_xScroll = _contentLeft + _contentWidth;
+						flowComposer.composeToController(flowComposer.getControllerIndex(this));
+						newScroll = pinValue(x, _contentLeft + _compositionWidth, _contentLeft + _contentWidth);
+					}
+				}
+				else
+					newScroll = pinValue(x, _contentLeft, (_contentLeft + curEstimatedWidth) - _compositionWidth);
+			}
+			return newScroll;
+		}
+		
+		
+		/** Specifies the current vertical scroll location on the stage. The value specifies the number of 
+		 * pixels from the top.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function get verticalScrollPosition():Number
+		{
+			return _yScroll;
+		}
+		
+		public function set verticalScrollPosition(y:Number):void
+		{
+			if (!_rootElement)
+				return;
+			
+			if (_verticalScrollPolicy == ScrollPolicy.OFF)
+			{
+				_yScroll = 0;
+				return;
+			}
+			
+			var oldScroll:Number = _yScroll;
+			var newScroll:Number = computeVerticalScrollPosition(y,true);
+			
+			if (newScroll != oldScroll)
+			{			
+				_shapesInvalid = true;
+				_yScroll = newScroll;
+				updateForScroll(ScrollEventDirection.VERTICAL, newScroll - oldScroll);
+			}
+		}	
+		
+		private function computeVerticalScrollPosition(y:Number,okToCompose:Boolean):Number
+		{
+			var newScroll:Number = 0;
+			var curcontentHeight:Number = contentHeight;
+			var wmode:String = effectiveBlockProgression;
+			
+			// Only try to scroll if the content height is greater than the composition height, then there is text that is not visible to scroll to
+			if (curcontentHeight > _compositionHeight)
+			{
+				// new scroll value is somewhere between the topmost content, and the top of the last containerfull
+				newScroll = pinValue(y, _contentTop, _contentTop + (curcontentHeight - _compositionHeight));
+				
+				// if we're not composed to the end, compose further so we can scroll to it. Sets the scroll position and then 
+				// recomposes the container, which will compose through the end of the screenfull that starts at the requested position.
+				if (okToCompose && _uncomposedTextLength != 0 && wmode == BlockProgression.TB)
+				{
+					_yScroll = y;
+					if (_yScroll < _contentTop)
+						_yScroll = _contentTop;
+					flowComposer.composeToController(flowComposer.getControllerIndex(this));
+					newScroll = pinValue(y, _contentTop, _contentTop + (curcontentHeight - _compositionHeight));
+				}
+			}
+			return newScroll;
+		}
+		
+		/** 
+		 * Returns the area that the text occupies, as reflected by the last compose or update operation. 
+		 * The width and the height might be estimated, if the container is scrollable and the text exceeds the 
+		 * visible area.
+		 * 
+		 * @return describes the area that the text occupies.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 *
+		 * @see org.apache.flex.geom.Rectangle Rectangle
+		 */
+		public function getContentBounds():Rectangle
+		{
+			return new Rectangle(_contentLeft, _contentTop, contentWidth, contentHeight);
+		}
+		
+		/**
+		 * @private
+		 */
+		
+		public function get contentLeft():Number
+		{
+			return _contentLeft;
+		}
+		
+		/**
+		 * @private
+		 */
+		
+		public function get contentTop():Number
+		{
+			return _contentTop;
+		}
+		
+		/** @private */
+		public function computeScaledContentMeasure(measure:Number):Number
+		{
+			CONFIG::debug { assert(_finalParcelStart != -1 && _finalParcelStart >= this.absoluteStart && _finalParcelStart <= textFlow.textLength-_uncomposedTextLength,"computeScaledContentMeasure bad _finalParcelStart"); }
+			var charsInFinalParcel:int = textFlow.textLength-_finalParcelStart;
+			var composeCompleteRatio:Number = charsInFinalParcel / (charsInFinalParcel-_uncomposedTextLength);
+			// trace(measure*composeCompleteRatio,charsInFinalParcel,_uncomposedTextLength,measure,composeCompleteRatio);
+			return measure * composeCompleteRatio;
+		}
+		
+		/** 
+		 * @private
+		 *
+		 * Returns the vertical extent of the text. For horizontal text, it includes space taken for descenders on the last line. 
+		 * If not all the text is composed, this returns an estimated value based on how much text is already composed; the
+		 * more text that is composed, the more accurate s the estimate. To get a completely accurate value, recompose
+		 * with the rootElement's flowComposer before accessing contentHeight.
+		 * You can get the composed bounds of the text by getting the contentLeft, contentTop, contentWidth, contentHeight properties.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function get contentHeight():Number
+		{
+			if (_uncomposedTextLength == 0 || effectiveBlockProgression != BlockProgression.TB)
+				return _contentHeight;
+			return computeScaledContentMeasure(_contentHeight);
+		}
+		
+		/** 
+		 * @private
+		 *
+		 * Returns the horizontal extent of the text. For vertical text, it includes space taken for descenders on the last line. 
+		 * If not all the text is composed, this returns an estimated value based on how much text is already composed; the
+		 * more text that is composed, the more accurate is the estimate. To get a completely accurate value, recompose
+		 * with the rootElement's flowComposer before accessing contentWidth.
+		 * You can get the composed bounds of the text by getting the contentLeft, contentTop, contentWidth, contentHeight properties.
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function get contentWidth():Number
+		{			
+			if (_uncomposedTextLength == 0 || effectiveBlockProgression != BlockProgression.RL)
+				return _contentWidth;
+			return computeScaledContentMeasure(_contentWidth);
+		}
+		
+		/** @private */
+		public function setContentBounds(contentLeft:Number, contentTop:Number, contentWidth:Number, contentHeight:Number):void
+		{
+			_contentWidth = contentWidth;
+			_contentHeight = contentHeight;
+			_contentLeft = contentLeft;
+			_contentTop = contentTop;
+			checkScrollBounds();
+		}
+		
+		private function updateForScroll(direction:String, delta:Number):void
+		{
+			_linesInView.length = 0;		// zero out array of previously gathered up lines; its invalid because we've changed the visible area
+			var flowComposer:IFlowComposer = textFlow.flowComposer;
+			flowComposer.updateToController(flowComposer.getControllerIndex(this));
+			
+			attachTransparentBackgroundForHit(false);
+			
+			// notify client that we scrolled.
+			if (textFlow.hasEventListener(TextLayoutEvent.SCROLL))
+				textFlow.dispatchEvent(new ScrollEvent(TextLayoutEvent.SCROLL, false, false, direction, delta));
+			
+			//	trace("contentHeight", contentHeight, "contentWidth", contentWidth);
+			//	trace("contentHeight", contentHeight, "contentWidth", contentWidth);
+		}
+		
+		/** @private */
+		CONFIG::debug public function validateLines():void
+		{
+			if (!Debugging.containerLineValidation)
+				return;
+			
+			// Optimally we would recalculate which lines are in view and make sure they are parented
+			// to the container... but that causes side-effects (like creating TextLines) that affect
+			// regular execution. So we don't try that.
+			
+			// Check all the children of the container. Verify that adornments go before or after lines, that lines are at the expected z-order position
+			// And that extraneous lines are not parented to the container.
+			var firstLineIndex:int = -1;
+			var lastLineIndex:int = -1;
+			var numChildren:int = _container.numElements;
+			for (var childIndex:int = 0; childIndex < numChildren; ++childIndex)
+			{
+				var child:IChild = _container.getElementAt(childIndex);
+				if (_shapeChildren.indexOf(child) < 0 && (!_floatsInContainer || _floatsInContainer.indexOf(child) < 0))
+				{
+					// the very last thing can be the selection sprite
+					if (childIndex == numChildren - 1)
+						assert(child == getSelectionSprite(false),"expected selectionsprite but not found");
+					
+					assert(firstLineIndex == -1 || lastLineIndex == childIndex - 1, "Found adornment in the middle of ITextLine children");
+					continue;		// it's an adornment: skip
+				}
+				else 
+				{
+					if (firstLineIndex == -1)
+						firstLineIndex = childIndex;
+					lastLineIndex = childIndex;
+				}
+				if (_floatsInContainer && _floatsInContainer.indexOf(child) >= 0)	// it's a float
+					continue;
+				assert(child is ITextLine, "Expected child to be a ITextLine");
+				
+				// Check that the line comes after previous lines, in z-order
+				var lineIndex:int = _shapeChildren.indexOf(child);
+				if (lineIndex > 0)
+					assert(_container.getElementIndex(_shapeChildren[lineIndex - 1]) < childIndex, "Line is visible but not at expected z-order position: earlier line is later in z-order");
+				else if (lineIndex < 0)
+					assert(false, "Found line that should not be in the container (its not considered visible)");
+			}
+		}
+		
+		private function get containerScrollRectLeft():Number
+		{
+			var rslt:Number;
+			if (horizontalScrollPolicy == ScrollPolicy.OFF && verticalScrollPolicy == ScrollPolicy.OFF)
+				rslt = 0;
+			else
+				rslt= effectiveBlockProgression == BlockProgression.RL ? horizontalScrollPosition - compositionWidth : horizontalScrollPosition;
+			//CONFIG::debug { assert(container.scrollRect == null && rslt == 0 || int(rslt) == container.scrollRect.left,"Bad containerScrollRectLeft"); }
+			return rslt;
+		}
+		
+		private function get containerScrollRectRight():Number
+		{
+			var rslt:Number = containerScrollRectLeft+compositionWidth;
+			//CONFIG::debug { assert(container.scrollRect == null && rslt == compositionWidth || int(rslt) == container.scrollRect.right,"Bad containerScrollRectRight"); }
+			return rslt;
+		}
+		
+		private function get containerScrollRectTop():Number
+		{
+			var rslt:Number;
+			if (horizontalScrollPolicy == ScrollPolicy.OFF && verticalScrollPolicy == ScrollPolicy.OFF)
+				rslt = 0;
+			else
+				rslt = verticalScrollPosition;
+			//CONFIG::debug { assert(container.scrollRect == null && rslt == 0 || int(rslt) == container.scrollRect.top,"Bad containerScrollRectTop"); }
+			return rslt;
+		}
+		
+		private function get containerScrollRectBottom():Number
+		{
+			var rslt:Number = containerScrollRectTop+compositionHeight;
+			//CONFIG::debug { assert(container.scrollRect == null && rslt == compositionHeight || int(rslt) == container.scrollRect.bottom,"Bad containerScrollRectBottom"); }
+			return rslt;
+		}
+		
+		/** 
+		 * Scrolls so that the text range is visible in the container.
+		 *
+		 * @param activePosition	The end of the selection that is changed when you extend the selection. It can be
+		 * 	either the start or the end of the selection, expressed as an offset from the start of the text flow.
+		 * @param anchorPosition   	The stable end of the selection when you extend the selection. It can be either 
+		 * 	the start or the end of the selection.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function scrollToRange(activePosition:int,anchorPosition:int):void
+		{
+			
+			// return if we're not scrolling, or if it's not the last controller
+			if (!_hasScrollRect || !flowComposer || flowComposer.getControllerAt(flowComposer.numControllers-1) != this)
+				return;
+			
+			// clamp values to range absoluteStart,absoluteStart+_textLength
+			var controllerStart:int = absoluteStart;
+			var lastPosition:int = Math.min(controllerStart+_textLength, textFlow.textLength - 1);
+			activePosition = Math.max(controllerStart,Math.min(activePosition,lastPosition));
+			anchorPosition = Math.max(controllerStart,Math.min(anchorPosition,lastPosition));
+			
+			var verticalText:Boolean = effectiveBlockProgression == BlockProgression.RL;
+			var begPos:int = Math.min(activePosition,anchorPosition);
+			var endPos:int = Math.max(activePosition,anchorPosition);
+			
+			// is part of the selection in view?
+			var begLineIndex:int = flowComposer.findLineIndexAtPosition(begPos,(begPos == textFlow.textLength));
+			var endLineIndex:int = flowComposer.findLineIndexAtPosition(endPos,(endPos == textFlow.textLength));
+			
+			// no scrolling if any part of the selection is in view			
+			var scrollRectLeft:Number = containerScrollRectLeft;
+			var scrollRectTop:Number  = containerScrollRectTop;
+			var scrollRectRight:Number = containerScrollRectRight;
+			var scrollRectBottom:Number = containerScrollRectBottom;
+			
+			if (flowComposer.damageAbsoluteStart <= endPos)
+			{
+				endPos = Math.min(begPos + 100, endPos + 1);
+				flowComposer.composeToPosition(endPos);
+				begLineIndex = flowComposer.findLineIndexAtPosition(begPos,(begPos == textFlow.textLength));
+				endLineIndex = flowComposer.findLineIndexAtPosition(endPos,(endPos == textFlow.textLength));
+			}
+			var rect:Rectangle = rangeToRectangle(begPos, endPos, begLineIndex, endLineIndex);
+			if (rect) 
+			{
+//				var lastVisibleLine:ITextFlowLine;
+				var horizontalScrollOK:Boolean;
+				var verticalScrollOK:Boolean;
+				
+				// vertical scroll
+				if (verticalText) {					
+					// horizontal scroll
+					horizontalScrollOK = (rect.left < scrollRectLeft || rect.right > scrollRectLeft);
+					if (horizontalScrollOK)
+					{
+						if (rect.left < scrollRectLeft)
+							horizontalScrollPosition = rect.left + _compositionWidth;
+						if (rect.right > scrollRectRight)
+							horizontalScrollPosition = rect.right;
+					}
+					
+					// If we're showing a blinking insertion point, we need to scroll far enough that
+					// we can see the insertion point, and it comes just after the character.
+					if (rect.top < scrollRectTop)
+						verticalScrollPosition = rect.top;
+					if (activePosition == anchorPosition)
+						rect.bottom += 2;							
+					// vertical scroll
+					if (rect.bottom > scrollRectBottom)
+						verticalScrollPosition = rect.bottom - _compositionHeight;
+				}
+				else 
+				{
+					// vertical scroll
+					
+					// Don't scroll if the range extends both above and below
+					verticalScrollOK = (rect.top > scrollRectTop || rect.bottom < scrollRectBottom);
+					
+					// vertical scroll
+					if (verticalScrollOK)
+					{
+						if (rect.top < scrollRectTop)
+							verticalScrollPosition = rect.top;
+	
+						if (rect.bottom > scrollRectBottom)
+							verticalScrollPosition = rect.bottom - _compositionHeight;
+					}
+					
+					// horizontal scroll
+
+					// If we're showing a blinking insertion point, we need to scroll far enough to see the
+					// insertion point, and it comes up to the right
+					if (activePosition == anchorPosition)
+						rect.right += 2;
+
+					// Don't scroll if range extends both to the left and right
+					horizontalScrollOK = (rect.left > scrollRectLeft || rect.right < scrollRectRight);
+					if (horizontalScrollOK && rect.left < scrollRectLeft)
+						horizontalScrollPosition = rect.left - _compositionWidth / 2;
+					if (horizontalScrollOK && rect.right > scrollRectRight)
+						horizontalScrollPosition = rect.right - _compositionWidth / 2;
+				}
+			}
+		}		
+		
+		private function rangeToRectangle(start:int, end:int, startLineIndex:int, endLineIndex:int):Rectangle
+		{
+			var bbox:Rectangle;
+			var blockProgression:String = effectiveBlockProgression;		
+			var flowComposer:IFlowComposer = textFlow.flowComposer;
+			
+			if (!container || !flowComposer)
+				return null;
+			
+			if (startLineIndex == endLineIndex)
+			{
+				var line:ITextFlowLine = flowComposer.getLineAt(startLineIndex); 
+				if (line.isDamaged())
+					return null;
+				var textLine:ITextLine = line.getTextLine(true);
+				var paragraphStart:int = line.paragraph.getAbsoluteStart();
+				
+				var isTCY:Boolean = false;
+				if (blockProgression == BlockProgression.RL)
+				{
+					var leafElement:IFlowLeafElement = _rootElement.getTextFlow().findLeaf(start);
+					isTCY =  leafElement.getParentByType("TCYElement") != null;
+				}
+				
+				var minAtomIndex:int = textLine.atomCount;
+				var maxAtomIndex:int = 0;
+				if (start == end)
+				{
+					minAtomIndex = textLine.getAtomIndexAtCharIndex(start - paragraphStart);
+					maxAtomIndex = minAtomIndex;
+				}
+				else
+				{
+					var atomIndex:int;
+					var lastPosition:int = end - paragraphStart;
+					for (var pos:int = start - paragraphStart; pos < lastPosition; ++pos)
+					{
+						atomIndex = textLine.getAtomIndexAtCharIndex(pos);
+						if (atomIndex < minAtomIndex)
+							minAtomIndex = atomIndex;
+						if (atomIndex > maxAtomIndex)
+							maxAtomIndex = atomIndex;
+					}
+				}
+				bbox = atomToRectangle(minAtomIndex, line, textLine, blockProgression, isTCY);
+				if (minAtomIndex != maxAtomIndex)
+					bbox = bbox.union(atomToRectangle(maxAtomIndex, line, textLine, blockProgression, isTCY));
+			}
+			else
+			{
+				bbox = new Rectangle(_contentLeft, _contentTop, _contentWidth, _contentHeight);
+				var startLine:ITextFlowLine = flowComposer.getLineAt(startLineIndex); 
+				var endLine:ITextFlowLine = flowComposer.getLineAt(endLineIndex); 
+				if (blockProgression == BlockProgression.TB)
+				{
+					bbox.top = startLine.y;
+					bbox.bottom = endLine.y + endLine.textHeight;
+				}
+				else
+				{
+					bbox.right = startLine.x + startLine.textHeight;
+					bbox.left = endLine.x;
+				}
+			}
+			return bbox;
+		}
+		
+		
+		private function atomToRectangle(atomIdx:int, line:ITextFlowLine, textLine:ITextLine, blockProgression:String, isTCY:Boolean):Rectangle
+		{
+			var atomBounds:Rectangle;
+			CONFIG::debug { assert(atomIdx > -1, "How'd we get here?"); }
+			if (atomIdx > -1) 
+				atomBounds = textLine.getAtomBounds(atomIdx);
+			
+			// special handling for TCY - no line height adjustments TCY is perpendicular to the height direction
+			if (blockProgression == BlockProgression.RL)
+			{
+				if (isTCY)
+					return new Rectangle(line.x+atomBounds.x,line.y+atomBounds.y,atomBounds.width,atomBounds.height);
+				return new Rectangle(line.x, line.y + atomBounds.y, line.height, atomBounds.height);
+			}
+			return new Rectangle(line.x + atomBounds.x, line.y-line.height+line.ascent, atomBounds.width, line.height+textLine.descent);
+		}
+		
+		/**
+		 * @private
+		 */
+		
+		public function resetColumnState():void
+		{
+			if (_rootElement)
+				_columnState.updateInputs(effectiveBlockProgression, _rootElement.computedFormat.direction, this, _compositionWidth, _compositionHeight);
+		}
+		
+		/** 
+		 * Marks all the text in this container as needing composing. 
+		 *
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 */
+		
+		public function invalidateContents():void
+		{
+			if (textFlow)
+				textFlow.damage(absoluteStart, Math.min(_textLength, 1), FlowDamageType.GEOMETRY, false);
+		}
+		
+		/** @private */
+		private var _transparentBGX:Number;
+		/** @private */
+		private var _transparentBGY:Number;
+		/** @private */
+		private var _transparentBGWidth:Number;
+		/** @private */
+		private var _transparentBGHeight:Number;
+		
+		/** No mouse clicks or moves will be generated for the container unless it has a background covering its area.  Text Layout Framework
+		 * wants those events so that clicking on a container will select the text in it.  This code
+		 * adds or updates (on size change) that background for Sprite containers only. This may cause clients problems 
+		 * - definitely no hits is a problem - add this code to explore the issues - expect feedback.  
+		 * We may have to make this configurable. @private */
+		
+		
+		public function attachTransparentBackgroundForHit(justClear:Boolean):void
+		{
+			if ((_minListenersAttached || _mouseWheelListenerAttached) && attachTransparentBackground)
+			{
+				var s:IParentIUIBase = _container;
+                if (justClear)
+                {
+//TODO deal with graphics
+//                    s.graphics.clear();
+                    CONFIG::debug { Debugging.traceFTECall(null,s,"clearTransparentBackground()"); }
+                    _transparentBGX = _transparentBGY = _transparentBGWidth = _transparentBGHeight = NaN;
+                }
+                else
+                {
+                    var bgwidth:Number = _measureWidth ? _contentWidth : _compositionWidth;
+                    var bgheight:Number = _measureHeight ? _contentHeight : _compositionHeight;
+
+                    var adjustHorizontalScroll:Boolean = effectiveBlockProgression == BlockProgression.RL && _horizontalScrollPolicy != ScrollPolicy.OFF;
+                    var bgx:Number = adjustHorizontalScroll ? _xScroll - bgwidth : _xScroll;
+                    var bgy:Number = _yScroll;
+
+                    CONFIG::debug { assert(!isNaN(bgx) && !isNaN(bgy) && !isNaN(bgwidth) && ! isNaN(bgheight),"Bad background rectangle"); }
+
+                    if (bgx != _transparentBGX || bgy != _transparentBGY || bgwidth != _transparentBGWidth || bgheight != _transparentBGHeight)
+                    {
+//TODO graphics
+//                        s.graphics.clear();
+//                        CONFIG::debug { Debugging.traceFTECall(null,s,"clearTransparentBackground()"); }
+//                        if (bgwidth != 0 && bgheight != 0 )
+//                        {
+//                            s.graphics.beginFill(0, 0);
+//                            s.graphics.drawRect(bgx, bgy, bgwidth, bgheight);
+//                            s.graphics.endFill();
+//                            CONFIG::debug { Debugging.traceFTECall(null,s,"drawTransparentBackground",bgx, bgy, bgwidth, bgheight); }
+//                        }
+                        _transparentBGX = bgx;
+                        _transparentBGY = bgy;
+                        _transparentBGWidth = bgwidth;
+                        _transparentBGHeight = bgheight;
+                    }
+                }
+			}
+		}
+		
+		/** @private */
+		public	function interactionManagerChanged(newInteractionManager:ISelectionManager):void
+		{
+			if (!newInteractionManager)
+				detachContainer();
+			attachContainer();
+			checkScrollBounds();
+			// Need to forward whether the Ctrl key is needed to have
+			// hit-tested FlowElements emit events
+			if (_mouseEventManager)
+				_mouseEventManager.needsCtrlKey	= 
+					(interactionManager != null && interactionManager.editingMode == EditingMode.READ_WRITE);
+
+			// We have to tell the Player to bring up the soft keyboard on a
+			// keyboard edit gesture. Note that needsSoftKeyboard is new with 10.2, so 
+			// have to check for it. This is a change to the container, but unavoidable
+			if (GlobalSettings.playerEnablesSpicyFeatures)
+				_container["needsSoftKeyboard"] = (interactionManager && interactionManager.editingMode == EditingMode.READ_WRITE);
+		}
+		
+		//--------------------------------------------------------------------------
+		//  Event handlers for editing
+		//  Listeners are attached on first compose
+		//--------------------------------------------------------------------------
+		
+		/** @private */
+		public function attachContainer():void
+		{
+			if (!_minListenersAttached && textFlow && textFlow.interactionManager)
+			{
+				_minListenersAttached = true;
+				
+                _container.addEventListener(FocusEvent.FOCUS_IN, requiredFocusInHandler);
+                _container.addEventListener(MouseEvent.MOUSE_OVER, requiredMouseOverHandler);
+
+                attachTransparentBackgroundForHit(false);
+
+//TODO deal with reference to stage
+                // If the container already has focus, we have to attach all listeners
+//                if (_container.stage && _container.stage.focus == _container)
+//                    attachAllListeners();
+			}
+		}
+		
+		/** @private */
+		public function attachInteractionHandlers():void
+		{
+			// the receiver is either this or another class that is going to handle the methods.
+			var receiver:IInteractionEventHandler = getInteractionHandler();
+			
+			// the required handlers are implemented here and forwarded to the receiver
+			_container.addEventListener(MouseEvent.MOUSE_DOWN, requiredMouseDownHandler);
+			_container.addEventListener(FocusEvent.FOCUS_OUT, requiredFocusOutHandler);
+			_container.addEventListener(MouseEvent.DOUBLE_CLICK, receiver.mouseDoubleClickHandler);
+			_container.addEventListener(ActivateEvent.ACTIVATE, receiver.activateHandler);
+			_container.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, receiver.focusChangeHandler);
+			_container.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, receiver.focusChangeHandler);
+			_container.addEventListener(TextEvent.TEXT_INPUT, receiver.textInputHandler);
+			_container.addEventListener(MouseEvent.MOUSE_OUT, receiver.mouseOutHandler);
+			addMouseWheelListener();
+			_container.addEventListener(ActivateEvent.DEACTIVATE, receiver.deactivateHandler);
+			// attach by literal event name to avoid Argo dependency
+			// normally this would be IMEEvent.START_COMPOSITION
+			_container.addEventListener("imeStartComposition", receiver.imeStartCompositionHandler);
+			
+//TODO context menu
+//			if (_container.contextMenu)
+//				_container.contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT, receiver.menuSelectHandler);
+			_container.addEventListener(EditEvent.COPY, receiver.editHandler);
+			_container.addEventListener(SelectionEvent.SELECT_ALL, receiver.editHandler);
+			_container.addEventListener(EditEvent.CUT, receiver.editHandler);
+			_container.addEventListener(EditEvent.PASTE, receiver.editHandler);
+			_container.addEventListener(EditEvent.CLEAR, receiver.editHandler);
+		}
+		
+		/** @private */
+		public function removeInteractionHandlers():void
+		{
+			var receiver:IInteractionEventHandler = getInteractionHandler();
+			
+			_container.removeEventListener(MouseEvent.MOUSE_DOWN, requiredMouseDownHandler);
+			_container.removeEventListener(FocusEvent.FOCUS_OUT, requiredFocusOutHandler);
+			_container.removeEventListener(MouseEvent.DOUBLE_CLICK, receiver.mouseDoubleClickHandler);
+			_container.removeEventListener(ActivateEvent.ACTIVATE, receiver.activateHandler);
+			_container.removeEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, receiver.focusChangeHandler);
+			_container.removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, receiver.focusChangeHandler);
+			_container.removeEventListener(TextEvent.TEXT_INPUT, receiver.textInputHandler);
+			_container.removeEventListener(MouseEvent.MOUSE_OUT, receiver.mouseOutHandler);
+			removeMouseWheelListener();
+			_container.removeEventListener(ActivateEvent.DEACTIVATE, receiver.deactivateHandler);
+			//	_container.removeEventListener(IMEEvent.IME_START_COMPOSITION, receiver.imeStartCompositionHandler); 
+			// attach by literal event name to avoid Argo dependency
+			_container.removeEventListener("imeStartComposition", receiver.imeStartCompositionHandler); 
+			
+//TODO context menu
+//			if (_container.contextMenu) 
+//				_container.contextMenu.removeEventListener(ContextMenuEvent.MENU_SELECT, receiver.menuSelectHandler);
+			_container.removeEventListener(EditEvent.COPY, receiver.editHandler); 
+			_container.removeEventListener(SelectionEvent.SELECT_ALL, receiver.editHandler);
+			_container.removeEventListener(EditEvent.CUT, receiver.editHandler);
+			_container.removeEventListener(EditEvent.PASTE, receiver.editHandler);
+			_container.removeEventListener(EditEvent.CLEAR, receiver.editHandler);
+			
+			clearSelectHandlers();	
+		}
+		
+		/** @private */
+		private function detachContainer():void
+		{
+			if (_minListenersAttached)
+			{
+                _container.removeEventListener(FocusEvent.FOCUS_IN, requiredFocusInHandler);
+                _container.removeEventListener(MouseEvent.MOUSE_OVER, requiredMouseOverHandler);
+
+                if(_allListenersAttached)
+                {
+                    removeInteractionHandlers();
+                    removeContextMenu();
+
+                    attachTransparentBackgroundForHit(true);
+                    _allListenersAttached = false;
+                }
+				_minListenersAttached = false;
+			}
+			removeMouseWheelListener();
+		}
+		
+		
+		private function attachAllListeners():void
+		{	
+			if (!_allListenersAttached && textFlow && textFlow.interactionManager)
+			{
+				CONFIG::debug { assert(_minListenersAttached,"Bad call to attachAllListeners - won't detach"); }
+				_allListenersAttached = true;
+                attachContextMenu();
+                attachInteractionHandlers();
+			}
+		}
+		
+		/** @private */
+		public function addMouseWheelListener():void
+		{
+			if (!_mouseWheelListenerAttached)
+			{
+				_container.addEventListener(MouseEvent.WHEEL, getInteractionHandler().mouseWheelHandler);
+				_mouseWheelListenerAttached = true;
+			}
+		}
+		
+		/** @private */
+		public function removeMouseWheelListener():void
+		{
+			if (_mouseWheelListenerAttached)
+			{
+				_container.removeEventListener(MouseEvent.WHEEL, getInteractionHandler().mouseWheelHandler);
+				_mouseWheelListenerAttached = false;
+			}
+		}
+		
+		/** @private */
+		public function attachContextMenu():void
+		{
+//TODO deal with context menu
+//			_container.contextMenu = createContextMenu();
+		}
+		
+		/** @private */
+		public function removeContextMenu():void
+		{
+//TODO deal with context menu
+//			 _container.contextMenu = null;
+		}
+
+		
+		/** @private  
+		 *
+		 * Shared so that TextContainerManager can create the same ContextMenu. 
+		 */
+		static public function createDefaultContextMenu():ContextMenu
+		{
+			var contextMenu:ContextMenu = new ContextMenu();
+			contextMenu.clipboardMenu = true;
+			contextMenu.clipboardItems.clear = true;
+			contextMenu.clipboardItems.copy = true;
+			contextMenu.clipboardItems.cut = true;
+			contextMenu.clipboardItems.paste = true;
+			contextMenu.clipboardItems.selectAll = true;
+			return contextMenu;
+		}
+		
+		/** 
+		 * Creates a context menu for the ContainerController. 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>
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see flash.ui.ContextMenu ContextMenu
+		 */
+		protected function createContextMenu():ContextMenu
+		{
+			return createDefaultContextMenu();
+		}
+		
+        public function dispose():void
+        {
+            stopMouseSelectionScrolling();
+            clearSelectionShapes();
+            setRootElement(null);
+        }
+
+        private function stopMouseSelectionScrolling(containerRoot:IEventDispatcher = null):void
+        {
+            if(_scrollTimer)
+            {
+                _scrollTimer.stop();
+                _scrollTimer.removeEventListener(Timer.TIMER, scrollTimerHandler);
+
+//TODO fix this for FlexJS
+//                if(!containerRoot)
+//                {
+//                    containerRoot = getContainerRoot();
+//                }
+
+                if(containerRoot)
+                {
+                    containerRoot.removeEventListener(MouseEvent.MOUSE_UP, scrollTimerHandler);
+                }
+
+                _scrollTimer = null;
+            }
+        }
+		/** @private */
+		public function scrollTimerHandler(event:Event):void
+		{
+			// trace("BEGIN scrollTimerHandler");
+			if (!_scrollTimer)
+				return;
+			
+			// shut it down if not in this container
+			if (textFlow.interactionManager == null || textFlow.interactionManager.activePosition < absoluteStart || textFlow.interactionManager.activePosition > absoluteStart+textLength)
+				event = null;
+			
+			
+			// We're listening for MOUSE_UP so we can cancel autoscrolling
+			if (event is MouseEvent)
+			{
+				stopMouseSelectionScrolling(event.currentTarget as IEventDispatcher);
+//				CONFIG::debug { assert(_container.stage ==  null || getContainerRoot() == event.currentTarget,"scrollTimerHandler bad target"); }
+			}
+			else if (!event)
+			{
+                stopMouseSelectionScrolling();
+			}
+//TODO deal with stage
+//			else if (_container.stage)
+//			{
+//				var containerPoint:Point = new Point(_container.stage.mouseX, _container.stage.mouseY);
+//				containerPoint = _container.globalToLocal(containerPoint);
+//				var scrollChange:int = autoScrollIfNecessaryInternal(containerPoint);
+//				if (scrollChange != 0 && interactionManager)		// force selection update if we actually scrolled and we have a selection manager
+//				{
+//					var mouseEvent:MouseEvent = new PsuedoMouseEvent(MouseEvent.MOUSE_MOVE,false,false,_container.stage.mouseX, _container.stage.mouseY,_container.stage,false,false,false,true);
+//					var stashedScrollTimer:Timer = _scrollTimer;	
+//					try
+//					{
+//						_scrollTimer =  null;
+//						interactionManager.mouseMoveHandler(mouseEvent);
+//					}
+//					catch (e:Error)
+//					{
+//						throw(e);
+//					}
+//					finally
+//					{
+//						_scrollTimer = stashedScrollTimer;
+//					}
+//				}
+//			}
+			// trace("AFTER scrollTimerHandler");
+		}
+		
+		/** 
+		 * Handle a scroll event during a "drag" selection. 
+		 *
+		 * @param mouseX	The horizontal position of the mouse cursor on the stage.
+		 * @param mouseY	The vertical position of the mouse cursor  on the stage.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function autoScrollIfNecessary(mouseX:int, mouseY:int):void
+		{ 			
+			if (flowComposer.getControllerAt(flowComposer.numControllers-1) != this)
+			{
+				var verticalText:Boolean = (effectiveBlockProgression == BlockProgression.RL);
+				var lastController:IContainerController = flowComposer.getControllerAt(flowComposer.numControllers - 1);
+				if ((verticalText && _horizontalScrollPolicy == ScrollPolicy.OFF) ||
+					(!verticalText && _verticalScrollPolicy == ScrollPolicy.OFF))
+					return;
+				var r:Rectangle = DisplayUtils.getScreenBoundingRect(lastController.container);// lastController.container.getBounds(_container.stage);
+				if (verticalText)
+				{
+					if (mouseY >= r.top && mouseY <= r.bottom)
+						lastController.autoScrollIfNecessary(mouseX, mouseY);
+				}
+				else
+				{
+					if (mouseX >= r.left && mouseX <= r.right)
+						lastController.autoScrollIfNecessary(mouseX, mouseY);
+				}
+			}
+			
+			// even if not the last container - may scroll if there are explicit linebreaks
+			if (!_hasScrollRect)
+				return;
+			var containerPoint:Point = new Point(mouseX, mouseY);
+			containerPoint = PointUtils.globalToLocal(containerPoint, _container);// _container.globalToLocal(containerPoint); 			
+			autoScrollIfNecessaryInternal(containerPoint);
+		}
+		
+		/** 
+		 * Handle a scroll event during a "drag" selection. 
+		 *
+		 * @param mouseX	The horizontal position of the mouse cursor on the stage.
+		 * @param mouseY	The vertical position of the mouse cursor  on the stage.
+		 * @returns positive number if scroll went forward in reading order, negative number if it went backwards, and 0 if no scroll
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		private function autoScrollIfNecessaryInternal(extreme:Point):int
+		{
+			CONFIG::debug 
+			{ 
+				assert(_hasScrollRect, "internal scrolling function called on non-scrollable container");
+			}
+			
+			
+			var scrollDirection:int = 0;
+			
+			if (extreme.y - containerScrollRectBottom > 0) {
+				verticalScrollPosition += textFlow.configuration.scrollDragPixels;
+				scrollDirection = 1;
+			}
+			else if (extreme.y - containerScrollRectTop < 0) {
+				verticalScrollPosition -= textFlow.configuration.scrollDragPixels;
+				scrollDirection = -1;
+			}
+			
+			if (extreme.x - containerScrollRectRight > 0) {
+				horizontalScrollPosition += textFlow.configuration.scrollDragPixels;
+				scrollDirection = -1;
+			}
+			else if (extreme.x - containerScrollRectLeft < 0) {
+				horizontalScrollPosition -= textFlow.configuration.scrollDragPixels;
+				scrollDirection = 1;
+			}
+			
+			// we need a timer so that the mouse doesn't have to continue moving when the mouse is outside the content area
+			if (scrollDirection != 0 && !_scrollTimer) 
+			{
+				_scrollTimer = new Timer(textFlow.configuration.scrollDragDelay);	// 35 ms is the default auto-repeat interval for ScrollBars.
+				_scrollTimer.addEventListener(Timer.TIMER, scrollTimerHandler, false, 0, true);
+//TODO deal with platform specific events
+//				if (getContainerRoot())
+//				{
+//					getContainerRoot().addEventListener(MouseEvent.MOUSE_UP, scrollTimerHandler, false, 0, true);
+//					beginMouseCapture(); // TELL CLIENTS WE WANT mouseUpSomewhere events
+//				}
+				_scrollTimer.start();
+			}
+			
+			return scrollDirection;
+		}
+		
+		/** @private */ 
+		public function getFirstVisibleLine():ITextFlowLine
+		{ return _shapeChildren.length ? _shapeChildren[0].userData : null; }
+		/** @private */
+		public function getLastVisibleLine():ITextFlowLine
+		{ return _shapeChildren.length ? _shapeChildren[_shapeChildren.length-1].userData : null; }
+		
+		/** 
+		 * Figure out the scroll distance required to scroll up or down by the specified number of lines.
+		 * Negative numbers scroll upward, bringing more of the top of the TextFlow into view. Positive numbers 
+		 * scroll downward, bringing the next line from the bottom into full view.
+		 * 
+		 * <p>When scrolling up, for example, the method makes the next line fully visible. If the next line is partially
+		 * obscured and the number of lines specified is 1, the partially obscured line becomes fully visible.</p>
+		 *
+		 * @param nLines	The number of lines to scroll.
+		 *
+		 * @return 	the delta amount of space to scroll
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		
+		public function getScrollDelta(numLines:int):Number
+		{
+			var flowComposer:IFlowComposer = textFlow.flowComposer;
+			
+			if (flowComposer.numLines == 0)
+				return 0;
+			
+			// Now we want to calculate the top & bottom lines within the scrollRect. It's ok if they're just partially
+			// visible. Once we determine these lines, we figure out how much we need to scroll in order to bring the
+			// lines completely into view.
+			
+			var firstVisibleLine:ITextFlowLine = getFirstVisibleLine();
+			if (!firstVisibleLine)
+				return 0;
+
+			var lastVisibleLine:ITextFlowLine = getLastVisibleLine();
+			CONFIG::debug { assert(lastVisibleLine != null,"Expect lastVisibleLine when there is a firstVisibleLine"); }
+				
+			// trace("    // findFirstAndLastVisibleLine ",flowComposer.findLineIndexAtPosition(firstVisibleLine.absoluteStart),flowComposer.findLineIndexAtPosition(lastVisibleLine.absoluteStart));
+			
+			var newLineIndex:int;
+			var lineIndex:int;
+			if (numLines > 0) 
+			{
+				lineIndex = flowComposer.findLineIndexAtPosition(lastVisibleLine.absoluteStart);
+				// If the last visible line is only partly visible, don't count it as visible. But make sure it overlaps by
+				// at least two pixels, otherwise it doesn't look like its clipped.
+
+				var lastTextLine:ITextLine = lastVisibleLine.getTextLine(true);
+				if (effectiveBlockProgression == BlockProgression.TB)
+				{
+					if ((lastTextLine.y + lastTextLine.descent) - containerScrollRectBottom > 2)
+						--lineIndex;
+				}
+				else if (containerScrollRectLeft - (lastTextLine.x - lastTextLine.descent)  > 2)
+					--lineIndex;
+				
+				// if we hit the end, force composition so that we get more lines - I picked a random amount to scroll forward, if its not enough, it will keep going
+				while (lineIndex + numLines > flowComposer.numLines - 1 && flowComposer.damageAbsoluteStart < textFlow.textLength)	
+				{
+					var previousDamageStart:int = flowComposer.damageAbsoluteStart;
+					flowComposer.composeToPosition(flowComposer.damageAbsoluteStart + 1000);
+					// if we've made no progress, abort
+					if (flowComposer.damageAbsoluteStart == previousDamageStart)
+						return 0;
+				}
+				newLineIndex = Math.min(flowComposer.numLines-1, lineIndex + numLines);
+			}
+			if (numLines < 0) 
+			{
+				lineIndex = flowComposer.findLineIndexAtPosition(firstVisibleLine.absoluteStart);
+				
+				// If the first visible line is only partly visible, don't count it as visible. But make sure it overlaps by
+				// at least two pixels, otherwise it doesn't look like its clipped.
+				if (effectiveBlockProgression == BlockProgression.TB)
+				{
+					if (firstVisibleLine.y + 2 < containerScrollRectTop)
+						++lineIndex;
+				}
+				else if (firstVisibleLine.x + firstVisibleLine.ascent > containerScrollRectRight + 2)
+					++lineIndex;
+				
+				newLineIndex = Math.max(0, lineIndex + numLines);
+			}
+			
+			var line:ITextFlowLine = flowComposer.getLineAt(newLineIndex);
+			if (line.absoluteStart < absoluteStart)		// don't scroll past the start of this controller -- previous text is in previous controller
+				return 0;
+			if (line.validity != "valid")
+			{
+				var leaf:IFlowLeafElement = textFlow.findLeaf(line.absoluteStart);
+				var paragraph:IParagraphElement = leaf.getParagraph();
+				textFlow.flowComposer.composeToPosition(paragraph.getAbsoluteStart() + paragraph.textLength);
+				line = flowComposer.getLineAt(newLineIndex);
+				CONFIG::debug { assert(line.validity == "valid", "expected valid line after recomposing"); }
+			}
+			
+			var verticalText:Boolean = effectiveBlockProgression == BlockProgression.RL;
+			
+			var newScrollPosition:Number;
+			if (verticalText)
+			{
+				
+				newScrollPosition =  numLines < 0 ? line.x + line.textHeight : line.x - line.descent + _compositionWidth;
+				return newScrollPosition - horizontalScrollPosition;
+			}
+			
+			newScrollPosition = numLines < 0 ? line.y : line.y + line.textHeight - _compositionHeight;
+			return newScrollPosition - verticalScrollPosition;
+		}
+		
+		/** 
+		 * Processes the <code>MouseEvent.MOUSE_OVER</code> event when the client manages events. 
+		 *
+		 * @param event The MouseEvent object.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 *
+		 * @see org.apache.flex.events.MouseEvent#MOUSE_OVER MouseEvent.MOUSE_OVER
+		 */
+		
+		public function mouseOverHandler(event:MouseEvent):void
+		{
+			if (interactionManager && !event.isDefaultPrevented())
+				interactionManager.mouseOverHandler(event);
+		}
+		
+		/** @private Does required mouseOver handling.  Calls mouseOverHandler.  @see #mouseOverHandler */
+		public function requiredMouseOverHandler(event:MouseEvent):void
+		{
+			attachAllListeners();
+			getInteractionHandler().mouseOverHandler(event);
+		}
+		
+		/** Processes the <code>MouseEvent.MOUSE_OUT</code> event when the client manages events.
+		 *
+		 * @param event The MouseEvent object.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.events.MouseEvent#MOUSE_OUT MouseEvent.MOUSE_OUT
+		 */				
+		public function mouseOutHandler(event:MouseEvent):void
+		{
+			if (interactionManager && !event.isDefaultPrevented())
+				interactionManager.mouseOutHandler(event);
+		}
+		
+		/** Processes the <code>MouseEvent.MOUSE_WHEEL</code> event when the client manages events.
+		 *
+		 * @param event The MouseEvent object.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.events.MouseEvent#MOUSE_WHEEL MouseEvent.MOUSE_WHEEL
+		 */
+		public function mouseWheelHandler(event:MouseEvent):void
+		{
+			if (event.isDefaultPrevented())
+				return;
+			
+			// Do the scroll and call preventDefault only if the there is enough text to scroll. Otherwise
+			// we let the event bubble up and cause scrolling at the next level up in the client's container hierarchy.
+			var verticalText:Boolean = effectiveBlockProgression == BlockProgression.RL;
+			if (verticalText)
+			{
+				if (contentWidth > _compositionWidth && !_measureWidth)
+				{
+					horizontalScrollPosition += event.delta * textFlow.configuration.scrollMouseWheelMultiplier;
+					event.preventDefault();
+				}
+			}
+			else if (contentHeight > _compositionHeight && !_measureHeight)
+			{
+				verticalScrollPosition -= event.delta * textFlow.configuration.scrollMouseWheelMultiplier;
+				event.preventDefault();
+			}
+		}
+		
+		
+		/** Processes the <code>MouseEvent.MOUSE_DOWN</code> event when the client manages events. 
+		 *
+		 * @param event The MouseEvent object.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.events.MouseEvent#MOUSE_DOWN MouseEvent.MOUSE_DOWN
+		 */
+		
+		public function mouseDownHandler(event:MouseEvent):void
+		{
+			if (interactionManager && !event.isDefaultPrevented())
+			{
+				interactionManager.mouseDownHandler(event);
+				// grab the focus - alternative is to listen to keyevents on the Application
+				// is this necessary?
+				if ( interactionManager.hasSelection())
+					setFocus();
+			}
+		}
+		
+		/** @private Does required mouseDown handling.  Calls mouseDownHandler.  @see #mouseDownHandler */
+		public function requiredMouseDownHandler(event:MouseEvent):void
+		{
+			if (!_selectListenersAttached)
+			{
+//TODO fix root events
+//				var containerRoot:DisplayObject = getContainerRoot();
+//				if (containerRoot)
+//				{
+//					containerRoot.addEventListener(MouseEvent.MOUSE_MOVE, rootMouseMoveHandler, false, 0, true); 
+//					containerRoot.addEventListener(MouseEvent.MOUSE_UP,   rootMouseUpHandler, false, 0, true);
+//					
+//					beginMouseCapture(); // TELL CLIENTS THAT WE WANT moueUpSomewhere EVENTS
+//					
+//					
+//					_selectListenersAttached = true;
+//				}
+			}
+			getInteractionHandler().mouseDownHandler(event); 
+		}
+		
+		/** 
+		 * Processes the <code>MouseEvent.MOUSE_UP</code> event when the client manages events.
+		 *
+		 * @param event The MouseEvent object.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.events.MouseEvent#MOUSE_UP MouseEvent.MOUSE_UP
+		 *
+		 */
+		public function mouseUpHandler(event:MouseEvent):void
+		{
+			if (interactionManager && event && !event.isDefaultPrevented())
+			{
+				interactionManager.mouseUpHandler(event);
+			}
+		}		
+		
+		/** @private */
+		public function rootMouseUpHandler(event:MouseEvent):void
+		{
+			clearSelectHandlers();
+			getInteractionHandler().mouseUpHandler(event);
+		}
+		
+		
+		private function clearSelectHandlers():void
+		{	
+			if (_selectListenersAttached)
+			{
+//TODO fix root events
+//				CONFIG::debug { assert(getContainerRoot() != null,"No container root"); }
+//				getContainerRoot().removeEventListener(MouseEvent.MOUSE_MOVE, rootMouseMoveHandler); 					
+//				getContainerRoot().removeEventListener(MouseEvent.MOUSE_UP,   rootMouseUpHandler);
+//				endMouseCapture(); // TELL CLIENTS WE NO LONGER WANT mouseUpSomewhere EVENTS
+//				_selectListenersAttached = false;
+			}
+		}
+		
+		/** 
+		 * 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
+		{
+			// trace("BEGIN MOUSECAPTURE");
+			var sandboxManager:ISandboxSupport = getInteractionHandler() as ISandboxSupport;
+			if (sandboxManager && sandboxManager != this)
+				sandboxManager.beginMouseCapture();
+		}
+		/** 
+		 * 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
+		{
+			// trace("END MOUSECAPTURE");
+			var sandboxManager:ISandboxSupport = getInteractionHandler() as ISandboxSupport;
+			if (sandboxManager && sandboxManager != this)
+				sandboxManager.endMouseCapture();
+		}
+		/** 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(event:Event):void
+		{
+			rootMouseUpHandler(null);
+			scrollTimerHandler(null);
+		}
+		/** 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(event:Event):void
+		{
+			return;	// do nothing right now
+		}
+		
+		// What'd I hit???
+		private function hitOnMyFlowExceptLastContainer(event:MouseEvent):Boolean
+		{
+			if (event.target is ITextLine)
+			{
+				var tfl:ITextFlowLine = ITextLine(event.target).userData as ITextFlowLine;
+				if (tfl)
+				{
+					var para:IParagraphElement = tfl.paragraph;
+					if(para.getTextFlow() == textFlow)
+						return true;
+				}
+			}
+//TODO how to properly check

<TRUNCATED>

Mime
View raw message