Merge branch 'develop' into tables

* develop:
  the original change broke a few tests.  I think this one is better
diff --git a/automation_tests/src/MinimalTestsSuite.as b/automation_tests/src/MinimalTestsSuite.as
index 7f4b558..5f413ce 100644
--- a/automation_tests/src/MinimalTestsSuite.as
+++ b/automation_tests/src/MinimalTestsSuite.as
@@ -19,13 +19,13 @@
 package
 {
 
-    import UnitTest.Tests.ContainerTypeTest;
+    import UnitTest.Tests.FloatTest;
 
     [Suite]
     [RunWith("org.flexunit.runners.Suite")]
     public dynamic class MinimalTestsSuite
     {
-        public var containerTypeTest:ContainerTypeTest;
+        public var floatTest:FloatTest;
     }
 
 }
diff --git a/build.properties b/build.properties
index 925d779..fb3b6f7 100644
--- a/build.properties
+++ b/build.properties
@@ -27,7 +27,8 @@
 src.debug = on
 
 forkValue = true
-minimalTests = false
+minimalTests = true
+flexdebug = false
 
 # JVM options for <compc> and <mxmlc> tasks
 jvm.args = ${local.d32} -Xms64m -Xmx1024m -XX:MaxPermSize=1024m -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled -ea -Dapple.awt.UIElement=true
diff --git a/build.xml b/build.xml
index a7c5bb5..6a7b8f2 100644
--- a/build.xml
+++ b/build.xml
@@ -50,6 +50,7 @@
 
 	<property name="dbg" value="false"/>
 	<property name="rel" value="true"/>
+    <property name="flexdebug" value="${flexdebug}"/>
 
 	<condition property="digest" value="false" else="true">
 		<istrue value="${debug}"/>
@@ -97,6 +98,7 @@
 			<static-link-runtime-shared-libraries/>
 			<define name="CONFIG::debug" value="${dbg}"/>
 			<define name="CONFIG::release" value="${rel}"/>
+            <compiler.debug>${flexdebug}</compiler.debug>
 		</compc>
      </target>
 
@@ -110,6 +112,7 @@
             <arg value="+source.dir=textLayout" />
 			<define name="CONFIG::debug" value="${dbg}"/>
 			<define name="CONFIG::release" value="${rel}"/>
+            <compiler.debug>${flexdebug}</compiler.debug>
 		</compc>
      </target>
 
@@ -169,13 +172,14 @@
 
 			<define name="CONFIG::debug" 			value="${dbg}"/>
 			<define name="CONFIG::release" 			value="${rel}"/>
+            <compiler.debug>${flexdebug}</compiler.debug>
 		</compc>
      </target>
 
 	<target name="textLayout_ui" description="Builds textLayout_ui.swc">		
 		<compc fork="${forkValue}" output="${output.dir}/apps/testApps/bin/textLayout_ui.swc" 
 			include-classes="${textLayout.namespace}.UiClasses,bxf.BxfClasses" compute-digest="${digest}">
-			<source-path path-element="${textLayout_ui.dir}/src"/>	
+			<source-path path-element="${textLayout_ui.dir}/src"/>
 			<jvmarg line="${jvm.args}"/>
 			<library-path/>
 			
@@ -190,12 +194,13 @@
 
 			<define name="CONFIG::debug" 			value="${dbg}"/>
 			<define name="CONFIG::release" 			value="${rel}"/>
+            <compiler.debug>${flexdebug}</compiler.debug>
 		</compc>
      </target>
 
 	<target name="testApps" description="Builds TLF test application">
 		<mxmlc fork="${forkValue}" 	file="${testApps.dir}/src/Flow.mxml" 
-				keep-generated-actionscript="false"
+				keep-generated-actionscript="false" debug="${flexdebug}"
 				output="${output.dir}/apps/automation_apps/bin/Flow.swf">
 			
 			<default-size width="1100" height="550" />
@@ -225,7 +230,7 @@
 
 	<target name="asTestApp" description="build a single application in asTestApp" if="output">
 		<mxmlc fork="${forkValue}"
-			file="${asTestApps.dir}/src/${output}.as"
+			file="${asTestApps.dir}/src/${output}.as" debug="${flexdebug}"
 			output="${output.dir}/apps/asTestApps/bin/${output}.swf">
 			<source-path path-element="${asTestApps.dir}/src"/>
 			<jvmarg line="${jvm.args}"/>
@@ -319,7 +324,8 @@
 	</target>
 
 	<target name="automation_apps" description="Builds the automation test application">
-		<mxmlc fork="${forkValue}" 	file="${automation_apps.dir}/src/VellumUnit.mxml" 
+		<mxmlc fork="${forkValue}" 	file="${automation_apps.dir}/src/VellumUnit.mxml"
+               debug="${flexdebug}"
 				output="${output.dir}/apps/automation_apps/bin/VellumUnit.swf">
 			<jvmarg line="${jvm.args}"/>
 			<library-path/>
diff --git a/textLayout/src/flashx/textLayout/CoreClasses.as b/textLayout/src/flashx/textLayout/CoreClasses.as
index 42eaa87..c102f00 100644
--- a/textLayout/src/flashx/textLayout/CoreClasses.as
+++ b/textLayout/src/flashx/textLayout/CoreClasses.as
@@ -57,6 +57,7 @@
 		import flashx.textLayout.edit.ISelectionManager; ISelectionManager;
 		import flashx.textLayout.edit.SelectionFormat; SelectionFormat;
 		import flashx.textLayout.edit.SelectionState; SelectionState;
+		import flashx.textLayout.edit.SelectionType; SelectionType;
 		
 		import flashx.textLayout.elements.SubParagraphGroupElementBase; SubParagraphGroupElementBase;
 		import flashx.textLayout.elements.BreakElement; BreakElement;
@@ -87,7 +88,7 @@
 		import flashx.textLayout.elements.TableBodyElement; TableBodyElement;
 		import flashx.textLayout.elements.TableColElement; TableColElement;
 		import flashx.textLayout.elements.TableColGroupElement; TableColGroupElement;
-		import flashx.textLayout.elements.TableDataCellElement; TableDataCellElement;
+		import flashx.textLayout.elements.TableCellElement; TableCellElement;
 		import flashx.textLayout.elements.TableRowElement; TableRowElement;
 		import flashx.textLayout.elements.TCYElement; TCYElement;
 		import flashx.textLayout.elements.TextFlow; TextFlow;
diff --git a/textLayout/src/flashx/textLayout/compose/BaseCompose.as b/textLayout/src/flashx/textLayout/compose/BaseCompose.as
index b7c4b02..3ed8352 100644
--- a/textLayout/src/flashx/textLayout/compose/BaseCompose.as
+++ b/textLayout/src/flashx/textLayout/compose/BaseCompose.as
@@ -30,7 +30,6 @@
 	import flash.text.engine.TextLineValidity;
 	import flash.utils.Dictionary;
 	
-	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.container.ContainerController;
 	import flashx.textLayout.debug.Debugging;
 	import flashx.textLayout.debug.assert;
@@ -50,8 +49,8 @@
 	import flashx.textLayout.elements.SpanElement;
 	import flashx.textLayout.elements.TCYElement;
 	import flashx.textLayout.elements.TableBodyElement;
+	import flashx.textLayout.elements.TableCellElement;
 	import flashx.textLayout.elements.TableColElement;
-	import flashx.textLayout.elements.TableDataCellElement;
 	import flashx.textLayout.elements.TableElement;
 	import flashx.textLayout.elements.TableFormattedElement;
 	import flashx.textLayout.elements.TableRowElement;
@@ -70,14 +69,16 @@
 	import flashx.textLayout.formats.TextLayoutFormat;
 	import flashx.textLayout.formats.VerticalAlign;
 	import flashx.textLayout.property.Property;
+	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.utils.LocaleUtil;
 	import flashx.textLayout.utils.Twips;
 
 	use namespace tlf_internal;
 	
 	
-	[ExcludeClass]
-	/** @private Common composer base class */
+	/** 
+	 * Common composer base class 
+	 * */
 	public class BaseCompose
 	{
 		
@@ -125,6 +126,8 @@
 		protected var _curParaFormat:ITextLayoutFormat;
 		/** Absolute start position of _curParaElement */
 		protected var _curParaStart:int;
+		/** Absolute start position of the current line */
+		protected var _curLineStart:int;
 		/** leading model for the current line's para (set when line is being composed and committed to _lastLineLeadingModel when line is finalized) */
 		private var _curLineLeadingModel:String = "";
 		/** leading factor calculated when composing the current line (committed to _lastLineLeading when line is finalized)
@@ -307,7 +310,7 @@
 			if (controllerStartIndex < 0)
 				controllerStartIndex = 0;
 			
-			// this chains through the list - tell it if a "care about" comopseToPosition was specified
+			// this chains through the list - tell it if a "care about" composeToPosition was specified
 			_parcelList.beginCompose(composer, controllerStartIndex, controllerEndIndex, composeToPosition > 0);	
 			
 			_contentLogicalExtent = 0;
@@ -324,74 +327,53 @@
 			_listItemElement = null;
 		}
 		
-		private function composeBlockElement(elem:FlowGroupElement,absStart:int,isInTable:Boolean=false, startChildIdx:int = -1):Boolean
+		private function composeBlockElement(elem:FlowGroupElement, absStart:int, isInTable:Boolean=false, startChildIdx:int = -1):Boolean
 		{	
 			var child:FlowElement;
 			var rslt:Boolean;	// scratch
+			var isInTableCell:Boolean = elem is TextFlow && TextFlow(elem).parentElement is TableCellElement ? true : false;
+			var cellSpacing:Number = 0;
+			
+			if (isInTableCell) {
+				var tableCell:TableCellElement = TextFlow(elem).parentElement as TableCellElement;
+				var table:TableElement = tableCell.table;
+				cellSpacing = table.cellSpacing!=undefined ? table.cellSpacing : 0;
+			}
 			
 			// Iterate through the children, composing them. If we're starting in the middle of the element,
 			// make sure we advance to the starting child.
 			var idx:int = 0;
 			if ( startChildIdx != -1 )
 				idx = startChildIdx;
-			if(!isInTable)
+			
+			if (absStart != _curElementStart + _curElementOffset) 	// starting partway in
 			{
-				if (absStart != _curElementStart + _curElementOffset) 	// starting partway in
+				idx = elem.findChildIndexAtPosition((_curElementStart + _curElementOffset) - absStart);
+				child = elem.getChildAt(idx);
+				absStart += child.parentRelativeStart;
+				
+				// Fix bug#2907691 When composition starts in middle of the container, paddingBottom for the previous paragraph is ignored
+				// add prevous paragraph's paddingBottom values to totalDepth
+				var previousElement:FlowLeafElement = _textFlow.findLeaf(_startComposePosition - 1);
+				if (previousElement)
 				{
-					idx = elem.findChildIndexAtPosition((_curElementStart + _curElementOffset) - absStart);
-					child = elem.getChildAt(idx);
-					absStart += child.parentRelativeStart;
-					
-					// Fix bug#2907691 When composition starts in middle of the container, paddingBottom for the previous paragraph is ignored
-					// add prevous paragraph's paddingBottom values to totalDepth
-					var previousElement:FlowLeafElement = _textFlow.findLeaf(_startComposePosition - 1);
-					if (previousElement)
-					{
-						var previousParagraph:ParagraphElement = previousElement.getParagraph();
-						if (previousParagraph && previousParagraph != _curElement.getParagraph())
-							if (previousParagraph.paddingBottom != undefined)
-								_parcelList.addTotalDepth(previousParagraph.paddingBottom);
-					}
-					
-					// child is table means recompose starts in the middle of a table. 
-					// In this case, we finished compose the table, then continue
-					if ( child is TableElement )
-					{
-						child = _curElement;
-						while ( child && ! (child is TableRowElement) )
-							child = child.parent;
-						
-						var tableElement:TableElement = child.parent as TableElement;
-						tableElement.totalRowDepth = 0;
-						tableElement.numAcrossParcels = 0;
-						tableElement.originParcelIndex = 0;
-						tableElement.heightArray = [];
-						if ( ! composeBlockElement(child.parent, _curElementStart, true, (child as TableRowElement).rowIndex) )
-						{
-							BackgroundManager.collectBlock(_textFlow, tableElement, _parcelList, true);
-							return false;
-						}
-						
-						// Add table border info
-						tableElement.totalRowDepth += tableElement.getEffectiveBorderBottomWidth() + tableElement.computedFormat.cellSpacing;
-						tableElement.height = tableElement.totalRowDepth;
-						
-						absStart = _curElementStart;
-						idx ++;
-						
-						if ( ! gotoParcel(tableElement.numAcrossParcels, tableElement.totalRowDepth) )	
-							return false;
-						
-						_atColumnStart = false;
-						_parcelList.addTotalDepth(tableElement.getEffectiveMarginBottom());
-						
-						BackgroundManager.collectBlock(_textFlow, tableElement, _parcelList, true);
-					}
+					var previousParagraph:ParagraphElement = previousElement.getParagraph();
+					if (previousParagraph && previousParagraph != _curElement.getParagraph())
+						if (previousParagraph.paddingBottom != undefined)
+							_parcelList.addTotalDepth(previousParagraph.paddingBottom);
 				}
-			}
+				
+				// child is table means recompose starts in the middle of a table. 
+				// In this case, we finished compose the table, then continue
+				// Harbs - need to analize whether this makes sense at all...
+				/*
+				if ( child is TableElement )
+				{
+				// if it makes sense, we need to redo this logic.
 
-			
-			
+				}
+				*/
+			}
 			
 			var composeEntireElement:Boolean = (absStart == _curElementStart + _curElementOffset);
 
@@ -400,41 +382,45 @@
 			{
 				child = elem.getChildAt(idx);
 				
-				if(!isInTable)
+				// If the element has clear applied, handle that now
+				if (child.computedFormat.clearFloats != ClearFloats.NONE)
 				{
-					// If the element has clear applied, handle that now
-					if (child.computedFormat.clearFloats != ClearFloats.NONE)
-					{
-						var adjustedDepth:Number = _curParcel.applyClear(child.computedFormat.clearFloats, _parcelList.totalDepth, child.computedFormat.direction);
-						_parcelList.addTotalDepth(adjustedDepth);
-						_verticalSpaceCarried = 0;
-					}
-	
-					var boxLeftIndent:Number;		// logical with respect to horizontal/vertical text
-					var boxRightIndent:Number;		// logical with respect to horizontal/vertical text
-					var boxTopIndent:Number;		// logical with respect to horizontal/vertical text
-					var boxBottomIndent:Number;		// logical with respect to horizontal/vertical text
-					if (_blockProgression == BlockProgression.RL)
-					{
-						boxLeftIndent = child.getEffectivePaddingTop() + child.getEffectiveBorderTopWidth() + child.getEffectiveMarginTop();
-						boxRightIndent = child.getEffectivePaddingBottom() + child.getEffectiveBorderBottomWidth() + child.getEffectiveMarginBottom();
-						boxTopIndent = child.getEffectivePaddingRight() + child.getEffectiveBorderRightWidth() + child.getEffectiveMarginRight();
-						boxBottomIndent = child.getEffectivePaddingLeft() + child.getEffectiveBorderLeftWidth() + child.getEffectiveMarginLeft();
-					}
-					else
-					{
-						boxLeftIndent = child.getEffectivePaddingLeft() + child.getEffectiveBorderLeftWidth() + child.getEffectiveMarginLeft();
-						boxRightIndent = child.getEffectivePaddingRight() + child.getEffectiveBorderRightWidth() + child.getEffectiveMarginRight();
-						boxTopIndent = child.getEffectivePaddingTop() + child.getEffectiveBorderTopWidth() + child.getEffectiveMarginTop();
-						boxBottomIndent = child.getEffectivePaddingBottom() + child.getEffectiveBorderBottomWidth() + child.getEffectiveMarginBottom();
-					}
-					CONFIG::debug { assert(!isNaN(boxLeftIndent) && ! isNaN(boxRightIndent),"BAD indents"); }
-					_parcelList.pushLeftMargin(boxLeftIndent);
-					_parcelList.pushRightMargin(boxRightIndent);
-					if (composeEntireElement && boxTopIndent > _verticalSpaceCarried)
-						_parcelList.addTotalDepth(boxTopIndent - _verticalSpaceCarried);
-					_verticalSpaceCarried = Math.max(boxTopIndent, 0);
+					var adjustedDepth:Number = _curParcel.applyClear(child.computedFormat.clearFloats, _parcelList.totalDepth, child.computedFormat.direction);
+					_parcelList.addTotalDepth(adjustedDepth);
+					_verticalSpaceCarried = 0;
 				}
+				
+				var boxLeftIndent:Number;		// logical with respect to horizontal/vertical text
+				var boxRightIndent:Number;		// logical with respect to horizontal/vertical text
+				var boxTopIndent:Number;		// logical with respect to horizontal/vertical text
+				var boxBottomIndent:Number;		// logical with respect to horizontal/vertical text
+				if (_blockProgression == BlockProgression.RL)
+				{
+					boxLeftIndent = child.getEffectivePaddingTop() + child.getEffectiveBorderTopWidth() + child.getEffectiveMarginTop();
+					boxRightIndent = child.getEffectivePaddingBottom() + child.getEffectiveBorderBottomWidth() + child.getEffectiveMarginBottom();
+					boxTopIndent = child.getEffectivePaddingRight() + child.getEffectiveBorderRightWidth() + child.getEffectiveMarginRight();
+					boxBottomIndent = child.getEffectivePaddingLeft() + child.getEffectiveBorderLeftWidth() + child.getEffectiveMarginLeft();
+				}
+				else
+				{
+					boxLeftIndent = child.getEffectivePaddingLeft() + child.getEffectiveBorderLeftWidth() + child.getEffectiveMarginLeft();
+					boxRightIndent = child.getEffectivePaddingRight() + child.getEffectiveBorderRightWidth() + child.getEffectiveMarginRight();
+					boxTopIndent = child.getEffectivePaddingTop() + child.getEffectiveBorderTopWidth() + child.getEffectiveMarginTop();
+					boxBottomIndent = child.getEffectivePaddingBottom() + child.getEffectiveBorderBottomWidth() + child.getEffectiveMarginBottom();
+					
+					if (isInTableCell) {
+						boxLeftIndent += cellSpacing;
+						boxRightIndent += cellSpacing;
+						boxTopIndent += cellSpacing;
+						boxBottomIndent += cellSpacing;
+					}
+				}
+				CONFIG::debug { assert(!isNaN(boxLeftIndent) && ! isNaN(boxRightIndent),"BAD indents"); }
+				_parcelList.pushLeftMargin(boxLeftIndent);
+				_parcelList.pushRightMargin(boxRightIndent);
+				if (composeEntireElement && boxTopIndent > _verticalSpaceCarried)
+					_parcelList.addTotalDepth(boxTopIndent - _verticalSpaceCarried);
+				_verticalSpaceCarried = Math.max(boxTopIndent, 0);
 								
 				
 				var para:ParagraphElement = child as ParagraphElement;
@@ -468,7 +454,7 @@
 				}
 				else if (child is ListElement)
 				{						
-					rslt = composeBlockElement(FlowGroupElement(child),absStart,isInTable);
+					rslt = composeBlockElement(FlowGroupElement(child),absStart);
 					
 					if (!rslt)
 					{
@@ -481,7 +467,7 @@
 				{
 					var savedListItemElement:ListItemElement = _listItemElement;
 					_listItemElement = child as ListItemElement;
-					rslt = composeBlockElement(FlowGroupElement(child),absStart,isInTable);
+					rslt = composeBlockElement(FlowGroupElement(child),absStart);
 					_listItemElement = savedListItemElement;
 						
 					if (!rslt)
@@ -491,24 +477,6 @@
 						return false;
 					}
 				}
-				else if (child is TableElement)         // Compose TableElement
-				{
-					if ( ! composeTableElement(child as TableElement, absStart, isInTable) )
-						return false;
-				}
-				else if (child is TableRowElement)     // Compose TableRowElement
-				{
-					var rowElement:TableRowElement = child as TableRowElement;
-					rowElement.rowIndex = idx;
-					
-					if ( ! composeTableRowElement(elem as TableElement, rowElement, absStart, isInTable) )
-						return false;
-				}
-				else if (child is TableDataCellElement) // Compose TableDataCellElement
-				{
-					if ( ! composeTableDataCellElement(elem as TableRowElement, child as TableDataCellElement, absStart, isInTable) )
-						return false;
-				}
 				else 
 				{
 					if ( ! composeBlockElement(FlowGroupElement(child),absStart))
@@ -519,22 +487,20 @@
 					}
 				}
 				
-				if(! isInTable)
-				{
-					if (boxBottomIndent > _verticalSpaceCarried)
-						_parcelList.addTotalDepth(boxBottomIndent - _verticalSpaceCarried);
-					_verticalSpaceCarried = Math.max(boxBottomIndent, 0);
-	
-					// restore to original values
-					_parcelList.popLeftMargin(boxLeftIndent);
-					_parcelList.popRightMargin(boxRightIndent);
-					composeEntireElement = true;
-				}
+				if (boxBottomIndent > _verticalSpaceCarried)
+					_parcelList.addTotalDepth(boxBottomIndent - _verticalSpaceCarried);
+				_verticalSpaceCarried = Math.max(boxBottomIndent, 0);
+				
+				// restore to original values
+				_parcelList.popLeftMargin(boxLeftIndent);
+				_parcelList.popRightMargin(boxRightIndent);
+				composeEntireElement = true;
+				
 				absStart += child.textLength;
 			}
 			
-			//for elements, whose text are all visible, except for TableElement, TableRowElement and TableDataCellElement
-			if(!(elem is TableElement || elem is TableRowElement || elem is TableDataCellElement))
+			//for elements, whose text are all visible, except for TableElement, TableRowElement and TableCellElement
+			if(!(elem is TableElement || elem is TableRowElement || elem is TableCellElement))// (we don't process these...)
 				BackgroundManager.collectBlock(_textFlow, elem);
 			
 			return true;
@@ -545,322 +511,186 @@
 		 * In  : TableElement, table's absStart position, isInTable
 		 * Out : Boolean value, composition result, true - successful, false - failed
 		 */
-		private function composeTableElement(tableElement:TableElement, absStart:int, isInTable:Boolean):Boolean
+		private function composeTableElement(tableElement:TableElement, absStart:int):Boolean
 		{
-			//1st step findout the TableGroup element and get the composing parameter for the columns
-			//my first idea is to read them to be a TableElement list or map. And remove all the column 
-			//element so that the column element will not fall into the recursive loop
-			tableElement.numAcrossParcels = 0;
-            tableElement.heightArray = [];
-            tableElement.curRowIdx = 0;
-            tableElement.outOfLastParcel = false;
-            
-			//2nd step setup the environment settings
-			var marginLeft:Number = tableElement.computedFormat.marginLeft;
-			var marginTop:Number  = tableElement.computedFormat.marginTop;
-            
-			tableElement.x = _parcelList.currentParcel.x + marginLeft;
-			tableElement.y = _parcelList.totalDepth + (_atColumnStart ? marginTop : _firstLineDescentAndLeading + marginTop);
-			
-			_parcelList.addTotalDepth(tableElement.y - _parcelList.totalDepth);
-			
-			// The following codes are setting the column width
-			serializeTableColumnWidth(tableElement);
-			
-			//TO-DO: Verify the borderTopWidth Value
-			tableElement.originParcelIndex = _parcelList.currentParcelIndex;
-			var originParcel:Parcel = _parcelList.currentParcel;
-			
-			// Add table border info
-			_parcelList.addTotalDepth(tableElement.getEffectiveBorderTopWidth());
-			tableElement.totalRowDepth = _parcelList.totalDepth;
-			
-			//2nd step recursively compose the table elements
-			if ( ! composeBlockElement(FlowGroupElement(tableElement), absStart, true) )
-            {
-                // If we're out of parcel, we don't need to calculate table height anymore,
-                // because we've done in composeTableRowElement before it return false.
-                if(tableElement.outOfLastParcel)
-                    BackgroundManager.collectBlock(_textFlow, tableElement, _parcelList, false, true);
-                
-                return false;
-            }
-			
-			// Add table border info
-			tableElement.totalRowDepth += tableElement.getEffectiveBorderBottomWidth() + tableElement.computedFormat.cellSpacing;
-			
-            if(tableElement.numAcrossParcels == 0 && _startComposePosition <= tableElement.getAbsoluteStart())
-                tableElement.height = tableElement.totalRowDepth - tableElement.y;
-            else
-				tableElement.height = tableElement.totalRowDepth;
+			//TODO: remove any old existing cells in the _parcelList.currentParcel.controller from the position of the table and on.
+			// need to figure out the accounting needed for that.
 
-			// If current composition position plus one line height beyond the parcel bottom, then we jump to next parcel 
-			var depth:Number = tableElement.totalRowDepth;
-			
-			// If table already full the last column, we won't goto table cell parcel. Just goto last not table cell parcel
-			var parcelIdx:int = tableElement.originParcelIndex + tableElement.numAcrossParcels;
-			if ( _parcelList.getParcelAt(parcelIdx).isTableParcel )
-				depth = _parcelList.getParcelAt(--parcelIdx).bottom;
-			
-			if ( ! gotoParcel(parcelIdx, depth) )	
-				return false;
-			
-            _atColumnStart = false;
-			_parcelList.addTotalDepth(tableElement.getEffectiveMarginBottom());
-			
-			BackgroundManager.collectBlock(_textFlow, tableElement, _parcelList);
-			return true;
-		}
-		
-		/** @private
-		 * Compose a entire table row element
-		 * In  : TableElement, TableRowElement, table's absStart position, isInTable
-		 * Out : Boolean value, composition result, true - successful, false - failed
-		 */
-		private function composeTableRowElement(tableElement:TableElement, rowElement:TableRowElement, absStart:int, isInTable:Boolean):Boolean
-		{
-			rowElement.iMaxRowDepth = 0;
-            rowElement.columnIndex = 0;
-			
-			// Add table cell spacing value
-			tableElement.totalRowDepth += tableElement.computedFormat.cellSpacing;
-			
-			// Save environment parameters before compose a table row, because maybe this row out of "current" parcel, 
-			// in that case, we need to reload these parameters, then recompose this row.
-			var curParaStart:int = _curParaStart;
-			var curElementStart:int = _curElementStart;
-			var curParcelStart:int = _curParcelStart;
-			var curElement:FlowLeafElement = _curElement;
-			var curParaElement:ParagraphElement = _curParaElement;
-            
-            if ( ! composeBlockElement(FlowGroupElement(rowElement), absStart, isInTable) )
-			{
-				// Compose row failed, see if we it's because of out of parcel
-				if ( rowElement.beyondParcel )
-				{
-                    var nextParcel:Parcel = _parcelList.getParcelAt(tableElement.originParcelIndex + tableElement.numAcrossParcels + 1);
-
-                    if(tableElement.curRowIdx == 0)
-                    {
-                        if(!nextParcel || nextParcel.isTableParcel)
-                        {
-                            tableElement.x = tableElement.computedFormat.marginLeft;
-                            tableElement.y = tableElement.totalRowDepth;
-                        }
-                    }
-                    
-                    // Release textLines before re-compose
-                    for ( var i:int = 0; i < rowElement.numChildren; i ++ )
-                    {
-                        var cell:TableDataCellElement = rowElement.getChildAt(i) as TableDataCellElement;
-                        for ( var j:int = 0; j < cell.numChildren; j ++ )
-                        {
-                            var paragraph:ParagraphElement = cell.getChildAt(j) as ParagraphElement;
-                            var textBlock:TextBlock = paragraph.getTextBlock();
-                            _parcelList.currentParcel.controller.clearFloatsAt(paragraph.getAbsoluteStart());
-                            for (var textLine:TextLine = textBlock.lastLine; textLine; )
-                            {
-                                textBlock.releaseLines(textLine, textLine);
-                                textLine.userData = null;
-                                textLine.visible = false;
-                                //TextLineRecycler.addLineForReuse(textLine);
-                                if (_textFlow.backgroundManager)
-                                    _textFlow.backgroundManager.removeLineFromCache(textLine);
-                                textLine = textBlock.lastLine;
-                            }
-                            paragraph.releaseTextBlock();
-                        }
-                    }
-                    
-					// If table already full the last column, we won't goto table cell parcel. Just goto last not table cell parcel
-					if ( ! nextParcel || nextParcel.isTableParcel)
-                    {
-                        tableElement.outOfLastParcel = true;
-						return false;
-                    }
-                    
-                    if(tableElement.curRowIdx > 0)
-                    {
-                        tableElement.numAcrossParcels ++;
-                        tableElement.heightArray.push(0);
-                        tableElement.totalRowDepth = nextParcel.y + tableElement.computedFormat.cellSpacing;
-                    }
-                    else if(tableElement.curRowIdx == 0)
-                    {
-                        tableElement.originParcelIndex++;
-                        tableElement.x = nextParcel.x + tableElement.computedFormat.marginLeft;
-                        tableElement.y = nextParcel.y;
-                        tableElement.totalRowDepth = nextParcel.y + tableElement.computedFormat.cellSpacing + tableElement.getEffectiveBorderTopWidth();
-                    }
-
-					rowElement.beyondParcel = false;
-                    
-                    // Reload values for recompose
-                    _curParaStart = curParaStart;
-                    _curElementStart = curElementStart;
-                    _curParcelStart = curParcelStart;
-                    _curElement = curElement;
-                    _curParaElement = curParaElement;
-                    _curElementOffset = 0;
-                    _contentLogicalExtent = 0;
-                    rowElement.columnIndex = 0;
-                    
-					// Recompose current table row
-					if ( ! composeBlockElement(FlowGroupElement(rowElement), absStart, isInTable) )
-						return false;
-				}
-			}
-			
-            var curParcel:Parcel = _parcelList.getParcelAt(tableElement.originParcelIndex + tableElement.numAcrossParcels);
-			rowElement.parcelIndex = tableElement.originParcelIndex + tableElement.numAcrossParcels;
-			rowElement.height = rowElement.iMaxRowDepth;
-			rowElement.x = curParcel.x;
-			rowElement.y = tableElement.totalRowDepth;
-			tableElement.totalRowDepth += rowElement.height;
-			
-			if(tableElement.numAcrossParcels == 0 && _startComposePosition <= tableElement.getAbsoluteStart())
-				tableElement.height = tableElement.totalRowDepth - tableElement.y;
+			// Space before does not apply to the first line, unless LeadingModel.BOX is used
+			// Space carried never applies to the first line
+			if(_curLine && _curLine.paragraph == _curParaElement)
+				var spaceBefore:Number = 0;
 			else
-				tableElement.height = tableElement.totalRowDepth;
+				spaceBefore = isNaN(_curParaElement.computedFormat.paragraphSpaceBefore) ? 0 : _curParaElement.computedFormat.paragraphSpaceBefore;
 			
-			tableElement.height += tableElement.cellSpacing;
+			spaceBefore  = _atColumnStart ? 0 : spaceBefore;
+			var spaceCarried:Number = _atColumnStart ? 0 : _paragraphSpaceCarried;
+			if (spaceBefore != 0 || spaceCarried != 0)
+				_parcelList.addTotalDepth(Math.max(spaceBefore, spaceCarried));
 			
-			// Add table cell to columnState for hitTest
-			var ccOfRow:ContainerController = _parcelList.getParcelAt(rowElement.parcelIndex).controller;
-			for ( i = 0; i < rowElement.numChildren; i ++ )
-			{
-				cell = rowElement.getChildAt(i) as TableDataCellElement;
-				cell.height = rowElement.height;
-				_parcelList.addTableCell2ColumnState(ccOfRow, cell);
-			}
+			_paragraphSpaceCarried = 0;
+			if (_verticalSpaceCarried != 0)
+				_verticalSpaceCarried = 0;
+
+			// get a slug...
+			_parcelList.getLineSlug(_lineSlug, 0, 1, _textIndent, _curParaFormat.direction == Direction.LTR);
 			
-			BackgroundManager.collectBlock(_textFlow, rowElement, _parcelList);
-            tableElement.curRowIdx++;
-			return true;
-		}
-		
-		/** @private
-		 * Compose a entire table row element
-		 * In  : TableRowElement, TableDataCellElement, table's absStart position, isInTable
-		 * Out : Boolean value, composition result, true - successful, false - failed
-		 */
-		private function composeTableDataCellElement(rowElement:TableRowElement, cellElement:TableDataCellElement, absStart:int, isInTable:Boolean):Boolean
-		{
-			//TableDataCellElement's parent must be TableRowElement
-			var tableElement:TableElement = rowElement.getTable();
+			// doesn't do anything yet.
+			tableElement.normalizeColumnWidths(_lineSlug.width);
 			
-			//TO-DO: This is temporary codes, needs to be updated when the real column attribute is implemented
-			var currParcel:Parcel = _parcelList.getParcelAt(tableElement.originParcelIndex + tableElement.numAcrossParcels);
-			cellElement.x = currParcel.x + tableElement.getColumnAt(rowElement.columnIndex).x;
-			cellElement.y = tableElement.totalRowDepth;
-			cellElement.width = tableElement.getColumnWidth(rowElement.columnIndex);
-			cellElement.height = undefined;
-			var rc:Rectangle = new Rectangle(cellElement.x, cellElement.y, cellElement.width, 8000); // 8000 is Max row height
-			cellElement.parcelIndex = _parcelList.numParcels();
-			var newParcel:Parcel = _parcelList.addParcel(rc, currParcel.controller,
-				tableElement.originParcelIndex + tableElement.numAcrossParcels);
-			newParcel.isTableParcel = true;
+			// step 1 -- make sure all cells are composed
+			tableElement.composeCells();
 			
-			if ( ! gotoParcel(cellElement.parcelIndex, 0) )
-				return false;
+			// step 2 get header and footer heights
+			// I'm not sure if we need to calculate table padding/margin
+			//var baseTableHeight:Number = tableElement.getHeaderHeight() + tableElement.getFooterHeight();
+			//_parcelList.getLineSlug(_lineSlug, 0, 1, _textIndent, _curParaFormat.direction == Direction.LTR);
 			
-			// Add border and cell padding
-			_parcelList.addTotalDepth(cellElement.getEffectiveBorderTopWidth() + cellElement.computedFormat.cellPadding);
-			_parcelList.pushLeftMargin(cellElement.getEffectiveBorderLeftWidth() + cellElement.computedFormat.cellPadding);
-			_parcelList.pushRightMargin(cellElement.getEffectiveBorderRightWidth() + cellElement.computedFormat.cellPadding);
+			var headerHeight:Number = tableElement.getHeaderHeight();
+			var footerHeight:Number = tableElement.getFooterHeight();
 			
-			// fall into the recursive loop directly
-			if ( ! composeBlockElement(FlowGroupElement(cellElement), absStart, isInTable) )
-				return false;
+			// need to calculate margins and padding as well. (top, bottom and sides) It should inherit from the containing paragraph if necessary.
+			// I'm assuming tables can inherit proeprties from paragraphs.
 			
-			// Add border and cell padding, pop out padding margin
-			_parcelList.addTotalDepth(cellElement.getEffectiveBorderBottomWidth() + cellElement.computedFormat.cellPadding);
-			_parcelList.popLeftMargin(cellElement.getEffectiveBorderLeftWidth() + cellElement.computedFormat.cellPadding);
-			_parcelList.popRightMargin(cellElement.getEffectiveBorderRightWidth() + cellElement.computedFormat.cellPadding);
+			// step 3 loop through the cells and assign them to containers and set the positions
+
+			var totalRowHeight:Number = 0;
+			var haveRealRows:Boolean = false;
+			//grab the headers and footers for use in each parcel
+			var headerRows:Vector.< Vector.<TableCellElement> > = tableElement.getHeaderRows();
+			var footerRows:Vector.< Vector.<TableCellElement> > = tableElement.getFooterRows();
 			
-			//TO-DO, The codes may be changed, mingjun's original codes are as following:
-			//cellElement.height = _parcelList.totalDepth + paragraph.paddingBottom + paragraph.borderBottomWidth + paragraph.marginBottom;
-			cellElement.height = _parcelList.totalDepth;
-			if ( cellElement.height > rowElement.iMaxRowDepth )
-				rowElement.iMaxRowDepth = cellElement.height;
-			rowElement.columnIndex ++;
+			var curRow:Vector.<TableCellElement> = tableElement.getNextRow();
+			var curTableBlock:TextFlowTableBlock = tableElement.getFirstBlock();
+			curTableBlock.clear();
+			curTableBlock.y = _parcelList.totalDepth;
+			var adjustTop:Number = isNaN(_lastLineDescent) ? 0 : _lastLineDescent;
+			curTableBlock.y += adjustTop;
+			curTableBlock.x = _lineSlug.leftMargin;
+			var lineOffset:Number = (_curParaFormat.direction == Direction.LTR) ? _lineSlug.leftMargin : _lineSlug.rightMargin;
+			curTableBlock.initialize(_curParaElement, _lineSlug.width, lineOffset-_parcelList.insideListItemMargin, tableElement.getAbsoluteStart(),1);
+			var blockToAdd:Boolean = true;
 			
-			// See if the composed line beyond "current" parcel's bottom
-			if ( tableElement.totalRowDepth + rowElement.iMaxRowDepth > currParcel.bottom)
-			{
-				rowElement.beyondParcel = true;
+			while(curRow){
 				
-				// Pop out useless parcels for this row
-				for ( var n:int = 0; n < rowElement.columnIndex; n ++ )
-					_parcelList.popParcel();
-				return false;
+				// I'm ignoring headers and footers for now. We need to add them in later.
+				var rIdx:int = curRow[0].rowIndex;
+				var curRowElem:TableRowElement = tableElement.getRowAt(rIdx);
+				var rowHeight:Number = curRowElem.composedHeight;
+				var minRowHeight:Number = curRowElem.totalHeight;
+				//_parcelList.addTotalDepth(tableElement.getEffectiveMarginBottom());
+				
+				while(
+					false
+					//!(_parcelList.currentParcel.fitsInHeight(_parcelList.totalDepth, minRowHeight + footerHeight))
+				){
+					//TODO: add in footer rows...
+					
+					curTableBlock.height = totalRowHeight;
+					
+					if(!haveRealRows)
+						curTableBlock.clear();
+					
+					endTableBlock(curTableBlock);
+//					curTableBlock.setController(_parcelList.currentParcel.controller,_parcelList.currentParcel.columnIndex);
+
+//					_parcelList.currentParcel.controller.addComposedTableBlock(curTableBlock.container);
+//					BackgroundManager.collectTableBlock(_textFlow,curTableBlock, _parcelList.currentParcel.controller);
+					blockToAdd = false;
+					
+					if(!_parcelList.next())
+					{ // current parcel not valid
+						break;
+					}
+					_parcelList.getLineSlug(_lineSlug, 0, 1, _textIndent, _curParaFormat.direction == Direction.LTR);
+					curTableBlock = tableElement.getNextBlock();
+					blockToAdd = true;
+					curTableBlock.clear();
+					curTableBlock.y = _parcelList.totalDepth;
+					curTableBlock.x = _lineSlug.leftMargin;
+					curTableBlock.initialize(_curParaElement, _lineSlug.width, lineOffset-_parcelList.insideListItemMargin, tableElement.getAbsoluteStart(),1);
+					totalRowHeight = 0;
+					//TODO: remove any old existing cells in the _parcelList.currentParcel.controller
+					
+					//TODO: add in header rows. Collect them on the next iteration if no real rows fit.
+					
+					// not needed?
+					if(_parcelList.currentParcel == null){
+						blockToAdd = false;
+						break;
+					}
+				}
+				
+				if(_parcelList.currentParcel == null){
+					blockToAdd = false;
+					break;
+				}
+				
+				// we have a parcel and a row. Let's add the cells.
+				for each(var cell:TableCellElement in curRow) {
+					cell.y = totalRowHeight;
+					var col:TableColElement = tableElement.getColumnAt(cell.colIndex);
+					
+					if (col) {
+						cell.x = col.x;
+					}
+					
+					tableElement.addCellToBlock(cell, curTableBlock);
+					//curTableBlock.addCell(cell.container);
+					// add the cells to _parcelList.currentParcel.controller
+					// need to figure out exactly how.
+					
+				}
+				
+				// add the row height
+				// we're assuming normal top to bottom tables -- not Japanese ones...
+				_parcelList.addTotalDepth(rowHeight);
+				
+				curRow = tableElement.getNextRow();
+				totalRowHeight += rowHeight;
 			}
 			
+			if(_parcelList.currentParcel && blockToAdd){
+				curTableBlock.height = totalRowHeight;
+				endTableBlock(curTableBlock);
+//				curTableBlock.setController(_curParcel.controller,_curParcel.columnIndex);
+//				_parcelList.currentParcel.controller.addComposedTableBlock(curTableBlock.container);
+//				BackgroundManager.collectTableBlock(_textFlow,curTableBlock, _parcelList.currentParcel.controller);
+			}
+			//reference ComposeState.composeNextLine() which creates the the TextLine.
+			// We don't need getLineSlug() because tables can extend beyond the container width
+			// We do need to get the available height and push any cells that don't fit to the next Parcel/container
+			// Repeat until there's no more Parcels.
+			
+			// step 4 draw the backgrounds and borders
+			// handled by the BackgroundManager			
+			
 			return true;
 		}
-		
-		/** @private
-		 * Calculate table columm width based on column's width value, the rule is :
-		 * if there are zero column width or the sum(columnWidth) not equal to table.tableWidth,
-		 * we calculate average column width.
-		 * In  : Number or percentage
-		 * Out : Set pixcel based width of each table column
-		 */
-		private function serializeTableColumnWidth(table:TableElement):void
+		/** Called when we are finished composing a line, and it is committed. Handler for derived classes to override default behavior.  */
+		protected function endTableBlock(block:TextFlowTableBlock):void
 		{
-            var curParcelWidth:Number = _parcelList.currentParcel.width;
-			if ( table.tableWidth != undefined && table.tableWidth < curParcelWidth )
-				table.computedWidth = table.tableWidth;
-			else
-                table.computedWidth = curParcelWidth;
-            
-            if(table.computedFormat.marginLeft > 0)
-                table.computedWidth -= table.computedFormat.marginLeft;
-            
-            table.computedWidth -= table.computedFormat.marginRight;
-            
-			var logicalWidth:Number = table.computedWidth - table.getEffectiveBorderLeftWidth() - table.getEffectiveBorderRightWidth() - (table.column + 1) * table.computedFormat.cellSpacing;
-			var columnTotalWidth:Number = table.getEffectiveBorderLeftWidth() + table.computedFormat.marginLeft;
-            
-			for ( var i:int = 0; i < table.column; i ++ )
-			{
-				var colWidth:* = table.getColumnWidth(i);
-				
-				// Column width is percentage like "20%"
-				var strWidth:String = colWidth as String;
-				if ( strWidth && strWidth.length != 0 && strWidth.charAt(strWidth.length - 1) == '%' )
-				{
-					colWidth = Property.toNumberIfPercent(colWidth) * logicalWidth / 100;
-					if ( colWidth >= 0 )
-						table.setColumnWidth(i, colWidth);
-				}
-				
-				columnTotalWidth += table.computedFormat.cellSpacing;
-				table.getColumnAt(i).x = columnTotalWidth;
-				columnTotalWidth += colWidth;
-			}
-            
-			columnTotalWidth -= (table.computedFormat.marginLeft + table.getEffectiveBorderLeftWidth() + table.column * table.computedFormat.cellSpacing);
+			_curLine = block;
+			block.setController(_curParcel.controller, _curParcel.columnIndex);
+			//				_parcelList.currentParcel.controller.addComposedTableBlock(curTableBlock.container);
+			BackgroundManager.collectTableBlock(_textFlow, block, _parcelList.currentParcel.controller);
+
+			_contentCommittedExtent = Math.max(_contentCommittedExtent, _workingContentExtent);
+			_contentCommittedHeight = Math.max(_contentCommittedHeight, _workingContentHeight);
+			_contentLogicalExtent = Math.max(_contentLogicalExtent, _workingContentLogicalExtent);
 			
-			// If the sum of column width wider than table width,
-			// we set every column width to average column width
-			if ( columnTotalWidth - logicalWidth > 1 )
-			{
-				var avgColumnWidth:Number = logicalWidth / table.column;
-				for ( var m:int = 0; m < table.column; m ++ )
-				{
-					table.setColumnWidth(m, avgColumnWidth);
-					table.getColumnAt(m).x = table.getEffectiveBorderLeftWidth() + table.computedFormat.marginLeft 
-						+ (m+1)*table.computedFormat.cellSpacing + m * avgColumnWidth;
-				}
-			} 
-			// if the sum of column width less than table width, we enlarge the last column's width to fit table width
-			else if ( columnTotalWidth < logicalWidth ) 
-			{
-				var orgWidth:Number = table.getColumnWidth(table.column - 1);
-				table.setColumnWidth(table.column - 1, orgWidth + logicalWidth - columnTotalWidth);
-			}
-		}
+			// if not measuring than contentLogicalExtent needs to match contentCommitedExtent so restarting composition in the middle gets the right extent
+			// don't need contentLogicalExtent to exclude things pushing beyond the right margin as alignment is happening as we go
+			if (!_measuring)
+				_contentLogicalExtent = _contentCommittedExtent;
+			if (_pushInFloats)
+				_pushInFloats.length = 0;	// zero it out for the next line
+			_atColumnStart = false;
+			_linePass = 0;
+			if (!isNaN(_workingParcelLogicalTop))
+				_parcelLogicalTop = _workingParcelLogicalTop;
+		}		
+
 		
 		/**
 		 * Compose the flow into the text container. Starts at the root element,
@@ -892,9 +722,8 @@
 			
 			resetControllerBounds();
 			
-			// Bug, needs to remove
-			if (ContainerController.tlf_internal::startComposeFromBeginning)
-				_startComposePosition = _startController.absoluteStart;
+			// Bug, needs to remove 
+			_startComposePosition = _startController.absoluteStart;
 			
 			// This is where we will start composing from
 			_curElement = _textFlow.findLeaf(_startComposePosition);
@@ -964,9 +793,6 @@
 				}
 				
 				var nextParcel:Parcel = parcelList.getParcelAt(parcelList.currentParcelIndex + 1);
-				if ( parcelList.currentParcel.isTableParcel
-					&& ((nextParcel && nextParcel.isTableParcel) || parcelList.currentParcelIndex == parcelList.numParcels()-1))
-					_correctTextLength = true;
 		
 				advanceToNextParcel();
 				_correctTextLength = false;
@@ -1186,6 +1012,7 @@
 		{
 			_curParaElement  = elem;
 			_curParaStart    = absStart;
+			_curLineStart    = absStart;
 			_curParaFormat = elem.computedFormat;
 			
 			CONFIG::debug { assert(_curParaStart == elem.getAbsoluteStart(),"composeParagraphElement: bad start"); }
@@ -1204,25 +1031,28 @@
 			{
 				// Lines that are now composed that would not be visible on update, might still be in the display list from
 				// a previous update. Don't release in that case.
-				var textBlock:TextBlock = elem.getTextBlock();
+				var textBlocks:Vector.<TextBlock> = elem.getTextBlocks();
 				var textLine:TextLine;
-				for (textLine = textBlock.lastLine; textLine && okToRelease; textLine = textLine.previousLine) 
+				for each(var textBlock:TextBlock in textBlocks)
 				{
-					if (textLine.parent)
-						okToRelease = false;
-				}
-				if (okToRelease)	// no textlines were in view, go ahead and release them all, starting at the end and working to the start
-				{
-					for (textLine = textBlock.lastLine; textLine; )
+					for (textLine = textBlock.lastLine; textLine && okToRelease; textLine = textLine.previousLine) 
 					{
-						textBlock.releaseLines(textLine, textLine);
-						textLine.userData = null;
-						TextLineRecycler.addLineForReuse(textLine);
-						if (_textFlow.backgroundManager)
-							_textFlow.backgroundManager.removeLineFromCache(textLine);
-						textLine = textBlock.lastLine;
+						if (textLine.parent)
+							okToRelease = false;
 					}
-					elem.releaseTextBlock();
+					if (okToRelease)	// no textlines were in view, go ahead and release them all, starting at the end and working to the start
+					{
+						for (textLine = textBlock.lastLine; textLine; )
+						{
+							textBlock.releaseLines(textLine, textLine);
+							textLine.userData = null;
+							TextLineRecycler.addLineForReuse(textLine);
+							if (_textFlow.backgroundManager)
+								_textFlow.backgroundManager.removeLineFromCache(textLine);
+							textLine = textBlock.lastLine;
+						}
+						elem.releaseTextBlock(textBlock);
+					}
 				}
 			}
 
@@ -1256,9 +1086,12 @@
 		 */
 		protected function composeParagraphElementIntoLines():Boolean
 		{
+			// make sure TextBlocks are normalized
+			_curParaElement.createContentElement();
 			var result:Boolean = true;
 			var textLine:TextLine;
 			
+			
 			var leftMargin:Number;
 			var rightMargin:Number;
 			
@@ -1304,6 +1137,34 @@
 					break;
 				}
 
+				// do table here?
+				//_curElementStart == _curParaStart
+				//			var startCompose:int = _curElementStart + _curElementOffset - _curParaStart;
+				var curChild:FlowElement = _curParaElement.getChildAt(_curParaElement.findChildIndexAtPosition(_curElementStart - _curParaStart));
+				if(curChild is TableElement)
+				{
+					
+					if(!composeTableElement(curChild as TableElement, _curElementStart))
+						return false;
+					
+					_curElementOffset = 0;
+					_curElementStart  += _curElement.textLength;
+					_curElement = _curElement.getNextLeaf();
+					_curLineStart++;
+					_previousLine = null;
+
+					// if the next span is the terminator bail out...
+					if(_curElement is SpanElement && SpanElement(_curElement).hasParagraphTerminator && _curElement.textLength == 1)
+					{
+						_curElementOffset = 0;
+						_curElementStart  += _curElement.textLength;
+						_curElement = _curElement.getNextLeaf();
+						return true;
+					}
+					
+					//break;
+					//return true;
+				}
 				// Get the next line
 				textLine = composeNextLine();
 				if (textLine ==  null)
@@ -1507,7 +1368,7 @@
 			
 			var textLine:TextLine = null;
 			textLine = TextLineRecycler.getLineForReuse();
-			var textBlock:TextBlock = _curParaElement.getTextBlock();
+			var textBlock:TextBlock = _curParaElement.getTextBlockAtPosition(_curElement.getElementRelativeStart(_curParaElement));
 			if (textLine)
 			{
 				CONFIG::debug { assert(_textFlow.backgroundManager == null || _textFlow.backgroundManager.getEntry(textLine) === undefined,"createTextLine - Bad TextLine in recycler cache"); }
@@ -1527,9 +1388,8 @@
 
 			CONFIG::debug { assert(_curParaStart == _curParaElement.getAbsoluteStart(),"bad _curParaStart"); }
 
-			_curLine.initialize(_curParaElement, targetWidth, lineOffset-_parcelList.insideListItemMargin, textLine.textBlockBeginIndex + _curParaStart, textLine.rawTextLength, textLine);
+			_curLine.initialize(_curParaElement, targetWidth, lineOffset-_parcelList.insideListItemMargin, _curLineStart, textLine.rawTextLength, textLine);
 			CONFIG::debug { assert(_curLine.targetWidth == targetWidth,"Bad targetWidth"); }
-
 			return textLine;
 		}
 		
@@ -1553,6 +1413,7 @@
 		/** Called when we are finished composing a line, and it is committed. Handler for derived classes to override default behavior.  */
 		protected function endLine(textLine:TextLine):void	// No PMD
 		{
+			_curLineStart += _curLine.textLength;
 			_contentCommittedExtent = Math.max(_contentCommittedExtent, _workingContentExtent);
 			_contentCommittedHeight = Math.max(_contentCommittedHeight, _workingContentHeight);
 			_contentLogicalExtent = Math.max(_contentLogicalExtent, _workingContentLogicalExtent);
@@ -1922,7 +1783,7 @@
 				bounds = textLine.getAtomBounds(lastAtom != 0 && endOfParagraph ? 1 : 0);						
 				lineWidth -= (_blockProgression == BlockProgression.TB) ? bounds.left : bounds.top;
 			}
-			textLine.flushAtomData();
+			//textLine.flushAtomData(); // Warning: Now does nothing
 			return lineWidth;
 		}
 
@@ -2194,7 +2055,7 @@
 				for (;;)
 				{
 					advanceToNextParcel();
-					if (!_curLine || _parcelList.atEnd() || _parcelList.currentParcel.isTableParcel)
+					if (!_curLine || _parcelList.atEnd())
 						return false;
 					if (_parcelList.getLineSlug(_lineSlug,0, 1, _textIndent, _curParaFormat.direction == Direction.LTR))
 					{
@@ -2857,15 +2718,14 @@
 				{
 					if (oldController == null && _startController)
 						clearControllers(_startController, newController);
-					else if ( ! _curParcel.isTableParcel )
+					else
 						clearControllers(oldController, newController);
 				}
 				if (newController)
 				{
 					CONFIG::debug 
 					{ 
-						if ( ! newParcel.isTableParcel )
-							assert(!oldController || newController.absoluteStart == oldController.absoluteStart + oldController.textLength, "newController not yet set up");
+						assert(!oldController || newController.absoluteStart == oldController.absoluteStart + oldController.textLength, "newController not yet set up");
 					}
 					if (oldController)		// advance the start pos to the next controller if newController isn't the first controller
 						_startComposePosition = newController.absoluteStart;
diff --git a/textLayout/src/flashx/textLayout/compose/ComposeState.as b/textLayout/src/flashx/textLayout/compose/ComposeState.as
index d40dc2b..171e1b6 100644
--- a/textLayout/src/flashx/textLayout/compose/ComposeState.as
+++ b/textLayout/src/flashx/textLayout/compose/ComposeState.as
@@ -27,7 +27,6 @@
 	import flash.text.engine.TextLineCreationResult;
 	import flash.text.engine.TextLineValidity;
 	
-	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.container.ContainerController;
 	import flashx.textLayout.debug.Debugging;
 	import flashx.textLayout.debug.assert;
@@ -47,11 +46,11 @@
 	import flashx.textLayout.formats.ListStylePosition;
 	import flashx.textLayout.formats.TextAlign;
 	import flashx.textLayout.formats.VerticalAlign;
+	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.utils.Twips;
 	
 	use namespace tlf_internal;
 
-	[ExcludeClass]
 	/** Keeps track of internal state during composition. 
 	 * 
 	 * This is the simpler version, used when there are no floats, no wraps, no columns.
@@ -111,7 +110,7 @@
 			vjBeginLineIndex = 0;
 			vjDisableThisParcel = false;
 			
-			return super.composeTextFlow(textFlow, composeToPosition, controllerEndIndex);
+ 			return super.composeTextFlow(textFlow, composeToPosition, controllerEndIndex);
 		}
 		
 		protected override function initializeForComposer(composer:IFlowComposer,composeToPosition:int,controllerStartIndex:int, controllerEndIndex:int):void
@@ -303,7 +302,15 @@
          	if (minY != TextLine.MAX_LINE_WIDTH && Math.abs(minY-_parcelTop) >= 1)
            		_parcelTop = minY;
  		}
- 				
+ 			
+		protected override function endTableBlock(block:TextFlowTableBlock):void
+		{
+			super.endTableBlock(block);
+			(_flowComposer as StandardFlowComposer).addLine(block,_curLineIndex);
+			
+			commitLastLineState (_curLine);
+			_curLineIndex++;
+		}
 		/** Called when we are finished composing a line. Handler for derived classes to override default behavior.  */
 		override protected function endLine(textLine:TextLine):void
 		{
@@ -328,7 +335,8 @@
 		protected override function composeNextLine():TextLine
 		{			
 			// mjzhang: this code adds for recompose a table row, we need to recorrect _curLineIndex parameter based on _curElementStart and _curElementOffset.
-			_curLineIndex = _flowComposer.findLineIndexAtPosition(_curElementStart + _curElementOffset);
+			//Harbs: I don't see a need for this now that I changed the table logic.
+			//_curLineIndex = _flowComposer.findLineIndexAtPosition(_curElementStart + _curElementOffset);
 			
 			CONFIG::debug { assert(_curLineIndex == _flowComposer.findLineIndexAtPosition(_curElementStart + _curElementOffset),"bad _curLineIndex"); }
 
@@ -338,7 +346,6 @@
 			// width in fitLineToParcel to make sure it fits at the (possibly changed) line height.
 			var startCompose:int = _curElementStart + _curElementOffset - _curParaStart;
 			var line:TextFlowLine = _curLineIndex < _flowComposer.numLines ? (_flowComposer as StandardFlowComposer).lines[_curLineIndex] : null;
-			
 			var useExistingLine:Boolean = line && (!line.isDamaged() || line.validity == FlowDamageType.GEOMETRY);
 			if (ContainerController.tlf_internal::usesDiscretionaryHyphens)
 			{
@@ -418,7 +425,7 @@
 				if (fitLineToParcel(textLine, !useExistingLine, numberLine))
 					break;	// we have a good line
 				_curLine = null;	// keep looking
-				if (_parcelList.atEnd() || _parcelList.currentParcel.isTableParcel)
+				if (_parcelList.atEnd())
 				{
 					popInsideListItemMargins(numberLine);
 					return null;
@@ -447,7 +454,7 @@
 			var textLine:TextLine = super.createTextLine(targetWidth, allowEmergencyBreaks);
 			
 			if (textLine)
-	 			textLine.doubleClickEnabled = true;		// allow line to be the target oif a double click event
+	 			textLine.doubleClickEnabled = true;		// allow line to be the target of a double click event
 			else
 				_curLine = null;
  			
diff --git a/textLayout/src/flashx/textLayout/compose/FlowComposerBase.as b/textLayout/src/flashx/textLayout/compose/FlowComposerBase.as
index b03592a..b050201 100644
--- a/textLayout/src/flashx/textLayout/compose/FlowComposerBase.as
+++ b/textLayout/src/flashx/textLayout/compose/FlowComposerBase.as
@@ -230,6 +230,10 @@
 				while (true)
 				{
 					line = _lines[lineIdx];
+					// An empty span following a table can cause this.
+					//if(line == null)
+					//	break;
+					
 					line.setAbsoluteStart(line.absoluteStart + lenToDel + deltaLength);
 					curPos = (startPosition > line.absoluteStart ? startPosition : line.absoluteStart);
 					
@@ -454,7 +458,20 @@
 				_damageAbsoluteStart = newLine.absoluteStart + newLine.textLength;
 				
 			if (workLine == null)
-				lines.push(newLine);				
+				lines.push(newLine);
+			else if((workLine is TextFlowTableBlock) && workLine != newLine)
+				_lines.splice(workIndex,1,newLine);
+			else if(newLine is TextFlowTableBlock)
+			{
+				if(workLine != newLine)
+				{
+					_lines.splice(workIndex,0,newLine);
+					// set the next line absolute start to be rational for the next line...
+					if(workLine.absoluteStart == newLine.absoluteStart)
+						workLine.setAbsoluteStart(workLine.absoluteStart+1);
+				}
+			}
+								
 			else if (workLine.absoluteStart != newLine.absoluteStart)
 			{
 				if (workLine.absoluteStart + workLine.textLength > newLine.absoluteStart + newLine.textLength)
diff --git a/textLayout/src/flashx/textLayout/compose/Parcel.as b/textLayout/src/flashx/textLayout/compose/Parcel.as
index 68769b6..9079d97 100644
--- a/textLayout/src/flashx/textLayout/compose/Parcel.as
+++ b/textLayout/src/flashx/textLayout/compose/Parcel.as
@@ -31,8 +31,8 @@
 
 	use namespace tlf_internal;
 		
-	[ExcludeClass]
-	/** Helper class for implementations of IParcelList
+	/** 
+	 * Helper class for implementations of IParcelList
 	 * 
 	 * @private
 	 */
@@ -43,7 +43,6 @@
 		public var width:Number;
 		public var height:Number;
 		public var logicalWidth:Number;
-		public var isTableParcel:Boolean;
 
 		private var _controller:ContainerController;
 		private var _columnIndex:int;
@@ -73,7 +72,6 @@
 			this.height = height;
 			this.logicalWidth = verticalText ? height : width;
 			this._verticalText = verticalText; 
-			this.isTableParcel = false;
 			
 			_controller   = controller;
 			_columnIndex  =  columnIndex;
diff --git a/textLayout/src/flashx/textLayout/compose/ParcelList.as b/textLayout/src/flashx/textLayout/compose/ParcelList.as
index fc3b516..b4e2a7b 100644
--- a/textLayout/src/flashx/textLayout/compose/ParcelList.as
+++ b/textLayout/src/flashx/textLayout/compose/ParcelList.as
@@ -25,7 +25,7 @@
 	import flashx.textLayout.container.ContainerController;
 	import flashx.textLayout.container.ScrollPolicy;
 	import flashx.textLayout.debug.assert;
-	import flashx.textLayout.elements.TableDataCellElement;
+	import flashx.textLayout.elements.TableCellElement;
 	import flashx.textLayout.formats.BlockProgression;
 	import flashx.textLayout.formats.ITextLayoutFormat;
 	import flashx.textLayout.formats.LineBreak;
@@ -35,7 +35,6 @@
 	
 	use namespace tlf_internal;
 			
-	[ExcludeClass]
 	/** @private
 	 * Used for composing text containers, keeps track of the areas that text in the 
 	 * flow is composed into.
@@ -208,7 +207,7 @@
 			return _parcelArray.pop();
 		}
 		
-		public function addTableCell2ColumnState(controller:ContainerController, cell:TableDataCellElement):void
+		public function addTableCell2ColumnState(controller:ContainerController, cell:TableCellElement):void
 		{
 			var columnState:ColumnState = controller.columnState;
 			if (columnState)
diff --git a/textLayout/src/flashx/textLayout/compose/StandardFlowComposer.as b/textLayout/src/flashx/textLayout/compose/StandardFlowComposer.as
index e1cb140..ca4b66e 100644
--- a/textLayout/src/flashx/textLayout/compose/StandardFlowComposer.as
+++ b/textLayout/src/flashx/textLayout/compose/StandardFlowComposer.as
@@ -65,7 +65,7 @@
 	public class StandardFlowComposer extends FlowComposerBase implements IFlowComposer
 	{
 		/** @private */
-		tlf_internal var _rootElement:ContainerFormattedElement;
+		protected var _rootElement:ContainerFormattedElement;
 		private var _controllerList:Array;
 		private var _composing:Boolean;
 
diff --git a/textLayout/src/flashx/textLayout/compose/TextFlowLine.as b/textLayout/src/flashx/textLayout/compose/TextFlowLine.as
index 11b8511..bd6e015 100644
--- a/textLayout/src/flashx/textLayout/compose/TextFlowLine.as
+++ b/textLayout/src/flashx/textLayout/compose/TextFlowLine.as
@@ -52,6 +52,8 @@
 	import flashx.textLayout.elements.SpanElement;
 	import flashx.textLayout.elements.SubParagraphGroupElementBase;
 	import flashx.textLayout.elements.TCYElement;
+	import flashx.textLayout.elements.TableElement;
+	import flashx.textLayout.elements.TableLeafElement;
 	import flashx.textLayout.elements.TextFlow;
 	import flashx.textLayout.factory.StringTextLineFactory;
 	import flashx.textLayout.formats.BackgroundColor;
@@ -92,7 +94,7 @@
 	 * @langversion 3.0
 	 */
 	
-	public final class TextFlowLine implements IVerticalJustificationLine 
+	public class TextFlowLine implements IVerticalJustificationLine 
 	{
 		
 		/** @private - the selection block cache */
@@ -126,10 +128,10 @@
 		
 		// added to support TextFlowLine when TextLine not available
 
-		private var _ascent:Number;
-		private var _descent:Number;
+		protected var _ascent:Number;
+		protected var _descent:Number;
 		private var _targetWidth:Number;
-		private var _lineOffset:Number;
+		protected var _lineOffset:Number;
 		private var _lineExtent:Number;	// content bounds logical width for the line
 		private var _accumulatedLineExtent:Number;
 		private var _accumulatedMinimumStart:Number;
@@ -356,8 +358,14 @@
 		{
 			if (_para)
 			{
-				var lineStart:int = _absoluteStart - _para.getAbsoluteStart();
+				var lineStart:int;
 				
+				// Harbs 8-31-14 added handling of multiple textBlocks might need more work to handle end?
+				var textLine:TextLine = peekTextLine();
+				if(textLine)
+					lineStart = _absoluteStart - _para.getTextBlockAbsoluteStart(textLine.textBlock);
+				else
+					lineStart = _absoluteStart - _para.getAbsoluteStart();
 				// Initialize settings for location
 				if (lineStart == 0)		// we're at the start of the paragraph
 					return _textLength == _para.textLength ? TextFlowLineLocation.ONLY : TextFlowLineLocation.FIRST;
@@ -797,15 +805,16 @@
 				return null;
 						
 			// Look it up in the textBlock
-			var textBlock:TextBlock = paragraph.peekTextBlock();
-			if (textBlock)
+			var textBlocks:Vector.<TextBlock> = paragraph.getTextBlocks();
+			for each(var textBlock:TextBlock in textBlocks)
 			{
 				for (textLine = textBlock.firstLine; textLine; textLine = textLine.nextLine)
 				{
 					if (textLine.userData == this) // found it
 						return textLine;
-					}
 				}
+				
+			}
 			return null;
 		}
 		
@@ -850,13 +859,14 @@
 		}
 
 		private function getTextLineInternal():TextLine
-		{			
+		{		
+			// 8-31-14 Do we need to change this to handle multiple textBlocks?
 			// Look it up in the textBlock
 			var paraAbsStart:int = paragraph.getAbsoluteStart();
 			
 			// If we haven't found it yet, we need to regenerate it.
 			// Regenerate the whole paragraph at once, up to the current position. 
-			var textBlock:TextBlock = paragraph.getTextBlock();
+			var textBlock:TextBlock = paragraph.getTextBlockAtPosition(absoluteStart - paraAbsStart);
 			var currentLine:TextLine = textBlock.firstLine;
 			var flowComposer:IFlowComposer = paragraph.getTextFlow().flowComposer;
 			var lineIndex:int = flowComposer.findLineIndexAtPosition(paraAbsStart);
@@ -871,6 +881,11 @@
 					textLine = currentLine;
 					currentLine = currentLine.nextLine;
 				}
+				else if(line is TextFlowTableBlock)
+				{
+					textLine = null;
+					currentLine = null;
+				}
 				else
 				{
 					textLine = line.recreateTextLine(textBlock, previousLine);
@@ -1205,6 +1220,8 @@
 					break;
 				elem = elem.getNextLeaf(_para);
 				CONFIG::debug { assert(elem != null,"bad nextLeaf"); }
+				if(elem == null)
+					break;
 			}
 			return totalLeading;
 		}
@@ -1367,8 +1384,11 @@
 			if (isDamaged())
 				return null;
 			
+			// 8-31-14 Do we need to adjust this for paras with multiple textBlocks? 
 			//get the absolute start of the paragraph.  Calculation is expensive, so just do this once.
-			var paraAbsStart:int = _para.getAbsoluteStart();
+			//var paraAbsStart:int = _para.getAbsoluteStart();
+			var textLine:TextLine = getTextLine();
+			var paraAbsStart:int = _para.getTextBlockAbsoluteStart(textLine.textBlock);
 			
 			//if the indexes are identical and are equal to the start of the line, then
 			//don't draw anything.  This prevents a bar being drawn on a following line when
@@ -1402,7 +1422,6 @@
 			selectionCache.begIdx = begIdx;
 			selectionCache.endIdx = endIdx;
 			
-			var textLine:TextLine = getTextLine();
 			var heightAndAdj:Array = getRomanSelectionHeightAndVerticalAdjustment(prevLine, nextLine);
 			calculateSelectionBounds(textLine, drawRects, begIdx, endIdx, blockProgression, heightAndAdj);
 			
@@ -1416,8 +1435,9 @@
 			
 			
 			//allow the atoms to be garbage collected.
-			if (textLine)
-				textLine.flushAtomData();
+			//if (textLine) {
+				//textLine.flushAtomData(); // Warning: Now does nothing
+			//}
 			
 			return selectionCache;
 		}
@@ -1429,7 +1449,8 @@
 			//the direction of the text
 			var direction:String = _para.computedFormat.direction;
 			//get the absolute start of the paragraph.  Calculation is expensive, so just do this once.
-			var paraAbsStart:int = _para.getAbsoluteStart();
+			//var paraAbsStart:int = _para.getAbsoluteStart();
+			var paraAbsStart:int = _para.getTextBlockAbsoluteStart(textLine.textBlock);
 			//the current index.  used to iterate to the next element
 			var curIdx:int = begIdx;
 			//the current FlowLeafElement as determined by curIdx
@@ -1472,6 +1493,18 @@
 				}
 				//the number of potential glyphs to hilite.  Could larger than needs be if we are only selecting part of it.
 				var numCharsSelecting:int = curElem.textLength + curElem.getElementRelativeStart(_para) - curIdx;
+				// special handling for TableLeafElements (do nothing)
+				if(curElem is TableLeafElement)
+				{
+					//if(floatRectArray == null)
+					//	floatRectArray = new Array();
+
+					//var block:TextFlowTableBlock = TableElement(TableLeafElement(curElem).parent).getFirstBlock();
+					//var blockRect:Rectangle = new Rectangle(floatInfo.x - textLine.x, floatInfo.y - textLine.y, ilg.elementWidth, ilg.elementHeight);
+					//floatRectArray.push(new Rectangle(0,0,block.width,block.height));
+					++curIdx;
+					continue;
+				}
 				//the index of the last glyph to hilite.  If a partial selection, use endIdx
 				var endPos:int = (numCharsSelecting + curIdx) > endIdx ? endIdx : (numCharsSelecting + curIdx);
 				
@@ -1753,7 +1786,7 @@
 		
 		/** @private 
 		 * 
-		 * 
+		 * ? Get a list of rects of the characters in the given textline? Used to show selection? JF 
 		 */
 		private function makeSelectionBlocks(textLine:TextLine, begIdx:int, endIdx:int, paraAbsStart:int, blockProgression:String, direction:String, heightAndAdj:Array):Array
 		{
@@ -1918,7 +1951,7 @@
 		
 		/** @private 
 		 * 
-		 * 
+		 * ? Get the bounds of the supplied range of characters in the given textline? Used to show selection? JF 
 		 */
 		private function makeBlock(textLine:TextLine, begTextIndex:int, begAtomIndex:int, endAtomIndex:int, startMetrics:Rectangle, blockProgression:String, direction:String, heightAndAdj:Array):Rectangle
 		{
@@ -2157,7 +2190,7 @@
 			if (!textLine || !textLine.parent)
 				return;
 			
-			var paraStart:int = _para.getAbsoluteStart();
+			var paraStart:int = _para.getTextBlockAbsoluteStart(textLine.textBlock);
 			begIdx -= paraStart;
 			endIdx -= paraStart;
 			
@@ -2198,7 +2231,9 @@
 			if (!textLine || !textLine.parent)
 				return null;			
 			// adjust to this paragraph's TextBlock
-			idx -= _para.getAbsoluteStart();
+			// I'm assuming this needs to be relative to the TextBlock and not the paragraph -- Harbs
+				idx -= _para.getTextBlockAbsoluteStart(textLine.textBlock);
+			//idx -= _para.getAbsoluteStart();
 			
 			textLine = getTextLine(true);
 			
@@ -2247,7 +2282,7 @@
 			}
 			
 			var heightAndAdj:Array = getRomanSelectionHeightAndVerticalAdjustment(prevLine, nextLine);
-			var blockRectArray:Array = makeSelectionBlocks(textLine, idx, endIdx, _para.getAbsoluteStart(), blockProgression, direction, heightAndAdj);
+			var blockRectArray:Array = makeSelectionBlocks(textLine, idx, endIdx, _para.getTextBlockAbsoluteStart(textLine.textBlock), blockProgression, direction, heightAndAdj);
 			CONFIG::debug{ assert(blockRectArray.length == 1, "A point selection should return a single selection rectangle!"); }
 			var rect:Rectangle = blockRectArray[0];
 			
@@ -2291,7 +2326,7 @@
 			}
 			
 			//allow the atoms to be garbage collected.
-			textLine.flushAtomData();
+			//textLine.flushAtomData(); // Warning: Now does nothing
 			
 			return rect;
 		}
@@ -2320,7 +2355,12 @@
 			}
 			else
 			{
-				var paraStart:int = _para.getAbsoluteStart();
+				var paraStart:int;
+				//8-31-14 Assuming this should be from the textBlock. Keeping getAbsoluteStart() in case there's no textLine -- not sure if that's needed
+				if(textLine)
+					paraStart = _para.getTextBlockAbsoluteStart(textLine.textBlock);
+				else
+					paraStart = _para.getAbsoluteStart();
 				var selCache:SelectionCache = this.getSelectionShapesCacheEntry(begIdx-paraStart,endIdx-paraStart,prevLine,nextLine,blockProgression);
 				if (selCache)
 				{
@@ -2490,6 +2530,8 @@
 		/** @private */
 		static tlf_internal function findNumberLine(textLine:TextLine):TextLine
 		{
+			if(textLine == null)
+				return null;
 			// not always going to be a numberLine - listStyleType may be "none"
 			// have to hunt for it because inlinegraphics get pushed at the beginning
 			// risk here is that clients decorate TextLines with other TextLines.
@@ -2715,7 +2757,7 @@
 				}
 			}
 		}
-		numberLine.flushAtomData();
+		// numberLine.flushAtomData(); // Warning: Now does nothing
 		//trace("textWidth",numberLine.textWidth,maxVal-minVal);
 		return maxVal > minVal ? maxVal-minVal : 0;
 	}
diff --git a/textLayout/src/flashx/textLayout/compose/TextFlowTableBlock.as b/textLayout/src/flashx/textLayout/compose/TextFlowTableBlock.as
new file mode 100644
index 0000000..b37c804
--- /dev/null
+++ b/textLayout/src/flashx/textLayout/compose/TextFlowTableBlock.as
@@ -0,0 +1,223 @@
+package flashx.textLayout.compose
+{
+	
+	import flash.display.DisplayObject;
+	import flash.display.Shape;
+	import flash.text.engine.TextLine;
+	
+	import flashx.textLayout.container.ContainerController;
+	import flashx.textLayout.edit.SelectionFormat;
+	import flashx.textLayout.elements.CellContainer;
+	import flashx.textLayout.elements.CellCoordinates;
+	import flashx.textLayout.elements.ParagraphElement;
+	import flashx.textLayout.elements.TableBlockContainer;
+	import flashx.textLayout.elements.TableCellElement;
+	import flashx.textLayout.elements.TableElement;
+	import flashx.textLayout.elements.TextFlow;
+	import flashx.textLayout.tlf_internal;
+	
+	use namespace tlf_internal;
+
+	/**
+	 * 
+	 **/
+	public class TextFlowTableBlock extends TextFlowLine
+	{
+		
+		private var _textHeight:Number;
+		
+		/** Constructor - creates a new TextFlowTableBlock instance. 
+		 *  <p><strong>Note</strong>: No client should call this. It's exposed for writing your own composer.</p>
+		 *
+		 * @param index The index in the Table text flow.
+		 * */
+		public function TextFlowTableBlock(index:uint)
+		{
+			blockIndex = index;
+			_container = new TableBlockContainer();
+			super(null,null);
+		}
+		
+		/**
+		 * @inheritDoc
+		 **/
+		override tlf_internal function initialize(paragraph:ParagraphElement, outerTargetWidth:Number = 0, lineOffset:Number = 0, absoluteStart:int = 0, numChars:int = 0, textLine:TextLine = null):void
+		{
+			_container.userData = this;
+			_lineOffset = lineOffset;
+
+			super.initialize(paragraph, outerTargetWidth, lineOffset, absoluteStart, numChars, textLine);
+		}
+		override tlf_internal function setController(cont:ContainerController,colNumber:int):void
+		{
+			super.setController(cont, colNumber);
+			if(cont)
+				controller.addComposedTableBlock(container);
+		}
+
+		
+		/**
+		 * The table that owns this table block
+		 **/
+		public var parentTable:TableElement;
+		
+		/**
+		 * The index of this block in the table text flow layout
+		 **/
+		public var blockIndex:uint = 0;
+		
+		/**
+		 * @private
+		 **/
+		private var _container:TableBlockContainer;
+		
+		private var _cells:Array;
+		
+		/**
+		 * Returns an array of table cells. 
+		 * @private
+		 **/
+		private function getCells():Array{
+			if(_cells == null){
+				_cells = [];
+			}
+			return _cells;
+		}
+		
+		/**
+		 * Returns a vector of table cell elements in the given cell range. 
+		 **/
+		public function getCellsInRange(anchorCoords:CellCoordinates,activeCoords:CellCoordinates):Vector.<TableCellElement>
+		{
+			if(!parentTable)
+				return null;
+			return parentTable.getCellsInRange(anchorCoords,activeCoords,this);
+		}
+		
+		/**
+		 * Clears the cells in the table block. Wraps clearCells(). 
+		 **/
+		public function clear():void{
+			clearCells();
+		}
+		
+		/**
+		 * Clears the cells in the table block
+		 **/
+		public function clearCells():void{
+			_container.removeChildren();
+			getCells().length = 0;
+		}
+		
+		/**
+		 * Adds a cell container to table container. This adds it to the display list. 
+		 * If the cell is already added it does not add it twice. 
+		 **/
+		public function addCell(cell:CellContainer):void{
+			var cells:Array = getCells();
+			if(cells.indexOf(cell) < 0){
+				cells.push(cell);
+				_container.addChild(cell);
+			}
+		}
+		
+		
+		public function drawBackground(backgroundInfo:*):void{
+			//TODO: need to figure this out...
+			
+		}
+		
+		/**
+		 * Container that displays this collection of cells
+		 **/
+		public function get container():TableBlockContainer

+		{
+			return _container;
+		}
+		
+		/**
+		 * Triggers drawing of composed cell contents
+		 **/
+		public function updateCompositionShapes():void{
+			var cells:Array = getCells();
+			for each(var cell:CellContainer in cells){
+				cell.element.updateCompositionShapes();
+			}
+		}
+
+		/**
+		 * Sets the height of the container 
+		 **/
+		public function set height(value:Number):void{
+			//_container.height = value;
+			_textHeight = value;
+		}
+		
+		/**
+		 * @inheritDoc
+		 **/
+		override public function get height():Number{
+			return _textHeight;
+		}
+		/**
+		 * Sets the width of the container 
+		 **/
+		public function set width(value:Number):void{
+			_container.width = value;
+		}
+		
+		/**
+		 * Gets the width of the container 
+		 **/
+		public function get width():Number{
+			return _container.width;
+		}
+		
+		/**
+		 * Sets the x position of the container
+		 **/
+		override public function set x(value:Number):void{
+			super.x = _container.x = value;
+		}
+		
+		override public function get x():Number{
+			return _container.x;
+		}
+		
+		/**
+		 * Sets the y value of the container
+		 **/
+		override public function set y(value:Number):void{
+			super.y = _container.y = value;
+		}
+		override public function get y():Number{
+			return _container.y;
+		}
+		
+		/**
+		 * Returns a vector of table cell elements.
+		 **/
+		public function getTableCells():Vector.<TableCellElement>
+		{
+			var tCells:Vector.<TableCellElement> = new Vector.<TableCellElement>();
+			var cells:Array = getCells();
+			
+			for each(var cellContainer:CellContainer in cells){
+				tCells.push(cellContainer.element);
+			}
+			
+			return tCells;
+		}
+
+		public override function get textHeight():Number

+		{
+			return _textHeight;
+		}
+		
+		tlf_internal override function hiliteBlockSelection(selObj:Shape, selFormat:SelectionFormat, container:DisplayObject, begIdx:int, endIdx:int, prevLine:TextFlowLine, nextLine:TextFlowLine):void
+		{
+			// do nothing for now...
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/textLayout/src/flashx/textLayout/container/ColumnState.as b/textLayout/src/flashx/textLayout/container/ColumnState.as
index 40a54e1..e84dd64 100644
--- a/textLayout/src/flashx/textLayout/container/ColumnState.as
+++ b/textLayout/src/flashx/textLayout/container/ColumnState.as
@@ -21,7 +21,7 @@
 	import flash.geom.Rectangle;
 	
 	import flashx.textLayout.debug.assert;
-	import flashx.textLayout.elements.TableDataCellElement;
+	import flashx.textLayout.elements.TableCellElement;
 	import flashx.textLayout.formats.BlockProgression;
 	import flashx.textLayout.formats.Direction;
 	import flashx.textLayout.formats.FormatValue;
@@ -181,12 +181,12 @@
 			return _columnCount == 1 ? _singleColumn : _columnArray[index];
 		}
 		
-		public function getCellAt(index:int):TableDataCellElement
+		public function getCellAt(index:int):TableCellElement
 		{
 			return _tableCellArray[index];
 		}
 		
-		public function pushTableCell(cell:TableDataCellElement):void
+		public function pushTableCell(cell:TableCellElement):void
 		{
 			if ( _tableCellArray == null )
 				_tableCellArray = new Array();
diff --git a/textLayout/src/flashx/textLayout/container/ContainerController.as b/textLayout/src/flashx/textLayout/container/ContainerController.as
index f992734..a000035 100644
--- a/textLayout/src/flashx/textLayout/container/ContainerController.as
+++ b/textLayout/src/flashx/textLayout/container/ContainerController.as
@@ -80,6 +80,53 @@
 
     use namespace tlf_internal;
 	
+	import flashx.textLayout.compose.FloatCompositionData;
+	import flashx.textLayout.compose.FlowComposerBase;
+	import flashx.textLayout.compose.FlowDamageType;
+	import flashx.textLayout.compose.IFlowComposer;
+	import flashx.textLayout.compose.TextFlowLine;
+	import flashx.textLayout.compose.TextFlowTableBlock;
+	import flashx.textLayout.compose.TextLineRecycler;
+	import flashx.textLayout.debug.Debugging;
+	import flashx.textLayout.debug.assert;
+	import flashx.textLayout.edit.EditingMode;
+	import flashx.textLayout.edit.IInteractionEventHandler;
+	import flashx.textLayout.edit.ISelectionManager;
+	import flashx.textLayout.edit.SelectionFormat;
+	import flashx.textLayout.elements.BackgroundManager;
+	import flashx.textLayout.elements.CellCoordinates;
+	import flashx.textLayout.elements.CellRange;
+	import flashx.textLayout.elements.Configuration;
+	import flashx.textLayout.elements.ContainerFormattedElement;
+	import flashx.textLayout.elements.FlowElement;
+	import flashx.textLayout.elements.FlowLeafElement;
+	import flashx.textLayout.elements.FlowValueHolder;
+	import flashx.textLayout.elements.InlineGraphicElement;
+	import flashx.textLayout.elements.LinkElement;
+	import flashx.textLayout.elements.ParagraphElement;
+	import flashx.textLayout.elements.TableBlockContainer;
+	import flashx.textLayout.elements.TableCellElement;
+	import flashx.textLayout.elements.TableElement;
+	import flashx.textLayout.elements.TableRowElement;
+	import flashx.textLayout.elements.TextFlow;
+	import flashx.textLayout.events.FlowElementMouseEvent;
+	import flashx.textLayout.events.FlowElementMouseEventManager;
+	import flashx.textLayout.events.ModelChange;
+	import flashx.textLayout.events.ScrollEvent;
+	import flashx.textLayout.events.ScrollEventDirection;
+	import flashx.textLayout.events.TextLayoutEvent;
+	import flashx.textLayout.events.UpdateCompleteEvent;
+	import flashx.textLayout.formats.BlockProgression;
+	import flashx.textLayout.formats.Float;
+	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.textLayout.utils.Twips;
+	
+	use namespace tlf_internal;
+	
 	/** 
 	 * 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
@@ -109,7 +156,6 @@
 	public class ContainerController implements IInteractionEventHandler, ITextLayoutFormat, ISandboxSupport
 	{		
 		static tlf_internal var usesDiscretionaryHyphens:Boolean = true;
-		static tlf_internal var startComposeFromBeginning:Boolean = false;
 		
 		private var _textFlowCache:TextFlow;
 		private var _rootElement:ContainerFormattedElement;
@@ -175,6 +221,7 @@
 		
 		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
@@ -264,6 +311,7 @@
 			
 			_shapeChildren = [ ];
 			_linesInView = [ ];
+			_tableBlocksInView = [];
 			
 			setCompositionSize(compositionWidth, compositionHeight);
 			format = _containerControllerInitialFormat;
@@ -394,7 +442,7 @@
 		/** 
 		 * 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>TextLine.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 ready back in <code>getContentBounds</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.
 		 *
@@ -412,7 +460,7 @@
 		 * @langversion 3.0
 		 */
 		
-		public function setCompositionSize(w:Number,h:Number):void
+		public function setCompositionSize(w:Number, h:Number):void
 		{
 		//	trace("setCompositionSize(" + w + ", " + h + ")");
 			
@@ -734,13 +782,19 @@
 				var curLine:TextFlowLine;
 				var textLine:TextLine;
 				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
-					curLine = flowComposer.getLineAt(firstLine);	
-					textLine = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null) as TextLine;
+					var testPos:int = firstLine;
+					curLine = flowComposer.getLineAt(testPos++);
+					while(curLine && curLine is TextFlowTableBlock)
+						curLine = flowComposer.getLineAt(testPos++);
+					
+					testRslt = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null)
+					textLine = testRslt as TextLine;
 					firstLine++;	// its been tested
 					if (textLine)
 					{
@@ -756,10 +810,11 @@
 							var mid:int = (firstLine+hi)/2;
 							CONFIG::debug { assert(mid != 0,"ContainerController:gatherVisibleLines: bad mid"); }
 							curLine = flowComposer.getLineAt(mid);
-							var testRslt:* = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null);
-							textLine = testRslt as TextLine;
-							if (textLine)
+							testRslt = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null);
+							
+							if (testRslt && testRslt is TextLine)
 							{
+								textLine = testRslt as TextLine;
 								// note that we tested firstLine above so going to mid-1 is always valid
 								var tempLine:TextFlowLine = flowComposer.getLineAt(mid-1);
 								if (!(testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, tempLine, null) is TextLine))
@@ -773,6 +828,8 @@
 								}
 								testRslt = -1;	// past the start
 							}
+							// need to deal with TextFlowTableBlocks
+							
 							if (testRslt < 0 || testRslt == 2)
 								hi = mid-1;
 							else
@@ -782,8 +839,12 @@
 					
 					for (lineIndex = firstLine; lineIndex <= lastLine; lineIndex++)
 					{
-						curLine = flowComposer.getLineAt(lineIndex);	
-						textLine = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null) as TextLine;
+						curLine = flowComposer.getLineAt(lineIndex);
+						testRslt = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null);
+						
+						if(testRslt is TableBlockContainer)
+							continue;
+						textLine = testRslt as TextLine;
 						if (!textLine)
 							break;
 		
@@ -2815,6 +2876,42 @@
 			addSelectionChild(selObj);
 		}
 		
+		/** Add cell selection shapes to the displaylist. @private */
+		tlf_internal function addCellSelectionShapes(color:uint, tableBlock:TextFlowTableBlock, startCoords:CellCoordinates, endCoords:CellCoordinates): void
+		{
+			if(!tableBlock)
+				return;
+			if(!startCoords.isValid() || !endCoords.isValid())
+				return;
+			var cells:Vector.<TableCellElement> = tableBlock.getCellsInRange(startCoords,endCoords);
+			var selObj:Shape = new Shape();
+			selObj.graphics.beginFill(color);
+			for each( var cell:TableCellElement in cells)
+			{
+				var row:TableRowElement = cell.getRow();
+				var r:Rectangle = new Rectangle(cell.x, cell.y + tableBlock.y, cell.width, row.composedHeight);
+				selObj.graphics.drawRect(r.x,r.y,r.width,r.height);
+			}
+			addSelectionChild(selObj);
+		}
+		
+		/** 
+		 * Add cell selection shapes to the displaylist.
+		 * */
+		tlf_internal function addCellSelections(cells:Array, color:uint, tableBlock:TextFlowTableBlock): void
+		{
+			var shape:Shape = new Shape();
+			shape.graphics.beginFill(color);
+			
+			for each(var cell:TableCellElement in cells) {
+				var row:TableRowElement = cell.getRow();
+				var rectangle:Rectangle = new Rectangle(cell.x, cell.y + tableBlock.y, cell.width, row.composedHeight);
+				shape.graphics.drawRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
+			}
+			
+			addSelectionChild(shape);
+		}
+		
 		/** Add selection shapes to the displaylist. @private */
 		tlf_internal function addSelectionShapes(selFormat:SelectionFormat, selectionAbsoluteStart:int, selectionAbsoluteEnd:int): void
 		{
@@ -2859,6 +2956,7 @@
 				{
 					nextLine = idx != flowComposer.numLines - 1 ? flowComposer.getLineAt(idx+1) : null;
 					
+					// 9-1-14 Harbs Do we draw a selection rect for tables? If yes, this needs special handling in TextFlowTableBlock
 					line.hiliteBlockSelection(selObj, selFormat, this._container,
 						selectionAbsoluteStart < line.absoluteStart ? line.absoluteStart : selectionAbsoluteStart,
 						selectionAbsoluteEnd > line.absoluteStart+line.textLength ? line.absoluteStart+line.textLength : selectionAbsoluteEnd, prevLine, nextLine);
@@ -2907,7 +3005,7 @@
 		{
 			// If there's no selectionSprite on this controller, we use the parent's.
 			// That means we have to translate the coordinates.
-			// TODO: this only supports one level of ntesting
+			// TODO: this only supports one level of nesting
 			var selectionSprite:DisplayObjectContainer = getSelectionSprite(true);
 			
 			if (selectionSprite == null)
@@ -2922,7 +3020,7 @@
 				selectionSprite.blendMode = curBlendMode;
 			
 			if (selectionSprite.alpha != curAlpha)
-				selectionSprite.alpha = curAlpha;
+				selectionSprite.alpha = 1;//curAlpha; testing remove this 
 			
 			if (selectionSprite.numChildren == 0)
 				addSelectionContainer(selectionSprite);
@@ -3046,10 +3144,13 @@
 		{
 			setTextLength(0); 
 			
-			for each (var textLine:TextLine in _shapeChildren)
+			for each (var line:* in _shapeChildren)
 			{
-				removeTextLine(textLine);
-				CONFIG::debug { Debugging.traceFTECall(null,_container,"removeTextLine",textLine); }
+				if(line is TextLine)
+					removeTextLine(line as TextLine);
+				else
+					removeTableBlock(line as TableBlockContainer);
+				CONFIG::debug { Debugging.traceFTECall(null,_container,"removeTextLine",line); }
 			}
 			_shapeChildren.length = 0;
 			_linesInView.length = 0;
@@ -3062,6 +3163,21 @@
 		
 		private static var scratchRectangle:Rectangle = new Rectangle();
 		
+		private function intersperseTableBlocks(targetArray:Array):void{
+			if(_tableBlocksInView.length == 0)
+				return;
+			var blockIdx:int = 0;
+			var startLoc:int = (_tableBlocksInView[0] as TableBlockContainer).userData.parentTable.getAbsoluteStart();
+			for(var i:int=0;i<targetArray.length;i++){
+				if( targetArray[i].userData.absoluteStart < startLoc )
+					continue;
+				targetArray.splice(i,0,_tableBlocksInView[blockIdx++]);
+				if(blockIdx == _tableBlocksInView.length)
+					break;
+			}
+			while(blockIdx < _tableBlocksInView.length)
+				targetArray.push(_tableBlocksInView[blockIdx++]);
+		}
 		/** Add DisplayObjects that were created by composition to the container. @private */
 		tlf_internal function updateCompositionShapes():void
 		{
@@ -3089,6 +3205,9 @@
 			fillShapeChildren();
 			var newShapeChildren:Array = _linesInView;
 			
+			// Add in table blocks
+			intersperseTableBlocks(newShapeChildren);
+			
 			var childIdx:int = getFirstTextLineChildIndex(); // index where the first text line must appear at in its container  
 			var newIdx:int = 0;		// offset into newShapeChildren
 			var shapeChildrenStartIdx:int = 0;	// starting offset into shapeChildren
@@ -3100,11 +3219,14 @@
 			// beginning as usual. This can happen if we're scrolled forward, and then edit the first visible line.
 			if (_updateStart > absoluteStart && newShapeChildren.length > 0)
 			{
-				var firstTextLine:TextLine = newShapeChildren[0];
-				var firstLine:TextFlowLine = TextFlowLine(firstTextLine.userData);
+				var firstLine:TextFlowLine = TextFlowLine(newShapeChildren[0].userData);
 				var prevLine:TextFlowLine = flowComposer.findLineAtPosition(firstLine.absoluteStart - 1);
-				var prevTextLine:TextLine = prevLine.peekTextLine(); 
-				shapeChildrenStartIdx = _shapeChildren.indexOf(prevTextLine);
+				if(prevLine is TextFlowTableBlock){
+					shapeChildrenStartIdx = _shapeChildren.indexOf((prevLine as TextFlowTableBlock).container);
+				} else {
+					var prevTextLine:TextLine = prevLine.peekTextLine(); 
+					shapeChildrenStartIdx = _shapeChildren.indexOf(prevTextLine);
+				}
 				if (shapeChildrenStartIdx >= 0)
 				{
 					shapeChildrenStartIdx++;
@@ -3117,7 +3239,7 @@
 			
 			while (newIdx != newShapeChildren.length)
 			{
-				var newChild:TextLine = newShapeChildren[newIdx];
+				var newChild:* = newShapeChildren[newIdx];
 				if (newChild == _shapeChildren[oldIdx])
 				{
 					// Same shape is in both lists, no change necessary, advance to next item in each list
@@ -3125,23 +3247,45 @@
 					childIdx++;
 					newIdx++;
 					oldIdx++;
+					if(newChild is TableBlockContainer)
+					{
+						// update the contents in case cells were added or removed. (There might be a more efficient way to do this, but this works.)
+						(newChild as TableBlockContainer).userData.updateCompositionShapes();
+					}
 					continue;
 				}
-				
 				var newChildIdx:int = _shapeChildren.indexOf(newChild);
-				if (newChildIdx == -1)
-				{
-					// Shape is in the new list, but not in the old list, add it to the display list at the current location, and advance to next item
-					addTextLine(newChild, childIdx++);
-					CONFIG::debug { Debugging.traceFTECall(null,_container,"addTextLine",newChild); }
-					newIdx++;
-				}
-				else
-				{
+				if(newChild is TextLine){
+					if (newChildIdx == -1)
+					{
+						// Shape is in the new list, but not in the old list, add it to the display list at the current location, and advance to next item
+						addTextLine((newChild as TextLine), childIdx++);
+						CONFIG::debug { Debugging.traceFTECall(null,_container,"addTextLine",newChild); }
+						newIdx++;
+					}
+					else
+					{
 						// The shape is on both lists, but there are several intervening "old" shapes in between. We'll remove the old shapes that
-					// come before the new one we want to insert.
-					removeAndRecycleTextLines (oldIdx, newChildIdx);
-					oldIdx = newChildIdx;
+						// come before the new one we want to insert.
+						removeAndRecycleTextLines (oldIdx, newChildIdx);
+						oldIdx = newChildIdx;
+					}
+				} else {// it's a table block
+					if (newChildIdx == -1)
+					{
+						// Shape is in the new list, but not in the old list, add it to the display list at the current location, and advance to next item
+						addTableBlock((newChild as TableBlockContainer), childIdx++);
+						CONFIG::debug { Debugging.traceFTECall(null,_container,"addTableBlock",newChild); }
+						newIdx++;
+					}
+					else
+					{
+						// The shape is on both lists, but there are several intervening "old" shapes in between. We'll remove the old shapes that
+						// come before the new one we want to insert.
+						(newChild as TableBlockContainer).userData.updateCompositionShapes();
+						removeAndRecycleTextLines (oldIdx, newChildIdx);
+						oldIdx = newChildIdx;
+					}					
 				}
 			}
 			
@@ -3153,7 +3297,8 @@
 			{
 				// We only updated some of the lines. Remove the old versions off the end, and add in the new ones from _linesInView
 				_shapeChildren.length = shapeChildrenStartIdx;		// truncate
-				_shapeChildren = _shapeChildren.concat(_linesInView);	// append _linesInView to end of _shapeChildren
+				//_shapeChildren = _shapeChildren.concat(_linesInView);	// append _linesInView to end of _shapeChildren
+				_shapeChildren = _shapeChildren.concat(newShapeChildren);	// append _linesInView to end of _shapeChildren
 				_linesInView.length = 0;	// truncate
 			}
 			else
@@ -3993,6 +4138,12 @@
 			var textBlock:TextBlock;
 			for (var index:int = beginIndex; index < endIndex; index++)
 			{
+				// we don't recycle table blocks
+				if( !(_shapeChildren[index] is TextLine) ){
+					removeTableBlock(_shapeChildren[index]);
+					child = null;
+					continue;
+				}
 				child = _shapeChildren[index];					
 				removeTextLine(child);
 				CONFIG::debug { Debugging.traceFTECall(null,_container,"removeTextLine",child); }
@@ -4017,6 +4168,10 @@
 			{
 				while (beginIndex < endIndex)
 				{
+					if( !(_shapeChildren[beginIndex] is TextLine) ){
+						beginIndex++;
+						continue;
+					}
 					child = _shapeChildren[beginIndex++];
 										
 					// Recycle if its not displayed and not connected to the textblock
@@ -4073,6 +4228,10 @@
 				{
 					break;
 				}
+				
+				if(_container.getChildAt(firstTextLine) is TableBlockContainer)
+					break;
+				
 			}
 			return firstTextLine;
 		}
@@ -4125,6 +4284,54 @@
 		}
 		
 		/**
+		 * Adds a <code>TableBlockContainer</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>TableBlockContainer</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 #container
+		 * 
+		 */	
+		protected function addTableBlock(block:TableBlockContainer, index:int):void
+		{ 
+			if ( index > _container.numChildren )
+				index = _container.numChildren;
+			_container.addChildAt(block, index);
+			block.userData.updateCompositionShapes();
+		}
+		
+		/**
+		 * Removes a <code>TableBlockContainer</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>TableBlockContainer</code> object to remove 
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 * @see #container
+		 * 
+		 */	
+
+		protected function removeTableBlock(block:TableBlockContainer):void
+		{
+			if (_container.contains(block))
+				_container.removeChild(block);
+		}
+
+		
+		/**
 		 * 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
@@ -4590,6 +4797,13 @@
 			// bounds than the getBounds. I've left the old code here for verification.
 			CONFIG::debug { assert(textFlowLine != null,"testLineVisible"); }
 			
+			if(textFlowLine is TextFlowTableBlock)
+			{
+				if(textFlowLine.controller == this)
+					return TextFlowTableBlock(textFlowLine).container;
+				return null;
+			}
+			
 			//Bug #2988852, scrolling in the application causes all text to disappear. When auto-size images make the line "after visible"
 			//It's "after visible", but it cannot return 1. Because if it were 1, the binary-search in gatherVisibleLines() would make all the lines invisible.
 			if(textFlowLine.controller == null)
@@ -4657,6 +4871,10 @@
 			// about the children, and also the bounds of visible glyphs. We decided that the logical bounds is close enough,
 			// and is much faster to obtain. However, there may be some lines, that get a different result using the logical 
 			// bounds than the getBounds. I've left the old code here for verification.
+			
+			if(textFlowLine is TextFlowTableBlock)
+				return null;
+
 			if (!textFlowLine.hasLineBounds())
 			{
 				if (!textLine)
@@ -4731,7 +4949,47 @@
 			}
 			return boundsRect;
 		}
-		
+		/** @private */
+		tlf_internal function findCellAtPosition(point:Point):CellCoordinates
+		{
+			point = point.clone();
+			for each(var chld:Object in _shapeChildren)
+			{
+				if( !(chld is TableBlockContainer) )
+					continue;
+				
+				var block:TableBlockContainer = chld as TableBlockContainer;
+				if(block.y > point.y)
+					continue;
+				if(block.x > point.x)
+					continue;
+				if(block.y + block.height < point.y)
+					continue;
+				if(block.x + block.getTableWidth() < point.x)
+					continue;
+				
+				point.x -= block.x;
+				point.y -= block.y;
+				
+				// the point falls out inside the block. Find the cell...
+				var cells:Vector.<TableCellElement> = block.userData.getTableCells();
+				for each (var cell:TableCellElement in  cells)
+				{
+					if(cell.x + cell.width < point.x)
+						continue;
+					if(cell.y + cell.getRow().composedHeight < point.y)
+						continue;
+					if(cell.x > point.x)
+						continue;
+					if(cell.y > point.y)
+						continue;
+					return new CellCoordinates(cell.rowIndex,cell.colIndex,cell.getTable());
+					
+				}
+			}
+			
+			return null;
+		}
 		/** @private */
 		tlf_internal function getPlacedTextLineBounds(textLine:TextLine):Rectangle
 		{
@@ -4786,13 +5044,25 @@
 		{
 			_linesInView.push(textLine);			
 		}
-		
+
+		/** @private */
+		tlf_internal function addComposedTableBlock(block:TableBlockContainer):void
+		{
+			var idx:int = _tableBlocksInView.indexOf(block);
+			if(idx >= 0)
+				_tableBlocksInView.splice(idx,1);
+			else
+				_tableBlocksInView.push(block);
+		}
+
 		/** @private Return the array. Client code may add lines to the array. */
 		tlf_internal function get composedLines():Array
 		{
 			if (!_linesInView)
 				_linesInView = [];
-			return _linesInView;
+			var arr:Array = _linesInView.slice();
+			intersperseTableBlocks(arr);
+			return arr;
 		}
 		
 		/** @private Empty out the linesInView, starting from the supplied text index. */
@@ -4807,6 +5077,17 @@
 				index++;
 			}
 			_linesInView.length = index;
+			
+			index = 0;
+			for each (var tbc:TableBlockContainer in _tableBlocksInView)
+			{
+				var tftb:TextFlowTableBlock = tbc.userData;
+				if(tbc.userData.absoluteStart >= pos)
+					break;
+				index++;
+			}
+			_tableBlocksInView.length = index;
+			
 			_updateStart = Math.min(_updateStart, pos);
 		}
 		
diff --git a/textLayout/src/flashx/textLayout/conversion/BaseTextLayoutExporter.as b/textLayout/src/flashx/textLayout/conversion/BaseTextLayoutExporter.as
index 6b88cc2..9e0e105 100644
--- a/textLayout/src/flashx/textLayout/conversion/BaseTextLayoutExporter.as
+++ b/textLayout/src/flashx/textLayout/conversion/BaseTextLayoutExporter.as
@@ -22,6 +22,7 @@
 	import flash.utils.getQualifiedClassName;
 	
 	import flashx.textLayout.TextLayoutVersion;
+	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.debug.assert;
 	import flashx.textLayout.elements.Configuration;
 	import flashx.textLayout.elements.ContainerFormattedElement;
@@ -31,34 +32,35 @@
 	import flashx.textLayout.elements.LinkElement;
 	import flashx.textLayout.elements.ParagraphFormattedElement;
 	import flashx.textLayout.elements.SpanElement;
+	import flashx.textLayout.elements.TableCellElement;
+	import flashx.textLayout.elements.TableElement;
+	import flashx.textLayout.elements.TableRowElement;
 	import flashx.textLayout.elements.TextFlow;
 	import flashx.textLayout.elements.TextRange;
 	import flashx.textLayout.formats.ITextLayoutFormat;
 	import flashx.textLayout.formats.TextLayoutFormat;
 	import flashx.textLayout.formats.WhiteSpaceCollapse;
 	import flashx.textLayout.property.Property;
-	import flashx.textLayout.tlf_internal;
+
 	use namespace tlf_internal;
 	
-	[ExcludeClass]
 	/** 
-	 * @private 
-	 * Export converter for TextLayout format. 
+	 * Base export converter for TextLayout format. 
 	 */
-	internal class BaseTextLayoutExporter extends ConverterBase implements ITextExporter
-	{	
-		private var _config:ImportExportConfiguration;
+	public class BaseTextLayoutExporter extends ConverterBase implements ITextExporter
+	{
 		private var _rootTag:XML;
 		private var _ns:Namespace;
-				
-		public function BaseTextLayoutExporter(ns:Namespace, rootTag:XML, config:ImportExportConfiguration)
+		
+		public function BaseTextLayoutExporter(ns:Namespace, rootTag:XML, configuration:ImportExportConfiguration)
 		{
-			_config = config;
+			config = configuration;
 			_ns = ns;
 			_rootTag = rootTag;
 		}
 		
-		/** @copy ITextExporter#export()
+		/** 
+		 * @copy ITextExporter#export()
 		 */
 		public function export(source:TextFlow, conversionType:String):Object
 		{
@@ -68,7 +70,9 @@
 			return conversionType == ConversionType.STRING_TYPE ? convertXMLToString(result) : result;
 		}
 		
-		/** Export text content of a TextFlow into XFL format.
+		/** 
+		 * Export text content of a TextFlow into XFL format.
+		 * 
 		 * @param source	the text to export
 		 * @return XML	the exported content
 		 */
@@ -89,7 +93,9 @@
 			return result;
 		}
 		
-		/** Export text content as a string
+		/** 
+		 * Export text content as a string
+		 * 
 		 * @param xml	the XML to convert
 		 * @return String	the exported content
 		 * @private
@@ -118,7 +124,9 @@
 		}
 
 	
-		/** Base functionality for exporting a FlowElement. 
+		/** 
+		 * Base functionality for exporting a FlowElement.
+		 *  
 		 * @param exporter	Root object for the export
 		 * @param flowElement	Element to export
 		 * @return XMLList	XML for the element
@@ -128,14 +136,16 @@
 			return exporter.exportFlowElement(flowElement);
 		}
 		
-		/** Overridable worker method for exporting a FlowElement. Creates the XMLList.
+		/** 
+		 * Overridable worker method for exporting a FlowElement. Creates the XMLList.
+		 * 
 		 * @param flowElement	Element to export
 		 * @return XMLList	XML for the element
 		 */
 		protected function exportFlowElement (flowElement:FlowElement):XMLList
 		{
 			var className:String = flash.utils.getQualifiedClassName(flowElement);
-			var elementName:String = _config.lookupName(className);	// NO PMD
+			var elementName:String = config.lookupName(className);	// NO PMD
 			var output:XML = <{elementName}/>;
 			output.setNamespace(_ns);
 			return XMLList(output);
@@ -199,8 +209,10 @@
 			}		
 		}  
 		
-		/** Base functionality for exporting a Span. Exports as a FlowElement,
+		/** 
+		 * Base functionality for exporting a Span. Exports as a FlowElement,
 		 * and exports the text of the span.
+		 * 
 		 * @param exporter	Root object for the export
 		 * @param span	Element to export
 		 * @return XMLList	XML for the element
@@ -214,7 +226,8 @@
 		
 		static private const brRegEx:RegExp = /\u2028/;
 		
-		/** Gets the regex that specifies characters in span text to be replaced with XML elements
+		/** 
+		 * Gets the regex that specifies characters in span text to be replaced with XML elements.
 		 *  Note: Each match is a single character 
 		 */
 		protected function get spanTextReplacementRegex():RegExp
@@ -222,7 +235,8 @@
 			return brRegEx;
 		}
 
-		/** Gets the xml element used to represent a character in the export format
+		/** 
+		 * Gets the xml element used to represent a character in the export format
 		 */
 		protected function getSpanTextReplacementXML(ch:String):XML
 		{
@@ -232,8 +246,10 @@
 			return breakXML;
 		}
 		
-		/** Base functionality for exporting a FlowGroupElement. Exports as a FlowElement,
+		/** 
+		 * Base functionality for exporting a FlowGroupElement. Exports as a FlowElement,
 		 * and exports the children of a element.
+		 * 
 		 * @param exporter	Root object for the export
 		 * @param flowBlockElement	Element to export
 		 * @return XMLList	XML for the element
@@ -241,20 +257,26 @@
 		static public function exportFlowGroupElement(exporter:BaseTextLayoutExporter, flowBlockElement:FlowGroupElement):XMLList
 		{
 			var output:XMLList = exportFlowElement(exporter, flowBlockElement);
+			var count:int = flowBlockElement.numChildren;
 			
 			// output each child
-			for(var childIter:int = 0; childIter < flowBlockElement.numChildren; ++childIter)
+			for(var index:int; index < count; ++index)
 			{
-				var flowChild:FlowElement = flowBlockElement.getChildAt(childIter);
+				var flowChild:FlowElement = flowBlockElement.getChildAt(index);
 				var childXML:XMLList = exporter.exportChild(flowChild);
-				if (childXML)
+				
+				if (childXML) {
 					output.appendChild(childXML);
+				}
 			}
+			
 			return output;
 		}
 
-		/** Base functionality for exporting a ParagraphFormattedElement. Exports as a FlowGroupElement,
+		/** 
+		 * Base functionality for exporting a ParagraphFormattedElement. Exports as a FlowGroupElement,
 		 * and exports paragraph attributes.
+		 * 
 		 * @param exporter	Root object for the export
 		 * @param flowParagraph	Element to export
 		 * @return XMLList	XML for the element
@@ -264,22 +286,6 @@
 			return exporter.exportParagraphFormattedElement(flowParagraph);
 		}
 		
-		/** Overridable worker method for exporting a ParagraphFormattedElement. Creates the XMLList.
-		 * @param flowElement	Element to export
-		 * @return XMLList	XML for the element
-		 */
-		protected function exportParagraphFormattedElement(flowElement:FlowElement):XMLList
-		{
-			var rslt:XMLList = exportFlowElement(flowElement);
-			// output each child
-			for(var childIter:int = 0; childIter < ParagraphFormattedElement(flowElement).numChildren; ++childIter)
-			{
-				var flowChild:FlowElement = ParagraphFormattedElement(flowElement).getChildAt(childIter);
-				rslt.appendChild(exportChild(flowChild));
-			}
-			return rslt;
-		}
-		
 		static public function exportList(exporter:BaseTextLayoutExporter, flowParagraph:ParagraphFormattedElement):XMLList
 		{
 			return exporter.exportList(flowParagraph);
@@ -287,14 +293,17 @@
 		
 		protected function exportList(flowElement:FlowElement):XMLList
 		{
-			var rslt:XMLList = exportFlowElement(flowElement);
+			var result:XMLList = exportFlowElement(flowElement);
+			var count:int = FlowGroupElement(flowElement).numChildren;
+			
 			// output each child
-			for(var childIter:int = 0; childIter < FlowGroupElement(flowElement).numChildren; ++childIter)
+			for(var index:int; index < count; ++index)
 			{
-				var flowChild:FlowElement = FlowGroupElement(flowElement).getChildAt(childIter);
-				rslt.appendChild(exportChild(flowChild));
+				var flowChild:FlowElement = FlowGroupElement(flowElement).getChildAt(index);
+				result.appendChild(exportChild(flowChild));
 			}
-			return rslt;
+			
+			return result;
 		}
 		
 		static public function exportListItem(exporter:BaseTextLayoutExporter, flowParagraph:ParagraphFormattedElement):XMLList
@@ -304,18 +313,23 @@
 		
 		protected function exportListItem(flowElement:FlowElement):XMLList
 		{
-			var rslt:XMLList = exportFlowElement(flowElement);
+			var result:XMLList = exportFlowElement(flowElement);
+			var count:int = FlowGroupElement(flowElement).numChildren;
+			
 			// output each child
-			for(var childIter:int = 0; childIter < FlowGroupElement(flowElement).numChildren; ++childIter)
+			for(var index:int; index < count; ++index)
 			{
-				var flowChild:FlowElement = FlowGroupElement(flowElement).getChildAt(childIter);
-				rslt.appendChild(exportChild(flowChild));
+				var flowChild:FlowElement = FlowGroupElement(flowElement).getChildAt(index);
+				result.appendChild(exportChild(flowChild));
 			}
-			return rslt;
+			
+			return result;
 		}
 		
-		/** Base functionality for exporting a ContainerFormattedElement. Exports as a ParagraphFormattedElement,
+		/** 
+		 * Base functionality for exporting a ContainerFormattedElement. Exports as a ParagraphFormattedElement,
 		 * and exports container attributes.
+		 * 
 		 * @param exporter	Root object for the export
 		 * @param container	Element to export
 		 * @return XMLList	XML for the element
@@ -325,7 +339,9 @@
 			return exporter.exportContainerFormattedElement(container);
 		}
 		
-		/** Overridable worker method for exporting a ParagraphFormattedElement. Creates the XMLList.
+		/** 
+		 * Overridable worker method for exporting a ParagraphFormattedElement. Creates the XMLList.
+		 * 
 		 * @param flowElement	Element to export
 		 * @return XMLList	XML for the element
 		 */
@@ -333,9 +349,126 @@
 		{
 			return exportParagraphFormattedElement(flowElement);
 		}
-
-		/** Base functionality for exporting a TextFlow. Exports as a ContainerElement,
+		
+		/** 
+		 * Base functionality for exporting a TableElement. Exports as a TableElement,
+		 * and exports table attributes.
+		 * 
+		 * @param exporter	Root object for the export
+		 * @param container	Element to export
+		 * @return XMLList	XML for the element
+		 */
+		static public function exportTableElement(exporter:BaseTextLayoutExporter, table:TableElement):XMLList
+		{
+			return exporter.exportTableElement(table);
+		}
+		
+		/** 
+		 * Overridable worker method for exporting a TableElement. Creates the XMLList.
+		 * 
+		 * @param flowElement	Element to export
+		 * @return XMLList	XML for the element
+		 */
+		protected function exportTableElement(tableElement:TableElement):XMLList
+		{
+			var result:XMLList = exportFlowElement(tableElement);
+			var count:int = tableElement.numRows;
+			
+			// output each child
+			for(var index:int = 0; index < count; ++index)
+			{
+				var flowChild:FlowElement = tableElement.getRowAt(index);
+				result.appendChild(exportChild(flowChild));
+			}
+			
+			return result;
+		}
+		
+		/** 
+		 * Base functionality for exporting a TableRowElement. Exports as a TableRowElement,
+		 * and exports table row attributes.
+		 * 
+		 * @param exporter	Root object for the export
+		 * @param container	Element to export
+		 * @return XMLList	XML for the element
+		 */
+		static public function exportTableRowElement(exporter:BaseTextLayoutExporter, tableRow:TableRowElement):XMLList
+		{
+			return exporter.exportTableRowElement(tableRow);
+		}
+		
+		/** 
+		 * Overridable worker method for exporting a TableRowElement. Creates the XMLList.
+		 * 
+		 * @param flowElement	Element to export
+		 * @return XMLList	XML for the element
+		 */
+		protected function exportTableRowElement(tableRowElement:TableRowElement):XMLList
+		{
+			var result:XMLList = exportFlowElement(tableRowElement);
+			var count:int = tableRowElement.numCells;
+			
+			// output each child
+			for(var index:int; index < count; ++index)
+			{
+				var flowChild:FlowElement = tableRowElement.getCellAt(index);
+				result.appendChild(exportChild(flowChild));
+			}
+			
+			return result;
+		}
+		
+		/** 
+		 * Base functionality for exporting a TableCellElement. Exports as a TableCellElement,
+		 * and exports table cell attributes.
+		 * 
+		 * @param exporter	Root object for the export
+		 * @param container	Element to export
+		 * @return XMLList	XML for the element
+		 */
+		static public function exportTableCellElement(exporter:BaseTextLayoutExporter, tableCell:TableCellElement):XMLList
+		{
+			return exporter.exportTableCellElement(tableCell);
+		}
+		
+		/** 
+		 * Overridable worker method for exporting a TableCellElement. Creates the XMLList.
+		 * 
+		 * @param flowElement	Element to export
+		 * @return XMLList	XML for the element
+		 */
+		protected function exportTableCellElement(tableCellElement:TableCellElement):XMLList
+		{
+			var result:XMLList = exportFlowElement(tableCellElement);
+			
+			return result;
+		}
+		
+		/** 
+		 * Overridable worker method for exporting a ParagraphFormattedElement. Creates the XMLList.
+		 * 
+		 * @param flowElement	Element to export
+		 * @return XMLList	XML for the element
+		 */
+		protected function exportParagraphFormattedElement(flowElement:FlowElement):XMLList
+		{
+			var result:XMLList = exportFlowElement(flowElement);
+			var count:int = ParagraphFormattedElement(flowElement).numChildren;
+			
+			// output each child
+			for(var index:int; index < count; ++index)
+			{
+				var flowChild:FlowElement = ParagraphFormattedElement(flowElement).getChildAt(index);
+				result.appendChild(exportChild(flowChild));
+			}
+			
+			return result;
+		}
+		
+		/** 
+		 * Base functionality for exporting a TextFlow. Exports as a ContainerElement,
 		 * and exports container attributes.
+		 * 
 		 * @param exporter	Root object for the export
 		 * @param textFlow	Element to export
 		 * @return XMLList	XML for the element
@@ -344,8 +477,10 @@
 		{
 			var output:XMLList = exportContainerFormattedElement(exporter, textFlow);
 			
-			// TextLayout will use PRESERVE on output
-			output.@[TextLayoutFormat.whiteSpaceCollapseProperty.name] = WhiteSpaceCollapse.PRESERVE;
+			if (exporter.config.whiteSpaceCollapse) {
+				// TextLayout will use PRESERVE on output
+				output.@[TextLayoutFormat.whiteSpaceCollapseProperty.name] = exporter.config.whiteSpaceCollapse;
+			}
 			
 			// TextLayout adds version information
 			output.@["version"] = TextLayoutVersion.getVersionString(TextLayoutVersion.CURRENT_VERSION);
@@ -354,8 +489,10 @@
 		}
 
 
-		/** Exports the object. It will find the appropriate exporter and use it to 
+		/** 
+		 * Exports the object. It will find the appropriate exporter and use it to 
 		 * export the object.
+		 * 
 		 * @param exporter	Root object for the export
 		 * @param flowElement	Element to export
 		 * @return XMLList	XML for the flowElement
@@ -363,13 +500,15 @@
 		public function exportChild(flowElement:FlowElement):XMLList
 		{
 			var className:String = flash.utils.getQualifiedClassName(flowElement);
-			var info:FlowElementInfo = _config.lookupByClass(className);
+			var info:FlowElementInfo = config.lookupByClass(className);
 			if (info != null)
 				return info.exporter(this, flowElement);
 			return null;
 		}
 					
-		/** Helper function to export styles (core or user) in the form of xml attributes or xml children
+		/** 
+		 * Helper function to export styles (core or user) in the form of xml attributes or xml children.
+		 * 
 		 * @param xml object to which attributes/children are added 
 		 * @param sortableStyles an array of objects (xmlName,xmlVal) members that is sorted and exported.
 		 */
@@ -400,7 +539,6 @@
 		protected function get formatDescription():Object
 		{
 			return null;
-		}		
-
+		}
 	}
 }
diff --git a/textLayout/src/flashx/textLayout/conversion/BaseTextLayoutImporter.as b/textLayout/src/flashx/textLayout/conversion/BaseTextLayoutImporter.as
index 21317b3..3eb6ac3 100644
--- a/textLayout/src/flashx/textLayout/conversion/BaseTextLayoutImporter.as
+++ b/textLayout/src/flashx/textLayout/conversion/BaseTextLayoutImporter.as
@@ -21,6 +21,7 @@
 	import flash.system.System;
 	
 	import flashx.textLayout.TextLayoutVersion;
+	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.debug.assert;
 	import flashx.textLayout.elements.BreakElement;
 	import flashx.textLayout.elements.Configuration;
@@ -36,15 +37,15 @@
 	import flashx.textLayout.elements.ParagraphFormattedElement;
 	import flashx.textLayout.elements.SpanElement;
 	import flashx.textLayout.elements.TabElement;
+	import flashx.textLayout.elements.TableCellElement;
 	import flashx.textLayout.elements.TextFlow;
 	import flashx.textLayout.property.Property;
-	import flashx.textLayout.tlf_internal;
+
 	use namespace tlf_internal;
 
-	[ExcludeClass]
 	/**
-	 * @private  
-	 * BaseTextLayoutImporter is a base class for handling the import/export of TextLayout text in the native format.
+	 * BaseTextLayoutImporter is a base class for handling the import/export of TextLayout text 
+	 * in the native format.
 	 */ 
 	internal class BaseTextLayoutImporter extends ConverterBase implements ITextImporter
 	{	
@@ -205,7 +206,9 @@
 			return importer.createTextFlowFromXML(xmlToParse, null);
 		}		
 		
-		/** Static method to parse the supplied XML into a paragrph. Parse the <p ...> tag and it's children.
+		/** 
+		 * Static method to parse the supplied XML into a paragrph. 
+		 * Parse the <p ...> tag and it's children.
 		 * 
 		 * @param importer	parser object
 		 * @param xmlToParse	content to parse
@@ -230,7 +233,8 @@
 			dst.id          = src.id;
 		}
 		
-		/** Static method for constructing a span from XML. Parse the <span> ... </span> tag. 
+		/** 
+		 * Static method for constructing a span from XML. Parse the <span> ... </span> tag. 
 		 * Insert the span into its parent
 		 * 
 		 * @param importer	parser object
@@ -296,7 +300,8 @@
 			}
 		}
 		
-		/** Static method for constructing a break element from XML. Validate the <br> ... </br> tag. 
+		/** 
+		 * Static method for constructing a break element from XML. Validate the <br> ... </br> tag. 
 		 * Use "\u2028" as the text; Insert the new element into its parent 
 		 * 
 		 * @param importer	parser object
@@ -308,9 +313,9 @@
 			var breakElem:BreakElement = importer.createBreakFromXML(xmlToParse);
 			importer.addChild(parent, breakElem);
 		}
-
 		
-		/** Static method for constructing a tab element from XML. Validate the <tab> ... </tab> tag. 
+		/** 
+		 * Static method for constructing a tab element from XML. Validate the <tab> ... </tab> tag. 
 		 * Use "\t" as the text; Insert the new element into its parent 
 		 * 
 		 * @param importer	parser object
@@ -323,7 +328,14 @@
 			if (tabElem)
 				importer.addChild(parent, tabElem);
 		}
-
+		
+		/** 
+		 * Static method for constructing a list element from XML. 
+		 * 
+		 * @param importer	parser object
+		 * @param xmlToParse	content to parse
+		 * @param parent 		the parent for the new content
+		 */
 		static public function parseList(importer:BaseTextLayoutImporter, xmlToParse:XML, parent:FlowGroupElement):void
 		{
 			var listElem:ListElement = importer.createListFromXML(xmlToParse);
@@ -332,7 +344,14 @@
 				importer.parseFlowGroupElementChildren(xmlToParse, listElem);
 			}
 		}
-
+		
+		/** 
+		 * Static method for constructing a list item from XML. 
+		 * 
+		 * @param importer	parser object
+		 * @param xmlToParse	content to parse
+		 * @param parent 		the parent for the new content
+		 */
 		static public function parseListItem(importer:BaseTextLayoutImporter, xmlToParse:XML, parent:FlowGroupElement):void
 		{
 			var listItem:ListItemElement = importer.createListItemFromXML(xmlToParse);
@@ -420,7 +439,9 @@
 			return workAttrs;
 		}	
 		
-		/** Parse XML and convert to  TextFlow.
+		/** 
+		 * Parse XML and convert to  TextFlow.
+		 * 
 		 * @param xmlToParse	content to parse
 		 * @param textFlow 		TextFlow we're parsing. If null, create or find a new TextFlow based on XML content
 		 * @return TextFlow	the new TextFlow created as a result of the parse
@@ -467,7 +488,9 @@
 			return new TabElement();
 		}
 		
-		/** Parse XML, convert to FlowElements and add to the parent.
+		/** 
+		 * Parse XML, convert to FlowElements and add to the parent.
+		 * 
 		 * @param xmlToParse	content to parse
 		 * @param parent 		the parent for the new content
 		 */
@@ -476,7 +499,9 @@
 			parseFlowGroupElementChildren(xmlToParse, parent);
 		}
 		
-		/** Parse XML, convert to FlowElements and add to the parent.
+		/** 
+		 * Parse XML, convert to FlowElements and add to the parent.
+		 * 
 		 * @param xmlToParse	content to parse
 		 * @param parent 		the parent for the new content
 		 * @param chainedParent whether parent actually corresponds to xmlToParse or has been chained (such as when xmlToParse is a formatting element) 
@@ -510,6 +535,54 @@
 				resetImpliedPara();
 		}
 		
+		/** 
+		 * Parse XML, convert XML to FlowElements and TextFlow and add to the parent table cell
+		 * 
+		 * @param xmlToParse	content to parse
+		 * @param parent 		the parent for the new content
+		 * @param chainedParent whether parent actually corresponds to xmlToParse or has been chained (such as when xmlToParse is a formatting element) 
+		 */
+		public function parseTableCellElementChildren(xmlToParse:XML, parent:FlowGroupElement, exceptionElements:Object = null, chainedParent:Boolean=false):void
+		{
+			var textFlow:TextFlow;
+			
+			for each (var child:XML in xmlToParse.children())
+			{
+				if (child.nodeKind() == "element")
+				{
+					if (child.name().localName=="p") {
+						textFlow = new TextFlow();
+						parseObject(child.name().localName, child, textFlow, exceptionElements);
+					}
+					else if (child.name().localName=="TextFlow") {
+						TableCellElement(parent).textFlow = createTextFlowFromXML(child);
+					}
+				}
+					// look for mixed content here
+				else if (child.nodeKind() == "text") 
+				{
+					var txt:String = child.toString();
+					// Strip whitespace-only text appearing as a child of a container-formatted element
+					var strip:Boolean = false;
+					if (parent is ContainerFormattedElement)
+					{
+						strip = txt.search(anyPrintChar) == -1;
+					}
+					
+					if (!strip) {
+						textFlow = new TextFlow();
+						parseObject(child.name().localName, child, textFlow, exceptionElements);
+						//addChild(textFlow, createImpliedSpan(txt));
+					}
+				}
+				
+				if (textFlow) {
+					TableCellElement(parent).textFlow = textFlow;
+					textFlow = null;
+				}
+			}
+		}
+		
 		/** create an implied span with specified text */
 		public function createImpliedSpan(text:String):SpanElement
 		{
diff --git a/textLayout/src/flashx/textLayout/conversion/ConverterBase.as b/textLayout/src/flashx/textLayout/conversion/ConverterBase.as
index de22970..6924d21 100644
--- a/textLayout/src/flashx/textLayout/conversion/ConverterBase.as
+++ b/textLayout/src/flashx/textLayout/conversion/ConverterBase.as
@@ -34,6 +34,7 @@
 		private var _errors:Vector.<String> = null;
 		private var _throwOnError:Boolean = false;
 		private var _useClipboardAnnotations:Boolean = false;
+		private var _config:ImportExportConfiguration;
 
 		/** A converter that converts clipboard data into a TextFlow should use the MERGE_TO_NEXT_ON_PASTE property
 		 * to control how the elements are treated when they are merged into an existing TextFlow on paste. This is useful
@@ -111,5 +112,19 @@
 			_useClipboardAnnotations = value;
 		}
 		
+		/**
+		 * Returns the import and export configuration. 
+		 **/
+		public function get config():ImportExportConfiguration {
+			return _config;
+		}
+		
+		/**
+		 * @private
+		 **/
+		public function set config(value:ImportExportConfiguration):void {
+			_config = value;
+		}
+		
 	}
 }
\ No newline at end of file
diff --git a/textLayout/src/flashx/textLayout/conversion/ITextExporter.as b/textLayout/src/flashx/textLayout/conversion/ITextExporter.as
index 8b3a955..fc10128 100644
--- a/textLayout/src/flashx/textLayout/conversion/ITextExporter.as
+++ b/textLayout/src/flashx/textLayout/conversion/ITextExporter.as
@@ -94,6 +94,12 @@
 		 * @langversion 3.0
 		 */
 		function get useClipboardAnnotations():Boolean;
-		function set useClipboardAnnotations(value:Boolean):void;		
+		function set useClipboardAnnotations(value:Boolean):void;
+		
+		/**
+		 * Accesses the config options for the exporter. 
+		 **/
+		function get config():ImportExportConfiguration;
+		function set config(value:ImportExportConfiguration):void;		
 	}
 }
diff --git a/textLayout/src/flashx/textLayout/conversion/ImportExportConfiguration.as b/textLayout/src/flashx/textLayout/conversion/ImportExportConfiguration.as
index 3406284..b9c996c 100644
--- a/textLayout/src/flashx/textLayout/conversion/ImportExportConfiguration.as
+++ b/textLayout/src/flashx/textLayout/conversion/ImportExportConfiguration.as
@@ -18,13 +18,16 @@
 ////////////////////////////////////////////////////////////////////////////////
 package flashx.textLayout.conversion
 {
-	import flashx.textLayout.debug.assert;
 	import flashx.textLayout.tlf_internal;
+	import flashx.textLayout.debug.assert;
+	import flashx.textLayout.formats.WhiteSpaceCollapse;
+
 	use namespace tlf_internal;
 
-	[ExcludeClass]
-	/** Configure for import/export of standard components.
-	 * Configures the import/export package so it can export all the standard FlowElements. 
+	/** 
+	 * Configure for import/export of standard components.
+	 * Configures the import/export package so it can export all the standard FlowElements.
+	 *  
 	 * @see flashx.textLayout.elements.Configuration
 	 * @playerversion Flash 10
 	 * @playerversion AIR 1.5
@@ -32,12 +35,22 @@
 	 */
 	public class ImportExportConfiguration 
 	{
-		/** array of FlowElementInfo objects (key = name, value = FlowElementInfo) */	
+		/** 
+		 * array of FlowElementInfo objects (key = name, value = FlowElementInfo) 
+		 * */	
 		tlf_internal var flowElementInfoList:Object = {};	
 		tlf_internal var flowElementClassList:Object= {};	
 		tlf_internal var classToNameMap:Object = {};
+		
+		/**
+		 * Whitespace collapse export setting
+		 * @default WhiteSpaceCollapse.PRESERVE
+		 **/
+		public var whiteSpaceCollapse:String = WhiteSpaceCollapse.PRESERVE;
 
-		/** Constructor.
+		/** 
+		 * Constructor.
+		 * 
 		* @playerversion Flash 10
 		* @playerversion AIR 1.5
 	 	* @langversion 3.0
@@ -46,8 +59,10 @@
 		{
 		}
 		
-		/** Add a parser for a new FlowElement type. This allows FlowElements to be added from outside the main system,
+		/** 
+		 * Add a parser for a new FlowElement type. This allows FlowElements to be added from outside the main system,
 		 * and still have the main system be able to import them from XML.
+		 * 
 		 * @param name		the name of the FlowElement class, as it appear in the XML
 		 * @param flowClass	the class of the FlowElement
 		 * @param parser	function fpr importing the XML into a FlowElement
@@ -71,7 +86,9 @@
 				classToNameMap[info.flowClassName] = name;
 		}
 		
-		/** Return the information being held about the FlowElement, as a FlowElementInfo.
+		/** 
+		 * Return the information being held about the FlowElement, as a FlowElementInfo.
+		 * 
 		 * @param name				the name of the FlowElement class, as it appears in the XML
 		 * @return FlowElementInfo	the information being held, as it was supplied to addParseInfo
 		 * @private
@@ -81,7 +98,9 @@
 			return flowElementInfoList[name];
 		}
 
-		/** Return the element name for the class
+		/** 
+		 * Return the element name for the class
+		 * 
 		 * @param classToMatch		fully qualified class name of the FlowElement
 		 * @return name				export name to use for class
 		 * @private
@@ -91,7 +110,9 @@
 			return classToNameMap[classToMatch];
 		}
 
-		/** Return the information being held about the FlowElement, as a FlowElementInfo.
+		/** 
+		 * Return the information being held about the FlowElement, as a FlowElementInfo.
+		 * 
 		 * @param classToMatch		fully qualified class name of the FlowElement
 		 * @return FlowElementInfo	the information being held, as it was supplied to addParseInfo
 		 * @private
diff --git a/textLayout/src/flashx/textLayout/conversion/TextLayoutExporter.as b/textLayout/src/flashx/textLayout/conversion/TextLayoutExporter.as
index 4965993..d4649ba 100644
--- a/textLayout/src/flashx/textLayout/conversion/TextLayoutExporter.as
+++ b/textLayout/src/flashx/textLayout/conversion/TextLayoutExporter.as
@@ -20,6 +20,7 @@
 {
 	import flash.utils.Dictionary;
 	
+	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.debug.assert;
 	import flashx.textLayout.elements.ContainerFormattedElement;
 	import flashx.textLayout.elements.DivElement;
@@ -33,6 +34,9 @@
 	import flashx.textLayout.elements.SpanElement;
 	import flashx.textLayout.elements.SubParagraphGroupElement;
 	import flashx.textLayout.elements.TCYElement;
+	import flashx.textLayout.elements.TableElement;
+	import flashx.textLayout.elements.TableCellElement;
+	import flashx.textLayout.elements.TableRowElement;
 	import flashx.textLayout.elements.TextFlow;
 	import flashx.textLayout.formats.FormatValue;
 	import flashx.textLayout.formats.ITextLayoutFormat;
@@ -40,13 +44,10 @@
 	import flashx.textLayout.formats.TextLayoutFormat;
 	import flashx.textLayout.formats.WhiteSpaceCollapse;
 	import flashx.textLayout.property.Property;
-	import flashx.textLayout.tlf_internal;
 	
 	use namespace tlf_internal;
 	
-	[ExcludeClass]
 	/** 
-	 * @private
 	 * Export filter for TextLayout format. 
 	 */
 	internal class TextLayoutExporter extends BaseTextLayoutExporter
@@ -87,8 +88,9 @@
 			return replacementXML;	
 		}
 		
-		/** Helper function to export styles (core or user) in the form of xml attributes or xml children
-		 * @private
+		/** 
+		 * Helper function to export styles (core or user) in the form of xml attributes or xml children
+		 * 
 		 */
 		tlf_internal function createStylesFromDescription(styles:Object, description:Object, includeUserStyles:Boolean, exclusions:Array):Array
 		{
@@ -170,7 +172,7 @@
 				exportStyles(rslt, sortableStyles );
 			}
 			
-			// export id and styleName
+			// export id and typeName
 			if (flowElement.id != null)
 				rslt.@["id"] = flowElement.id;
 			if (flowElement.typeName != flowElement.defaultTypeName)
@@ -179,8 +181,10 @@
 			return rslt;
 		}
 
-		/** Base functionality for exporting an Image. Exports as a FlowElement,
+		/** 
+		 * Base functionality for exporting an Image. Exports as a FlowElement,
 		 * and exports image properties.
+		 * 
 		 * @param exporter	Root object for the export
 		 * @param image	Element to export
 		 * @return XMLList	XML for the element
@@ -203,8 +207,10 @@
 			return output;
 		}
 
-		/** Base functionality for exporting a LinkElement. Exports as a FlowGroupElement,
+		/** 
+		 * Base functionality for exporting a LinkElement. Exports as a FlowGroupElement,
 		 * and exports link properties.
+		 * 
 		 * @param exporter	Root object for the export
 		 * @param link	Element to export
 		 * @return XMLList	XML for the element
@@ -222,7 +228,9 @@
 			return output;
 		}
 		
-		/** Base functionality for exporting a DivElement. Exports as a FlowContainerFormattedElement
+		/** 
+		 * Base functionality for exporting a DivElement. Exports as a FlowContainerFormattedElement
+		 * 
 		 * @param exporter	Root object for the export
 		 * @param div	Element to export
 		 * @return XMLList	XML for the element
@@ -232,7 +240,9 @@
 			return exportContainerFormattedElement(exporter, div);
 		}
 		
-		/** Base functionality for exporting a SubParagraphGroupElement. Exports as a FlowGroupElement
+		/** 
+		 * Base functionality for exporting a SubParagraphGroupElement. Exports as a FlowGroupElement
+		 * 
 		 * @param exporter	Root object for the export
 		 * @param elem	Element to export
 		 * @return XMLList	XML for the element
@@ -241,7 +251,9 @@
 		{
 			return exportFlowGroupElement(exporter, elem);
 		}
-		/** Base functionality for exporting a TCYElement. Exports as a FlowGroupElement
+		/** 
+		 * Base functionality for exporting a TCYElement. Exports as a FlowGroupElement
+		 * 
 		 * @param exporter	Root object for the export
 		 * @param tcy	Element to export
 		 * @return XMLList	XML for the element
@@ -251,6 +263,42 @@
 			return exportFlowGroupElement(exporter, tcy);
 		}
 		
+		/** 
+		 * Base functionality for exporting a TableElement. 
+		 * 
+		 * @param exporter	Root object for the export
+		 * @param table	Element to export
+		 * @return XMLList	XML for the element
+		 */
+		static public function exportTable(exporter:BaseTextLayoutExporter, table:TableElement):XMLList
+		{
+			return exportTableElement(exporter, table);
+		}
+		
+		/** 
+		 * Base functionality for exporting a TableRowElement. 
+		 * 
+		 * @param exporter	Root object for the export
+		 * @param table	Element to export
+		 * @return XMLList	XML for the element
+		 */
+		static public function exportTableRow(exporter:BaseTextLayoutExporter, tableRow:TableRowElement):XMLList
+		{
+			return exportTableRowElement(exporter, tableRow);
+		}
+		
+		/** 
+		 * Base functionality for exporting a TableCellElement. 
+		 * 
+		 * @param exporter	Root object for the export
+		 * @param table	Element to export
+		 * @return XMLList	XML for the element
+		 */
+		static public function exportTableCell(exporter:BaseTextLayoutExporter, tableCell:TableCellElement):XMLList
+		{
+			return exportTableCellElement(exporter, tableCell);
+		}
+		
 		override protected function get formatDescription():Object
 		{
 			return _formatDescription;
diff --git a/textLayout/src/flashx/textLayout/conversion/TextLayoutImporter.as b/textLayout/src/flashx/textLayout/conversion/TextLayoutImporter.as
index 9ebc61c..2cc9793 100644
--- a/textLayout/src/flashx/textLayout/conversion/TextLayoutImporter.as
+++ b/textLayout/src/flashx/textLayout/conversion/TextLayoutImporter.as
@@ -17,15 +17,19 @@
 //
 ////////////////////////////////////////////////////////////////////////////////
 package flashx.textLayout.conversion 
-{	
+{
 	import flash.display.Shape;
 	import flash.text.engine.TextRotation;
 	import flash.utils.Dictionary;
 	
 	import flashx.textLayout.TextLayoutVersion;
+	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.container.ContainerController;
 	import flashx.textLayout.debug.assert;
+	import flashx.textLayout.edit.EditManager;
+	import flashx.textLayout.edit.SelectionManager;
 	import flashx.textLayout.elements.BreakElement;
+	import flashx.textLayout.elements.ContainerFormattedElement;
 	import flashx.textLayout.elements.DivElement;
 	import flashx.textLayout.elements.FlowElement;
 	import flashx.textLayout.elements.FlowGroupElement;
@@ -40,16 +44,18 @@
 	import flashx.textLayout.elements.SubParagraphGroupElement;
 	import flashx.textLayout.elements.TCYElement;
 	import flashx.textLayout.elements.TabElement;
+	import flashx.textLayout.elements.TableCellElement;
+	import flashx.textLayout.elements.TableColElement;
+	import flashx.textLayout.elements.TableElement;
+	import flashx.textLayout.elements.TableRowElement;
 	import flashx.textLayout.elements.TextFlow;
 	import flashx.textLayout.formats.ITextLayoutFormat;
 	import flashx.textLayout.formats.ListMarkerFormat;
 	import flashx.textLayout.formats.TextLayoutFormat;
 	import flashx.textLayout.property.Property;
-	import flashx.textLayout.tlf_internal;
 	
 	use namespace tlf_internal;
 
-	[ExcludeClass]
 	/** 
 	 * @private
 	 * TextLayoutImporter converts from XML to TextLayout data structures and back.
@@ -82,6 +88,10 @@
 				_defaultConfiguration.addIEInfo("a", LinkElement,            TextLayoutImporter.parseLink, 			TextLayoutExporter.exportLink);
 	 			_defaultConfiguration.addIEInfo("div", DivElement,           TextLayoutImporter.parseDivElement, 	TextLayoutExporter.exportDiv);
 				_defaultConfiguration.addIEInfo("img", InlineGraphicElement, TextLayoutImporter.parseInlineGraphic, TextLayoutExporter.exportImage);	
+				_defaultConfiguration.addIEInfo("table", TableElement, 		 TextLayoutImporter.parseTable,     	TextLayoutExporter.exportTable);	
+				_defaultConfiguration.addIEInfo("tr", TableRowElement, 	     TextLayoutImporter.parseTableRow,	    TextLayoutExporter.exportTableRow);	
+				_defaultConfiguration.addIEInfo("th", TableCellElement, 	 TextLayoutImporter.parseTableCell,  	TextLayoutExporter.exportTableCell);	
+				_defaultConfiguration.addIEInfo("td", TableCellElement, 	 TextLayoutImporter.parseTableCell,  	TextLayoutExporter.exportTableCell);	
 				
 				// validate the defaultTypeName values.  They are to match the TLF format export xml names
 				CONFIG::debug 
@@ -251,6 +261,48 @@
 			return divElem;
 		}
 		
+		/**
+		 * Create a table element from XML
+		 **/
+		public function createTableFromXML(xmlToParse:XML):TableElement
+		{
+			// add the table element to the parent
+			var tableElement:TableElement = new TableElement();
+			
+			parseStandardFlowElementAttributes(tableElement, xmlToParse);
+
+			return tableElement;
+		}
+		
+		/**
+		 * Create a table row element from XML
+		 **/
+		public function createTableRowFromXML(xmlToParse:XML):TableRowElement
+		{
+			// add the table row element to the parent
+			var tableRowElement:TableRowElement = new TableRowElement();
+			
+			parseStandardFlowElementAttributes(tableRowElement, xmlToParse);
+			
+			return tableRowElement;
+		}
+		
+		/**
+		 * Create a table cell element from XML
+		 **/
+		public function createTableCellFromXML(xmlToParse:XML):TableCellElement
+		{
+			// add the table cell element to the parent
+			var tableCellElement:TableCellElement = new TableCellElement();
+			
+			parseStandardFlowElementAttributes(tableCellElement, xmlToParse);
+			
+			return tableCellElement;
+		}
+		
+		/**
+		 * Create a paragraph element from XML
+		 **/
 		public override function createParagraphFromXML(xmlToParse:XML):ParagraphElement
 		{
 			var paraElem:ParagraphElement = new ParagraphElement();
@@ -258,6 +310,9 @@
 			return paraElem;
 		}
 		
+		/**
+		 * Create a sub paragraph group element from XML
+		 **/
 		public function createSubParagraphGroupFromXML(xmlToParse:XML):SubParagraphGroupElement
 		{
 			var elem:SubParagraphGroupElement = new SubParagraphGroupElement();
@@ -265,6 +320,9 @@
 			return elem;
 		}
 		
+		/**
+		 * Create a tate chu yoko element from XML
+		 **/
 		public function createTCYFromXML(xmlToParse:XML):TCYElement
 		{
 			var tcyElem:TCYElement = new TCYElement();
@@ -272,7 +330,7 @@
 			return tcyElem;
 		}
 		
-			
+		
 		static internal const _linkDescription:Object = {
 			href : Property.NewStringProperty("href",null, false, null),
 			target : Property.NewStringProperty("target",null, false, null)
@@ -280,7 +338,8 @@
 		static private const _linkFormatImporter:TLFormatImporter = new TLFormatImporter(Dictionary,_linkDescription);
 		static private const _linkElementFormatImporters:Array = [ _linkFormatImporter, _formatImporter,_idImporter,_typeNameImporter,_customFormatImporter ];
 
-		/** Parse a LinkElement Block.
+		/** 
+		 * Parse a LinkElement Block.
 		 * 
 		 * @param - importFilter:BaseTextLayoutImporter - parser object
 		 * @param - xmlToParse:XML - the xml describing the Link
@@ -300,6 +359,9 @@
 			return linkElem;
 		}
 		
+		/**
+		 * Create a span element from XML
+		 **/
 		public override function createSpanFromXML(xmlToParse:XML):SpanElement
 		{
 			var spanElem:SpanElement = new SpanElement();
@@ -319,6 +381,9 @@
 		static private const _ilgFormatImporter:TLFormatImporter = new TLFormatImporter(Dictionary,_imageDescription);
 		static private const _ilgElementFormatImporters:Array = [ _ilgFormatImporter, _formatImporter, _idImporter, _typeNameImporter, _customFormatImporter ];
 
+		/**
+		 * Create an inline graphic from XML
+		 **/
 		public function createInlineGraphicFromXML(xmlToParse:XML):InlineGraphicElement
 		{				
 			var imgElem:InlineGraphicElement = new InlineGraphicElement();
@@ -341,6 +406,9 @@
 			return imgElem;
 		}
 	
+		/**
+		 * Create a list element from XML
+		 **/
 		public override function createListFromXML(xmlToParse:XML):ListElement
 		{
 			var rslt:ListElement = new ListElement;
@@ -348,6 +416,9 @@
 			return rslt;
 		}
 
+		/**
+		 * Create a list item element from XML
+		 **/
 		public override function createListItemFromXML(xmlToParse:XML):ListItemElement
 		{
 			var rslt:ListItemElement = new ListItemElement;
@@ -355,12 +426,16 @@
 			return rslt;
 		}
 		
+		/**
+		 * Extract text format attributes
+		 **/
 		public function extractTextFormatAttributesHelper(curAttrs:Object, importer:TLFormatImporter):Object
 		{
 			return extractAttributesHelper(curAttrs,importer);
 		}
 		
-		/** Parse an SPGE element
+		/** 
+		 * Parse an SPGE element
 		 * 
 		 * @param - importFilter:BaseTextLayoutImporter - parser object
 		 * @param - xmlToParse:XML - the xml describing the TCY Block
@@ -379,7 +454,8 @@
 			}
 		}
 
-		/** Parse a TCY Block.
+		/** 
+		 * Parse a TCY Block.
 		 * 
 		 * @param - importFilter:BaseTextLayoutImporter - parser object
 		 * @param - xmlToParse:XML - the xml describing the TCY Block
@@ -399,7 +475,8 @@
 		}
 		
 				
-		/** Parse a LinkElement Block.
+		/** 
+		 * Parse a LinkElement Block.
 		 * 
 		 * @param - importFilter:BaseTextLayoutImporter - parser object
 		 * @param - xmlToParse:XML - the xml describing the Link
@@ -456,7 +533,8 @@
 		static public function parseListMarkerFormat(importFilter:BaseTextLayoutImporter, xmlToParse:XML, parent:FlowGroupElement):void
 		{ parent.listMarkerFormat = TextLayoutImporter(importFilter).createListMarkerFormatDictionaryFromXML(xmlToParse); }
 
-		/** Parse the <div ...> tag and all its children
+		/** 
+		 * Parse the <div ...> tag and all its children
 		 * 
 		 * @param - importFilter:BaseTextLayoutImportFilter - parser object
 		 * @param - xmlToParse:XML - the xml describing the Div
@@ -474,7 +552,8 @@
 			}
 		}
 
-		/** Parse a leaf element, the <img ...>  tag.
+		/** 
+		 * Parse a leaf element, the <img ...>  tag.
 		 * 
 		 * @param - importFilter:BaseTextLayoutImporter - parser object
 		 * @param - xmlToParse:XML - the xml describing the InlineGraphic FlowElement
@@ -485,6 +564,115 @@
 			var ilg:InlineGraphicElement = TextLayoutImporter(importFilter).createInlineGraphicFromXML(xmlToParse);
 			importFilter.addChild(parent, ilg);
 		}
+		
+		/** 
+		 * Parse the <table ...> tag and all its children
+		 * 
+		 * @param - importFilter:BaseTextLayoutImportFilter - parser object
+		 * @param - xmlToParse:XML - the xml describing the Table
+		 * @param - parent:FlowBlockElement - the parent of the new Table
+		 */
+		static public function parseTable(importFilter:BaseTextLayoutImporter, xmlToParse:XML, parent:FlowGroupElement):void
+		{
+			var tableElement:TableElement = TextLayoutImporter(importFilter).createTableFromXML(xmlToParse);
+			
+			if (importFilter.addChild(parent, tableElement)) 
+			{
+				importFilter.parseFlowGroupElementChildren(xmlToParse, tableElement);
+				
+			}
+		}
+		
+		/** 
+		 * Parse the <tr ...> tag (TableRowElement) and all its children
+		 * 
+		 * @param - importFilter:BaseTextLayoutImportFilter - parser object
+		 * @param - xmlToParse:XML - the xml describing the Table
+		 * @param - parent:FlowBlockElement - the parent of the new Table
+		 */
+		static public function parseTableRow(importFilter:BaseTextLayoutImporter, xmlToParse:XML, parent:FlowGroupElement):void
+		{
+			var tableRowElement:TableRowElement = TextLayoutImporter(importFilter).createTableRowFromXML(xmlToParse);
+			var table:TableElement;
+			
+			if (importFilter.addChild(parent, tableRowElement))
+			{
+				
+				importFilter.parseFlowGroupElementChildren(xmlToParse, tableRowElement);
+				
+				table = tableRowElement.getTable();
+				
+				var columnCount:int = tableRowElement.getColumnCount();
+				
+				if (table.numColumns<columnCount) {
+					table.numColumns = columnCount;
+				}
+				
+				table.insertRow(tableRowElement, tableRowElement.mxmlChildren);
+				
+			}
+		}
+		
+		/** 
+		 * Parse the <td ...> or <th ...> tag (TableCellElement) and all its children
+		 * 
+		 * @param - importFilter:BaseTextLayoutImportFilter - parser object
+		 * @param - xmlToParse:XML - the xml describing the Table
+		 * @param - parent:FlowBlockElement - the parent of the new Table
+		 */
+		static public function parseTableCell(importFilter:BaseTextLayoutImporter, xmlToParse:XML, parent:FlowGroupElement):void
+		{
+			var tableCellElement:TableCellElement = TextLayoutImporter(importFilter).createTableCellFromXML(xmlToParse);
+			
+			if (importFilter.addChild(parent, tableCellElement))
+			{
+				importFilter.parseTableCellElementChildren(xmlToParse, tableCellElement);
+			}
+			
+			//tableCellElement.textFlow = getTextFlowContent("test cell");
+			
+			TableRowElement(parent).addCell(tableCellElement);
+			//TableRowElement(parent).getTable().addChild(tableCellElement);
+			//importFilter.parseFlowGroupElementChildren(xmlToParse, tableCellElement);
+			
+			// we can't have a <td> tag w/no children... so, add an empty text flow
+			//if (tableCellElement.numChildren == 0) {
+			//	tableCellElement.addChild(new TextFlow());
+			//}
+				
+		}
+		
+		/**
+		 * Creates default text flow from the text value passed in. Used for table cell text flows. 
+		 * Used for testing. May be removed in the future. 
+		 **/
+		static public function getTextFlowContent(text:String = null, selectable:Boolean = false, editable:Boolean = false):TextFlow {
+			var textFlowContent:TextFlow = new TextFlow();
+			var paragraph:ParagraphElement = new ParagraphElement();
+			var span:SpanElement = new SpanElement();
+			
+			if (text) {
+				span.text = text;
+			}
+			else {
+				span.text = "";
+			}
+			
+			paragraph.backgroundAlpha = 0.2;
+			paragraph.backgroundColor = 0xFF0000;
+			paragraph.addChild(span);
+			
+			if (editable) {
+				//textFlowContent.interactionManager = new EditManager(new UndoManager);
+			}
+			else if (selectable) {
+				//textFlowContent.interactionManager = new SelectionManager();
+			}
+			
+			textFlowContent.addChild(paragraph);
+			
+			return textFlowContent;
+		}
 	}
 }
 
diff --git a/textLayout/src/flashx/textLayout/edit/EditManager.as b/textLayout/src/flashx/textLayout/edit/EditManager.as
index 2b46e72..28a45c6 100644
--- a/textLayout/src/flashx/textLayout/edit/EditManager.as
+++ b/textLayout/src/flashx/textLayout/edit/EditManager.as
@@ -35,6 +35,7 @@
 	import flash.ui.Keyboard;
 	import flash.utils.getQualifiedClassName;
 	
+	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.compose.IFlowComposer;
 	import flashx.textLayout.container.ContainerController;
 	import flashx.textLayout.debug.Debugging;
@@ -52,11 +53,12 @@
 	import flashx.textLayout.elements.ParagraphElement;
 	import flashx.textLayout.elements.SubParagraphGroupElement;
 	import flashx.textLayout.elements.TCYElement;
-	import flashx.textLayout.elements.TableDataCellElement;
+	import flashx.textLayout.elements.TableCellElement;
 	import flashx.textLayout.elements.TableElement;
 	import flashx.textLayout.elements.TextFlow;
 	import flashx.textLayout.elements.TextRange;
 	import flashx.textLayout.events.FlowOperationEvent;
+	import flashx.textLayout.events.ModelChange;
 	import flashx.textLayout.formats.IListMarkerFormat;
 	import flashx.textLayout.formats.ITextLayoutFormat;
 	import flashx.textLayout.formats.TextLayoutFormat;
@@ -85,7 +87,6 @@
 	import flashx.textLayout.operations.SplitElementOperation;
 	import flashx.textLayout.operations.SplitParagraphOperation;
 	import flashx.textLayout.operations.UndoOperation;
-	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.utils.CharacterUtil;
 	import flashx.textLayout.utils.GeometryUtil;
 	import flashx.textLayout.utils.NavigationUtil;
@@ -466,6 +467,23 @@
 								doOperation(new CreateListOperation(new SelectionState(textFlow, element.getAbsoluteStart(), element.getAbsoluteStart() + element.textLength), listItem.parent));
 							}
 						}
+						else if (textFlow.nestedInTable()) {
+							var cell:TableCellElement;
+							
+							if (event.shiftKey) {
+								cell = (textFlow.parentElement as TableCellElement).getPreviousCell();
+							}
+							else {
+								cell = (textFlow.parentElement as TableCellElement).getNextCell();
+							}
+							
+							// select next cell in table
+							if (cell && cell.textFlow && cell.textFlow.interactionManager is EditManager) {
+								//cell.textFlow.interactionManager.selectLastPosition();
+								cell.textFlow.interactionManager.selectAll();
+								cell.textFlow.interactionManager.setFocus();
+							}
+						}
 						else
 						{
 							overwriteMode ? overwriteText(String.fromCharCode(event.charCode)) : insertText(String.fromCharCode(event.charCode));
@@ -829,8 +847,19 @@
 				redrawListener = null;
 			}
 
+			var cellHeight:Number = 0;
 			if (textFlow.flowComposer)
 			{
+				if(superManager && superManager is IEditManager)
+				{
+					var controller:ContainerController = textFlow.flowComposer.getControllerAt(0);
+					if (controller)
+					{
+						cellHeight = controller.container.height;
+					}
+				}
+
+
 				 textFlow.flowComposer.updateAllControllers(); 
 
 				// Scroll to selection
@@ -840,6 +869,18 @@
 					if (controllerIndex >= 0)
 						textFlow.flowComposer.getControllerAt(controllerIndex).scrollToRange(activePosition,anchorPosition);	
 				}
+				if(superManager && superManager is IEditManager)
+				{
+					if(controller.container.height != cellHeight)
+					{
+						var setFormat:String = selectionFormatState;
+						var table:TableElement = (textFlow.parentElement as TableCellElement).getTable();
+						table.modelChanged(ModelChange.ELEMENT_MODIFIED, table, 0, table.textLength);
+						(superManager as IEditManager).updateAllControllers();
+						if(setFormat == SelectionFormatState.FOCUSED)
+							setFocus();
+					}
+				}
 			}
 
 			selectionChanged(true, false);
@@ -1259,12 +1300,14 @@
 			if (!operationState)
 				return;
 
+			/*
+			This should not be necessary...
 			// mjzhang : fix for table
 			var leaf:FlowLeafElement = textFlow.findLeaf(operationState.absoluteStart);
 			var para:ParagraphElement = leaf.getParagraph();
 			if ( para.isInTable() )
 				return;
-			
+			*/
 			// Delete the next character if it's a caret selection, and allow adejacent delete next's to merge
 			// If it's a range selection, delete the range and disallow merge
 			var deleteOp:DeleteTextOperation;
@@ -1369,10 +1412,12 @@
 				}
 				if(movePara)
 				{
+					/*
+					should not be necessary...
 					// mjzhang: fix for table feature
 					if ( para.isInTable() )
 						return;
-					
+					*/
 					var source:FlowGroupElement;
 					var target:FlowGroupElement;
 					var numElementsToMove:int;
diff --git a/textLayout/src/flashx/textLayout/edit/ISelectionManager.as b/textLayout/src/flashx/textLayout/edit/ISelectionManager.as
index abe6f29..30eb446 100644
--- a/textLayout/src/flashx/textLayout/edit/ISelectionManager.as
+++ b/textLayout/src/flashx/textLayout/edit/ISelectionManager.as
@@ -25,9 +25,12 @@
 	import flash.events.MouseEvent;
 	import flash.events.TextEvent;
 	
+	import flashx.textLayout.elements.CellCoordinates;
+	import flashx.textLayout.elements.CellRange;
+	import flashx.textLayout.elements.TableElement;
 	import flashx.textLayout.elements.TextFlow;
-	import flashx.textLayout.formats.TextLayoutFormat;
 	import flashx.textLayout.elements.TextRange;
+	import flashx.textLayout.formats.TextLayoutFormat;
 
 	/** 
 	 * The ISelectionManager interface defines the interface for handling text selection.
@@ -64,6 +67,23 @@
 		function get textFlow():TextFlow;
 		function set textFlow(flow:TextFlow):void;
 		
+		function get currentTable():TableElement;
+		function set currentTable(table:TableElement):void;
+		function hasCellRangeSelection():Boolean;
+		
+		function selectCellRange(anchorCoords:CellCoordinates,activeCoords:CellCoordinates):void;
+		
+		function getCellRange():CellRange;
+		function setCellRange(range:CellRange):void;
+
+		/** Anchor point of the current cell selection, as coordinates within the table. */
+		function get anchorCellPosition():CellCoordinates;
+		function set anchorCellPosition(value:CellCoordinates):void;
+		
+		/** Active end of the current cell selection, as coordinates within the table. */
+		function get activeCellPosition():CellCoordinates;
+		function set activeCellPosition(value:CellCoordinates):void;
+
 		/** 
 		 * The text position of the start of the selection, as an offset from the start of the text flow.
 		 *  
@@ -113,6 +133,33 @@
 		 */
 		function selectAll() : void
 		
+		/**
+		 * Selects the last position in the entire flow.
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+ 		 * @langversion 3.0
+		 */
+		function selectLastPosition() : void
+		
+		/**
+		 * Selects the first position in the entire flow.
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+ 		 * @langversion 3.0
+		 */
+		function selectFirstPosition() : void
+
+		/**
+		 * Removes any selection from the text flow
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		function deselect() : void
+
 		/** 
 		 * The anchor point of the selection. 
 		 * 
@@ -140,7 +187,7 @@
 		function get activePosition() : int;
 		
 		/**
-		 * Indicates whether there is a selection. 
+		 * Indicates whether there is a text selection. 
 		 * 
 		 * <p>Returns <code>true</code> if there is either a range selection or a point selection. 
 		 * By default, when a selection manager is first set up, there is no selection (the start and end are -1).</p>
@@ -152,6 +199,34 @@
  		 * @langversion 3.0
 		 */
 		function hasSelection():Boolean;
+
+		/**
+		 * Indicates whether there is a text or cell selection. 
+		 * 
+		 * <p>Returns <code>true</code> if there is either a range selection or a point selection. 
+		 * By default, when a selection manager is first set up, there is no selection (the start and end are -1).</p>
+		 * 
+		 * @includeExample examples\SelectionManager_hasSelection.as -noswf
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		function hasAnySelection():Boolean;
+
+		/**
+		 * Indicates the type of selection. 
+		 * 
+		 * <p>The <code>selectionType</code> describes the kind of selection. 
+		 * It can either be <code>SelectionType.TEXT</code> or <code>SelectionType.CELLS</code>
+		 * 
+		 * @see flashx.textLayout.edit.SelectionType
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		function get selectionType() : String;
 		
 		/**
 		 * Indicates whether the selection covers a range of text.
@@ -201,6 +276,14 @@
 		 */	
 		function refreshSelection():void;
 
+		/** 
+		 * Clears the selection shapes. 
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+ 		 * @langversion 3.0
+		 */	
+		function clearSelection():void;
 
 		/** 
 		 * Gives the focus to the first container in the selection.
@@ -250,6 +333,19 @@
 		 */
 		function get currentSelectionFormat():SelectionFormat;
 
+		/** 
+		 * The current Cell SelectionFormat object.
+		 * 
+		 * <p>The current cell SelectionFormat object is chosen from the SelectionFormat objects assigned to the 
+		 * <code>unfocusedCellSelectionFormat</code>, <code>inactiveCellSelectionFormat</code> and <code>focusedCellSelectionFormat</code> 
+		 * properties based on the current state of the <code>windowActive</code> and <code>focused</code> properties.</p> 
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		function get currentCellSelectionFormat():SelectionFormat;
+
 		/**
 		 * Gets the character format attributes that are common to all characters in the specified text range or current selection.
 		 * 
@@ -346,7 +442,38 @@
 		 */		 		 
 		 function get inactiveSelectionFormat():SelectionFormat;
 		 function set inactiveSelectionFormat(val:SelectionFormat):void;		 		 
-		
+
+		 /**
+		  * The SelectionFormat object used to draw cell selections in a focused container. 
+		  * 
+		  * @playerversion Flash 10
+		  * @playerversion AIR 1.5
+		  * @langversion 3.0
+		  */		 
+		 function get focusedCellSelectionFormat():SelectionFormat;
+		 function set focusedCellSelectionFormat(val:SelectionFormat):void;
+		 
+		 /**
+		  * The SelectionFormat object used to draw cell selections when they are not in a focused container, but are in
+		  * the active window.
+		  * 
+		  * @playerversion Flash 10
+		  * @playerversion AIR 1.5
+		  * @langversion 3.0
+		  */		 		 
+		 function get unfocusedCellSelectionFormat():SelectionFormat;
+		 function set unfocusedCellSelectionFormat(val:SelectionFormat):void;
+		 
+		 /**
+		  * The SelectionFormat object used to draw cell selections when they are not in the active window.
+		  * 
+		  * @playerversion Flash 10
+		  * @playerversion AIR 1.5
+		  * @langversion 3.0
+		  */		 		 
+		 function get inactiveCellSelectionFormat():SelectionFormat;
+		 function set inactiveCellSelectionFormat(val:SelectionFormat):void;		 		 
+
 		/**
 		 * Executes any pending FlowOperations. 
 		 * 
@@ -379,6 +506,25 @@
  	 	 * @langversion 3.0
 		 */
 		function notifyInsertOrDelete(absolutePosition:int, length:int):void		 
-		 	
+		 
+		/**
+		 * The ISelectionManager object used to for cell selections nested within the TextFlow managed by this ISelectionManager.
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */		 		 
+		function get subManager():ISelectionManager;
+		function set subManager(value:ISelectionManager):void;
+		
+		/**
+		 * The ISelectionManager object used to manage the parent TextFlow of this ISelectionManager (i.e. for cell ISelectionManagers).
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */		 		 
+		function get superManager():ISelectionManager;
+		function set superManager(value:ISelectionManager):void;
 	}
 }
diff --git a/textLayout/src/flashx/textLayout/edit/ModelEdit.as b/textLayout/src/flashx/textLayout/edit/ModelEdit.as
index cdddb3b..b18f55f 100644
--- a/textLayout/src/flashx/textLayout/edit/ModelEdit.as
+++ b/textLayout/src/flashx/textLayout/edit/ModelEdit.as
@@ -516,7 +516,7 @@
 		
 		var element1Mark:ElementMark = new ElementMark(element1,0);
 		var element2Mark:ElementMark = new ElementMark(element2,0);
-		performInternal(textFlow, element1Mark, element2Mark);		
+		performInternal(textFlow, element1Mark, element2Mark);
 		var removeParentChain:IMemento = TextFlowEdit.removeEmptyParentChain(element2);
 
 		if (createMemento)
diff --git a/textLayout/src/flashx/textLayout/edit/ParaEdit.as b/textLayout/src/flashx/textLayout/edit/ParaEdit.as
index 974eba2..4af7e23 100644
--- a/textLayout/src/flashx/textLayout/edit/ParaEdit.as
+++ b/textLayout/src/flashx/textLayout/edit/ParaEdit.as
@@ -30,11 +30,13 @@
 	import flashx.textLayout.elements.ParagraphElement;
 	import flashx.textLayout.elements.SpanElement;
 	import flashx.textLayout.elements.SubParagraphGroupElementBase;
+	import flashx.textLayout.elements.TableLeafElement;
 	import flashx.textLayout.elements.TextFlow;
 	import flashx.textLayout.formats.Float;
 	import flashx.textLayout.formats.ITextLayoutFormat;
 	import flashx.textLayout.formats.TextLayoutFormat;
 	import flashx.textLayout.tlf_internal;
+	import flashx.textLayout.elements.TableElement;
 	
 	use namespace tlf_internal;
 
@@ -69,10 +71,15 @@
 			{
 				// If we're at the start a span, go to the previous span in the same paragraph, and insert at the end of it
 				if (paraSelBegIdx == sibling.getElementRelativeStart(paragraph))
-					sibling = FlowLeafElement(sibling).getPreviousLeaf(paragraph);	
-				siblingIndex = sibling.parent.getChildIndex(sibling) + 1;
+					sibling = FlowLeafElement(sibling).getPreviousLeaf(paragraph);
+				if(sibling is TableLeafElement)
+					siblingIndex = sibling.parent.parent.getChildIndex(sibling.parent) + 1;
+				else
+					siblingIndex = sibling.parent.getChildIndex(sibling) + 1;
 			}
 			var insertParent:FlowGroupElement = sibling.parent;
+			if(insertParent is TableElement)
+				insertParent = insertParent.parent;
 			
 			// If we are adding text to the start or end of a link, it doesn't allow the insertion to group with the link.
 			// So in that case, we will insert to the element beside the position that is *not* part of the link.
@@ -93,9 +100,13 @@
 					break;
 				}
 			}
-				
 			// adjust the flow so that we are in a span for the insertion
 			var insertSpan:SpanElement = sibling as SpanElement;
+			
+			// use the terminator span if it's empty
+			if(paragraph.terminatorSpan.textLength == 1 && paragraph.terminatorSpan == insertSpan)
+				createNewSpan = false;
+			
 			if (!insertSpan || createNewSpan)
 			{
 				var newSpan:SpanElement = new SpanElement();
@@ -111,7 +122,15 @@
 							sibling.splitAtPosition(relativeStart);		// we'll insert between the two elements
 					}
 				}
-				insertParent.replaceChildren(siblingIndex, siblingIndex, newSpan);
+				var nextLeaf:FlowLeafElement = paragraph.findLeaf(paraSelBegIdx);
+				if(nextLeaf && nextLeaf.textLength == 1 && nextLeaf == paragraph.terminatorSpan)
+				{
+					// use the terminator span instead of inserting a new one.
+					newSpan = SpanElement(nextLeaf);
+				}
+				else {
+					insertParent.replaceChildren(siblingIndex, siblingIndex, newSpan);
+				}
 				var formatElem:FlowLeafElement = newSpan.getPreviousLeaf(paragraph);
 				if (formatElem == null)
 					newSpan.format = newSpan.getNextLeaf(paragraph).format;
diff --git a/textLayout/src/flashx/textLayout/edit/SelectionManager.as b/textLayout/src/flashx/textLayout/edit/SelectionManager.as
index dd6f117..42e1579 100644
--- a/textLayout/src/flashx/textLayout/edit/SelectionManager.as
+++ b/textLayout/src/flashx/textLayout/edit/SelectionManager.as
@@ -20,8 +20,10 @@
 {
     import flash.desktop.Clipboard;
     import flash.desktop.ClipboardFormats;
+    import flash.display.BitmapData;
     import flash.display.DisplayObject;
     import flash.display.InteractiveObject;
+    import flash.display.Shape;
     import flash.display.Stage;
     import flash.errors.IllegalOperationError;
     import flash.events.ContextMenuEvent;
@@ -31,8 +33,10 @@
     import flash.events.KeyboardEvent;
     import flash.events.MouseEvent;
     import flash.events.TextEvent;
+    import flash.geom.Matrix;
     import flash.geom.Point;
     import flash.geom.Rectangle;
+    import flash.text.engine.TextBlock;
     import flash.text.engine.TextLine;
     import flash.text.engine.TextLineValidity;
     import flash.text.engine.TextRotation;
@@ -40,14 +44,20 @@
     import flash.ui.Keyboard;
     import flash.ui.Mouse;
     import flash.ui.MouseCursor;
+    import flash.ui.MouseCursorData;
+    import flash.utils.Dictionary;
     import flash.utils.getQualifiedClassName;
     
     import flashx.textLayout.compose.IFlowComposer;
     import flashx.textLayout.compose.TextFlowLine;
+    import flashx.textLayout.compose.TextFlowTableBlock;
     import flashx.textLayout.container.ColumnState;
     import flashx.textLayout.container.ContainerController;
     import flashx.textLayout.debug.Debugging;
     import flashx.textLayout.debug.assert;
+    import flashx.textLayout.elements.CellContainer;
+    import flashx.textLayout.elements.CellCoordinates;
+    import flashx.textLayout.elements.CellRange;
     import flashx.textLayout.elements.Configuration;
     import flashx.textLayout.elements.FlowElement;
     import flashx.textLayout.elements.FlowLeafElement;
@@ -55,7 +65,9 @@
     import flashx.textLayout.elements.IConfiguration;
     import flashx.textLayout.elements.InlineGraphicElement;
     import flashx.textLayout.elements.ParagraphElement;
-    import flashx.textLayout.elements.TableDataCellElement;
+    import flashx.textLayout.elements.TableBlockContainer;
+    import flashx.textLayout.elements.TableCellElement;
+    import flashx.textLayout.elements.TableColElement;
     import flashx.textLayout.elements.TableElement;
     import flashx.textLayout.elements.TableRowElement;
     import flashx.textLayout.elements.TextFlow;
@@ -122,23 +134,325 @@
      * @langversion 3.0
      */
     public class SelectionManager implements ISelectionManager
-    {       
+    {
+		static tlf_internal var useTableSelectionCursors:Boolean = false;
+		/**
+		 * Cursor for selection of table
+		 **/
+		public static var SelectTable:String = "selectTable";
+		
+		/**
+		 * Cursor for selection of table row
+		 **/
+		public static var SelectTableRow:String = "selectTableRow";
+		
+		/**
+		 * Cursor for selection of table column
+		 **/
+		public static var SelectTableColumn:String = "selectTableColumn";
+		
         private var _focusedSelectionFormat:SelectionFormat;
         private var _unfocusedSelectionFormat:SelectionFormat;
         private var _inactiveSelectionFormat:SelectionFormat;
+		private var _focusedCellSelectionFormat:SelectionFormat;
+		private var _unfocusedCellSelectionFormat:SelectionFormat;
+		private var _inactiveCellSelectionFormat:SelectionFormat;
         private var _selFormatState:String = SelectionFormatState.UNFOCUSED;
         private var _isActive:Boolean;
         
         /** The TextFlow of the selection. */
         private var _textFlow:TextFlow;
+		
+		protected var _subManager:ISelectionManager;
+		protected var _superManager:ISelectionManager;
+		
+		private var _currentTable:TableElement;
+		
+		// this should probably be produced dynamically rather than keep a reference.
+		private var _cellRange:CellRange;
+		
+		//TODO the following functions need proper comments and should be moved to a logical location within the class.
+		
+		public function get currentTable():TableElement
+		{
+			return _currentTable;
+		}
+		public function set currentTable(table:TableElement):void
+		{
+			_currentTable = table;
+		}
+		
+		public function hasCellRangeSelection():Boolean
+		{
+			if (!_currentTable) {
+				return false;
+			}
+			
+			//we should really check the anchorCellPosition and activeCellPosition instead
+			if (!_cellRange) {
+				return false;
+			}
+			
+			return true;
+		}
+		
+		/**
+		 * Select a table cell text flow
+		 **/
+		public function selectCellTextFlow(cell:TableCellElement):void {
+			
+			if (cell && cell.table) {
+				var selectionManager:SelectionManager = cell.textFlow.interactionManager as SelectionManager;
+				
+				clear();
+				
+				if (selectionManager) {
+					selectionManager.currentTable = cell.table;
+					selectionManager.selectAll();
+					
+					// this seems to be required to work but it should not be
+					selectionManager.setFocus(); 
+				}
+			}
+		}
+		
+		/**
+		 * Select a table cell. 
+		 **/
+		public function selectCell(cell:TableCellElement):void {
+			var beginCoordinates:CellCoordinates;
+			var endCoordinates:CellCoordinates;
+			
+			if (cell) {
+				beginCoordinates = new CellCoordinates(cell.rowIndex, cell.colIndex);
+				endCoordinates = new CellCoordinates(cell.rowIndex, cell.colIndex);
+				
+				if (beginCoordinates.isValid()) {
+					selectCellRange(beginCoordinates, endCoordinates);
+				}
+			}
+		}
+		
+		/**
+		 * Select table cells at the specified index.
+		 **/
+		public function selectCellAt(table:TableElement, rowIndex:int, colIndex:int):void {
+			var cell:TableCellElement = table.getCellAt(rowIndex, colIndex);
+			
+			if (cell) {
+				selectCell(cell);
+			}
+		}
+		
+		/**
+		 * Select table cells at the specified index
+		 **/
+		public function selectCells(cells:Vector.<TableCellElement>):void {
+			var startX:int = int.MAX_VALUE;
+			var startY:int = int.MAX_VALUE;
+			var endX:int = int.MIN_VALUE;
+			var endY:int = int.MIN_VALUE;
+			var cell:TableCellElement;
+			var table:TableElement;
+			for each(cell in cells)
+			{
+				if(cell)
+				{
+					if(table == null)
+						table = cell.getTable();
+					
+					var col:int = cell.colIndex;
+					var row:int = cell.rowIndex;
+					if(col < startX)
+						startX = col;
+					if(col > endX)
+						endX = col;
+					if(row < startY)
+						startY = row;
+					if(row > endY)
+						endY = row;
+				}
+			}
+			if(startX <= endX && startY <= endY)
+				selectCellRange(
+					new CellCoordinates(startY,startX,table),
+					new CellCoordinates(endY,endX,table)
+				);
+		}
+		
+		/**
+		 * Select the specified table row. 
+		 **/
+		public function selectRow(row:TableRowElement):void {
+			var beginCoordinates:CellCoordinates;
+			var endCoordinates:CellCoordinates;
+			
+			if (row) {
+				beginCoordinates = new CellCoordinates(row.rowIndex, 0);
+				endCoordinates = new CellCoordinates(row.rowIndex, row.numCells);
+				
+				if (beginCoordinates.isValid() && endCoordinates.isValid()) {
+					selectCellRange(beginCoordinates, endCoordinates);
+				}
+			}
+		}
+		
+		/**
+		 * Select a table row at the specified index
+		 **/
+		public function selectRowAt(table:TableElement, index:int):void {
+			var row:TableRowElement = table ? table.getRowAt(index) : null;
+			
+			if (row) {
+				selectRow(row);
+			}
+		}
+		
+		/**
+		 * Selects the table rows provided
+		 **/
+		public function selectRows(rows:Array):void {
+			var cells:Vector.<TableCellElement> = new Vector.<TableCellElement>();
+			var table:TableElement;
+			var cell:TableCellElement;
+			
+			if (rows && rows.length) {
+				
+				for (var i:int;i<rows.length;i++) 

+				{
+					var row:TableRowElement = rows[i] as TableRowElement;
+					
+					if (row)
+					{
+						for each(cell in row.cells)
+						cells.push(cell);
+					}
+				}
+				
+				selectCells(cells);
+			}
+		}
+		
+		/**
+		 * Select a table column. 
+		 **/
+		public function selectColumn(column:TableColElement):void {
+			var table:TableElement = column.table;
+			
+			if (column && table) {
+				selectCells(table.getCellsForColumn(column));
+			}
+		}
+		
+		/**
+		 * Select a table column at the specified index 
+		 **/
+		public function selectColumnAt(table:TableElement, index:int):void {
+			var column:TableColElement = table.getColumnAt(index);
+			
+			if (column && table) {
+				return selectColumn(column);
+			}
+		}
+		
+		/**
+		 * Selects the table columns provided
+		 **/
+		public function selectColumns(columns:Array):void {
+			var cells:Vector.<TableCellElement> = new Vector.<TableCellElement>();
+			var cell:TableCellElement;
+			
+			if (columns && columns.length) {
+				
+				for (var i:int;i<columns.length;i++) 
+				{
+					var column:TableColElement = columns[i] as TableColElement;
+					
+					if (column)
+					{
+						for each(cell in column.cells)
+							cells.push(cell);
+					}
+					
+				}
+				
+				selectCells(cells);
+			}
+		}
+		
+		/**
+		 * Select all cells in a table. 
+		 **/
+		public function selectTable(table:TableElement):void {
+			
+			if (table)
+			{
+				var startCoords:CellCoordinates = new CellCoordinates(0,0,table);
+				var endCoords:CellCoordinates = new CellCoordinates(table.numRows-1,table.numColumns-1,table);
+				selectCellRange(startCoords,endCoords);
+			}
+			
+		}
+		
+		/**
+		 * Select a range of table cells. 
+		 **/
+		public function selectCellRange(anchorCoords:CellCoordinates, activeCoords:CellCoordinates):void
+		{
+			var blocks:Vector.<TextFlowTableBlock>;
+			var block:TextFlowTableBlock;
+			var controller:ContainerController;
+			
+			if (selectionType == SelectionType.TEXT) {
+				clear();
+			}
+			clearCellSelections();
+			
+			if (anchorCoords && activeCoords) {
+				_cellRange = new CellRange(_currentTable, anchorCoords, activeCoords);
+				activeCellPosition = activeCoords;
+				blocks = _currentTable.getTableBlocksInRange(anchorCoords, activeCoords);
+				
+				for each(block in blocks) {
+					block.controller.clearSelectionShapes();
+					block.controller.addCellSelectionShapes(currentCellSelectionFormat.rangeColor, block, anchorCoords, activeCoords);
+				}
+				if(subManager)
+				{
+					subManager.selectRange(-1,-1);
+					subManager = null;
+				}
+			}
+			else
+			{
+				_cellRange = null;
+				activeCellPosition.column = -1;
+				activeCellPosition.row = -1;
+			}
+			selectionChanged();
+		}
+		
+		public function getCellRange():CellRange
+		{
+			// not really a good implementation. We'll fix this later
+			return _cellRange;
+		}
+		public function setCellRange(range:CellRange):void
+		{
+			selectCellRange(range.anchorCoordinates,range.activeCoordinates);
+			//_cellRange = range;
+			// do something about actually drawing the selection.
+		}
         
         // current range of selection
         /** Anchor point of the current selection, as an index into the TextFlow. */
         private var anchorMark:Mark;
         /** Active end of the current selection, as an index into the TextFlow. */
         private var activeMark:Mark;
-        
-        // used to save pending attributes at a point selection
+        private var _anchorCellPosition:CellCoordinates;
+		private var _activeCellPosition:CellCoordinates;
+
+		// used to save pending attributes at a point selection
         private var _pointFormat:ITextLayoutFormat;
         /** 
          * The format that will be applied to inserted text. 
@@ -190,6 +504,8 @@
             _textFlow = null;
             anchorMark = createMark();
             activeMark = createMark();
+			anchorCellPosition = createCellMark();
+			activeCellPosition = createCellMark();
             _pointFormat = null;
             _isActive = false;
             CONFIG::debug 
@@ -197,7 +513,12 @@
                 this.id = smCount.toString();
                 smCount++;
             }
+			
+			Mouse.registerCursor(SelectTable, createSelectTableCursor());
+			Mouse.registerCursor(SelectTableRow, createSelectTableRowCursor());
+			Mouse.registerCursor(SelectTableColumn, createSelectTableColumnCursor());
         }
+		
         /**
          * @copy ISelectionManager#getSelectionState()
          * 
@@ -211,7 +532,10 @@
          */
         public function getSelectionState():SelectionState
         {
-            return new SelectionState(_textFlow, anchorMark.position, activeMark.position, pointFormat);
+			if(subManager)
+				return subManager.getSelectionState();
+			
+            return new SelectionState(_textFlow, anchorMark.position, activeMark.position, pointFormat, _cellRange);
         }
                 
         /**
@@ -238,8 +562,45 @@
          * @langversion 3.0
          */
         public function hasSelection():Boolean
-        { return anchorMark.position != -1; }
+        {
+			return selectionType == SelectionType.TEXT;
+		}
 
+		/**
+		 *  @copy ISelectionManager#hasAnySelection()
+		 * 
+		 * @includeExample examples\SelectionManager_hasSelection.as -noswf
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function hasAnySelection():Boolean
+		{
+			return selectionType != SelectionType.NONE;
+		}
+
+		/**
+		 * Indicates the type of selection. 
+		 * 
+		 * <p>The <code>selectionType</code> describes the kind of selection. 
+		 * It can either be <code>SelectionType.TEXT</code> or <code>SelectionType.CELLS</code>
+		 * 
+		 * @see flashx.textLayout.edit.SelectionType
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get selectionType() : String
+		{
+			if(anchorMark.position != -1)
+				return SelectionType.TEXT;
+			else if(anchorCellPosition.isValid())
+				return SelectionType.CELLS;
+			
+			return SelectionType.NONE;
+		}
         /** 
          *  @copy ISelectionManager#isRangeSelection()
          * 
@@ -277,6 +638,8 @@
                     flushPendingOperations();
                 
                 clear();
+				clearCellSelections();
+				_cellRange = null;
                 
                 // If we switch into read-only mode, make sure the cursor isn't showing a text selection IBeam
                 if (!value) // see Watson 2637162
@@ -348,7 +711,29 @@
             }
             return focusedSelectionFormat;
          }
-         
+
+		 /**
+		  *  @copy ISelectionManager#currentCellSelectionFormat
+		  * 
+		  * @playerversion Flash 10
+		  * @playerversion AIR 1.5
+		  * @langversion 3.0
+		  * 
+		  * @see flashx.textLayout.edit.SelectionFormat
+		  */
+		 public function get currentCellSelectionFormat():SelectionFormat
+		 { 
+			 if (_selFormatState == SelectionFormatState.UNFOCUSED)
+			 {
+				 return unfocusedCellSelectionFormat;
+			 }
+			 else if (_selFormatState == SelectionFormatState.INACTIVE)
+			 {
+				 return inactiveCellSelectionFormat;
+			 }
+			 return focusedCellSelectionFormat;
+		 }
+
         /**
          *  @copy ISelectionManager#focusedSelectionFormat
          * 
@@ -420,7 +805,79 @@
          { 
             return _inactiveSelectionFormat ? _inactiveSelectionFormat : (_textFlow ? _textFlow.configuration.inactiveSelectionFormat : null);
          }       
-         
+
+		 /**
+		  *  @copy ISelectionManager#focusedCellSelectionFormat
+		  * 
+		  * @playerversion Flash 10
+		  * @playerversion AIR 1.5
+		  * @langversion 3.0
+		  * 
+		  * @see flashx.textLayout.edit.SelectionFormat
+		  */
+		 public function set focusedCellSelectionFormat(val:SelectionFormat):void
+		 { 
+			 _focusedCellSelectionFormat = val;
+			 if (this._selFormatState == SelectionFormatState.FOCUSED)
+				 refreshSelection();
+		 }
+		 
+		 /**
+		  * @private - docs on setter
+		  */
+		 public function get focusedCellSelectionFormat():SelectionFormat
+		 { 
+			 return _focusedCellSelectionFormat ? _focusedCellSelectionFormat : (_textFlow ? _textFlow.configuration.focusedSelectionFormat : null);
+		 }       
+		 
+		 /**
+		  *  @copy ISelectionManager#unfocusedCellSelectionFormat
+		  * 
+		  * @playerversion Flash 10
+		  * @playerversion AIR 1.5
+		  * @langversion 3.0
+		  * 
+		  * @see flashx.textLayout.edit.SelectionFormat
+		  */
+		 public function set unfocusedCellSelectionFormat(val:SelectionFormat):void
+		 { 
+			 _unfocusedCellSelectionFormat = val;
+			 if (this._selFormatState == SelectionFormatState.UNFOCUSED)
+				 refreshSelection();
+		 }          
+		 
+		 /**
+		  *  @private - docs on setter
+		  */
+		 public function get unfocusedCellSelectionFormat():SelectionFormat
+		 { 
+			 return _unfocusedCellSelectionFormat ? _unfocusedCellSelectionFormat : (_textFlow ? _textFlow.configuration.unfocusedSelectionFormat : null);
+		 }
+		 
+		 /**
+		  *  @copy ISelectionManager#inactiveCellSelectionFormat
+		  * 
+		  * @playerversion Flash 10
+		  * @playerversion AIR 1.5
+		  * @langversion 3.0
+		  * 
+		  * @see flashx.textLayout.edit.SelectionFormat
+		  */
+		 public function set inactiveCellSelectionFormat(val:SelectionFormat):void
+		 { 
+			 _inactiveCellSelectionFormat = val;
+			 if (this._selFormatState == SelectionFormatState.INACTIVE)
+				 refreshSelection();
+		 }          
+		 
+		 /**
+		  * @private - docs on setter
+		  */
+		 public function get inactiveCellSelectionFormat():SelectionFormat
+		 { 
+			 return _inactiveCellSelectionFormat ? _inactiveCellSelectionFormat : (_textFlow ? _textFlow.configuration.inactiveSelectionFormat : null);
+		 }       
+		 
          /** @private - returns the selectionFormatState.  @see flashx.textLayout.edit.SelectionFormatState */
          tlf_internal function get selectionFormatState():String
          { return _selFormatState; }
@@ -584,11 +1041,17 @@
          */
         public function selectAll() : void
         {
-            selectRange(0, int.MAX_VALUE);
+			if(subManager)
+				subManager.selectAll();
+			else
+			{
+				var lastSelectablePos:int = (_textFlow.textLength > 0) ? _textFlow.textLength - 1 : 0;
+				selectRange(0, lastSelectablePos);
+			}
         }
         
         /** 
-         *  @copy ISelectionManager#selectRange
+         * @copy ISelectionManager#selectRange
          * 
          * @includeExample examples\SelectionManager_selectRange.as -noswf
          * 
@@ -601,22 +1064,76 @@
         public function selectRange(anchorPosition:int, activePosition:int) : void
         {
             flushPendingOperations();
+			
+			if(subManager)
+				subManager.selectRange(-1,-1);
             
             // anchor and active can be in any order
             // TODO: range check and clamp anchor,active
             if (anchorPosition != anchorMark.position || activePosition != activeMark.position)
             {   
                 clearSelectionShapes();
+				clearCellSelections();
+				_cellRange = null;
                     
-                internalSetSelection(_textFlow, anchorPosition, activePosition);
+                internalSetSelection(_textFlow, anchorPosition, activePosition, _pointFormat);
                 
                 // selection changed
-                selectionChanged();     
+                selectionChanged();
                 
                 allowOperationMerge = false;
             }
         }
-        
+		
+		/** 
+		 * @copy ISelectionManager#selectFirstPosition
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 * @see flashx.textLayout.compose.IFlowComposer
+		 */
+		public function selectFirstPosition():void
+		{
+			selectRange(0, 0);
+		}
+		
+		/** 
+		 * @copy ISelectionManager#selectLastPosition
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 * @see flashx.textLayout.compose.IFlowComposer
+		 */
+		public function selectLastPosition():void
+		{
+			selectRange(int.MAX_VALUE, int.MAX_VALUE);
+		}
+
+		/** 
+		 * @copy ISelectionManager#deselect
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 * @see flashx.textLayout.compose.IFlowComposer
+		 */
+		public function deselect():void
+		{
+			if (hasAnySelection())
+			{
+				clearSelectionShapes();
+				clearCellSelections();
+				addSelectionShapes();
+			}
+			selectRange(-1,-1);
+			_cellRange = null;
+		}
+
         private function internalSetSelection(root:TextFlow,anchorPosition:int,activePosition:int,format:ITextLayoutFormat = null) : void
         {
             _textFlow = root;
@@ -627,6 +1144,8 @@
                 anchorPosition = -1;
                 activePosition = -1;
             }
+			else if(subManager)
+				subManager = null;
             
             var lastSelectablePos:int = (_textFlow.textLength > 0) ? _textFlow.textLength - 1 : 0;
             
@@ -645,7 +1164,8 @@
         //  trace("Selection ", anchorMark, "to", activeMark.position);
         }       
         
-        /** Clear any active selections.
+        /** 
+		 * Clear any active selections.
          */
         private function clear(): void
         {
@@ -659,7 +1179,32 @@
                 allowOperationMerge = false;
             }
         }
-        
+        /**
+		 * Clear any cell selections
+		 * */
+		private function clearCellSelections():void
+		{
+			var blocks:Vector.<TextFlowTableBlock>;
+			var block:TextFlowTableBlock;
+			var controller:ContainerController;
+			
+			if (_cellRange) {
+				blocks = _cellRange.table.getTableBlocksInRange(_cellRange.anchorCoordinates, _cellRange.activeCoordinates);
+				
+				for each (block in blocks) {
+					if (controller != block.controller) {
+						block.controller.clearSelectionShapes();
+					}
+					
+					controller = block.controller;
+				}
+				
+			}
+			if(block)
+				block.controller.clearSelectionShapes();
+			
+			//_cellRange = null;
+		}
         private function addSelectionShapes():void
         {
             if (_textFlow.flowComposer)
@@ -677,7 +1222,7 @@
                     {
                         _textFlow.flowComposer.getControllerAt(containerIter++).addSelectionShapes(currentSelectionFormat, absoluteStart, absoluteEnd);
                     }
-                } 
+                }
             }
         }
         
@@ -693,22 +1238,39 @@
                 }
             }
         }
-        
-        /** 
-         *  @copy ISelectionManager#refreshSelection()
-         * 
-         * @playerversion Flash 10
-         * @playerversion AIR 1.5
-         * @langversion 3.0
-        */
-        public function refreshSelection(): void
-        {
-            if (hasSelection())
-            {
-                clearSelectionShapes();
-                addSelectionShapes();
-            }
-        }
+		
+		/** 
+		 *  @copy ISelectionManager#refreshSelection()
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function refreshSelection(): void
+		{
+			if (hasAnySelection())
+			{
+				clearSelectionShapes();
+				clearCellSelections();
+				addSelectionShapes();
+			}
+		}
+		
+		/** 
+		 *  @copy ISelectionManager#clearSelection()
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function clearSelection(): void
+		{
+			if (hasAnySelection())
+			{
+				clearSelectionShapes();
+				clearCellSelections();
+			}
+		}
         
         /** Verifies that the selection is in a legal state. @private */
         CONFIG::debug public function debugCheckSelectionManager():int
@@ -745,7 +1307,12 @@
                 _pointFormat = null;
             
             if (doDispatchEvent && _textFlow)
-                textFlow.dispatchEvent(new SelectionEvent(SelectionEvent.SELECTION_CHANGE, false, false, hasSelection() ? getSelectionState() : null));
+			{
+				if(textFlow.parentElement && textFlow.parentElement.getTextFlow())
+					textFlow.parentElement.getTextFlow().dispatchEvent(new SelectionEvent(SelectionEvent.SELECTION_CHANGE, false, false, hasSelection() ? getSelectionState() : null));
+				else
+					textFlow.dispatchEvent(new SelectionEvent(SelectionEvent.SELECTION_CHANGE, false, false, hasSelection() ? getSelectionState() : null));
+			}
         }
 
         // TODO: this routine could be much more efficient - instead of iterating over all lines in the TextFlow it should iterate over 
@@ -771,11 +1338,14 @@
             //get the nearest column so we can ignore lines which aren't in the column we're looking for.
             //if we don't do this, we won't be able to select across column boundaries.
             var nearestColIdx:int = locateNearestColumn(controller, localX, localY, textFlow.computedFormat.blockProgression,textFlow.computedFormat.direction);
+
+			var prevLineBounds:Rectangle = null;
+			var previousLineIndex:int = -1;
+
+			/*
 			//For the table feature, we are trying to make sure if the current point is in the table and which cell it is in
-			var nearestCell:TableDataCellElement = locateNearestCell(controller, localX, localY, textFlow.computedFormat.blockProgression,textFlow.computedFormat.direction);
+			var nearestCell:TableCellElement = locateNearestCell(controller, localX, localY, textFlow.computedFormat.blockProgression,textFlow.computedFormat.direction);
             
-            var prevLineBounds:Rectangle = null;
-            var previousLineIndex:int = -1;
 			
 			if(nearestCell)
 			{
@@ -790,7 +1360,7 @@
 					return cellPara.getAbsoluteStart() + cellPara.textLength - 1;
 				}
 			}
-            
+            */
             var lastLineIndexInColumn:int = -1;
             
             // Matching TextFlowLine and TextLine - they are not necessarily valid
@@ -845,6 +1415,7 @@
                     //current line,. Otherwise, if the click's perpendicular coordinate is below the mid point between the current
                     //line or below it, then we want to use the line below (ie the previous line, but logically the one after the current)
                     var inPrevLine:Boolean = midPerpCoor != -1 && (isTTB ? perpCoor < midPerpCoor : perpCoor > midPerpCoor);
+					/*
 					if(rtline.paragraph.isInTable())
 					{
 						//if rtline is the last line of the cell and the isPrevLine is true, find the cell of the column in next row
@@ -852,10 +1423,10 @@
 						if ( inPrevLine && testIndex != lastLineIndexInColumn )
 						{
 							var rtPara:ParagraphElement = rtline.paragraph;
-							var rtCell:TableDataCellElement = rtPara.getTableDataCellElement();
+							var rtCell:TableCellElement = rtPara.getParentCellElement();
 							//get the last element of the cell
 							var lastElement:FlowElement = rtCell.getLastLeaf();
-							var rtLastTbLine:TextFlowLine = lastElement.getParagraph().getTextBlock().lastLine.userData;
+							var rtLastTbLine:TextFlowLine = lastElement ? lastElement.getParagraph().getTextBlock().lastLine.userData : null;
 							if( rtline == rtLastTbLine )
 							{
 								//temproray codes, need to be updated when the column apis are ready
@@ -864,7 +1435,7 @@
 								var nextRow:TableRowElement = rtRow.getNextSibling() as TableRowElement;
 								if ( nextRow && rtCell )
 								{
-									var nextCell:TableDataCellElement = nextRow.getChildAt(rtCell.colIndex) as TableDataCellElement;
+									var nextCell:TableCellElement = nextRow.getChildAt(rtCell.colIndex) as TableCellElement;
 									lineIndex = textFlow.flowComposer.findLineIndexAtPosition(nextCell.getFirstLeaf().getParagraph().getAbsoluteStart());
 								}
 							}
@@ -875,6 +1446,7 @@
 							lineIndex = testIndex;
 					}
 					else
+					*/
                     	lineIndex = inPrevLine && testIndex != lastLineIndexInColumn ? testIndex+1 : testIndex;
 					break;
                 }
@@ -895,79 +1467,87 @@
                 
             //Get a valid textLine -- check to make sure line is valid, regenerate if necessary, make sure it has correct container relative coordinates
             var textFlowLine:TextFlowLine = textFlow.flowComposer.getLineAt(lineIndex);
-            var textLine:TextLine = textFlowLine.getTextLine(true);
-            
-            // adjust localX,localY to be relative to the textLine.  
-            // Can't use localToGlobal/globalToLocal because textLine may not be on the display list due to virtualization
-            // we may need to bring this back if textline's can be rotated or placed by any mechanism other than a translation
-            // but then we'll need to provisionally place a virtualized TextLine in its parent container
-            localX -= textLine.x;
-            localY -= textLine.y;
-            /* var localPoint:Point = DisplayObject(controller.container).localToGlobal(new Point(localX,localY));
-            localPoint = textLine.globalToLocal(localPoint);
-            localX = localPoint.x;
-            localY = localPoint.y; */
-            
-            
-            var startOnNextLineIfNecessary:Boolean = false;
-            
-            var lastAtom:int = -1;
-            if (isDirectionRTL) {
-                lastAtom = textLine.atomCount - 1;
-            } else {
-                if ((textFlowLine.absoluteStart + textFlowLine.textLength) >= textFlowLine.paragraph.getAbsoluteStart() + textFlowLine.paragraph.textLength) {
-                    if (textLine.atomCount > 1) lastAtom = textLine.atomCount - 2;
-                } else {
-                    var lastLinePosInPar:int = textFlowLine.absoluteStart + textFlowLine.textLength - 1;
-                    var lastChar:String = textLine.textBlock.content.rawText.charAt(lastLinePosInPar);
-                    if (lastChar == " ") {
-                        if (textLine.atomCount > 1) lastAtom = textLine.atomCount - 2;
-                    } else {
-                        startOnNextLineIfNecessary = true;
-                        if (textLine.atomCount > 0) lastAtom = textLine.atomCount - 1;
-                    }
-                }
-            }
-            var lastAtomRect:Rectangle = (lastAtom > 0) ? textLine.getAtomBounds(lastAtom) : new Rectangle(0, 0, 0, 0);
-                        
-            if (!isTTB)
-            {
-                if (localX < 0)
-                    localX = 0;
-                else if (localX > (lastAtomRect.x + lastAtomRect.width))
-                {
-                    if (startOnNextLineIfNecessary) 
-                        return textFlowLine.absoluteStart + textFlowLine.textLength - 1;
-                    if (lastAtomRect.x + lastAtomRect.width > 0)
-                        localX = lastAtomRect.x + lastAtomRect.width;
-                }
-            }
-            else
-            {   
-                if (localY < 0) 
-                    localY = 0;
-                else if (localY > (lastAtomRect.y + lastAtomRect.height))
-                {
-                    if (startOnNextLineIfNecessary) 
-                        return textFlowLine.absoluteStart + textFlowLine.textLength - 1;    
-                    if (lastAtomRect.y + lastAtomRect.height > 0)
-                        localY = lastAtomRect.y + lastAtomRect.height;
-                }
-            }
-            
-			result = computeSelectionIndexInLine(textFlow, textLine, localX, localY);
+			if(textFlowLine is TextFlowTableBlock)
+			{
+				result = TextFlowTableBlock(textFlowLine).absoluteStart;
+			}
+			else
+			{
+				var textLine:TextLine = textFlowLine.getTextLine(true);
+				
+				// adjust localX,localY to be relative to the textLine.  
+				// Can't use localToGlobal/globalToLocal because textLine may not be on the display list due to virtualization
+				// we may need to bring this back if textline's can be rotated or placed by any mechanism other than a translation
+				// but then we'll need to provisionally place a virtualized TextLine in its parent container
+				localX -= textLine.x;
+				localY -= textLine.y;
+				/* var localPoint:Point = DisplayObject(controller.container).localToGlobal(new Point(localX,localY));
+				localPoint = textLine.globalToLocal(localPoint);
+				localX = localPoint.x;
+				localY = localPoint.y; */
+				
+				
+				var startOnNextLineIfNecessary:Boolean = false;
+				
+				var lastAtom:int = -1;
+				if (isDirectionRTL) {
+					lastAtom = textLine.atomCount - 1;
+				} else {
+					if ((textFlowLine.absoluteStart + textFlowLine.textLength) >= textFlowLine.paragraph.getAbsoluteStart() + textFlowLine.paragraph.textLength) {
+						if (textLine.atomCount > 1) lastAtom = textLine.atomCount - 2;
+					} else {
+						var lastLinePosInPar:int = textFlowLine.absoluteStart + textFlowLine.textLength - 1;
+						var lastChar:String = textLine.textBlock.content.rawText.charAt(lastLinePosInPar);
+						if (lastChar == " ") {
+							if (textLine.atomCount > 1) lastAtom = textLine.atomCount - 2;
+						} else {
+							startOnNextLineIfNecessary = true;
+							if (textLine.atomCount > 0) lastAtom = textLine.atomCount - 1;
+						}
+					}
+				}
+				var lastAtomRect:Rectangle = (lastAtom > 0) ? textLine.getAtomBounds(lastAtom) : new Rectangle(0, 0, 0, 0);
+				
+				if (!isTTB)
+				{
+					if (localX < 0)
+						localX = 0;
+					else if (localX > (lastAtomRect.x + lastAtomRect.width))
+					{
+						if (startOnNextLineIfNecessary) 
+							return textFlowLine.absoluteStart + textFlowLine.textLength - 1;
+						if (lastAtomRect.x + lastAtomRect.width > 0)
+							localX = lastAtomRect.x + lastAtomRect.width;
+					}
+				}
+				else
+				{   
+					if (localY < 0) 
+						localY = 0;
+					else if (localY > (lastAtomRect.y + lastAtomRect.height))
+					{
+						if (startOnNextLineIfNecessary) 
+							return textFlowLine.absoluteStart + textFlowLine.textLength - 1;    
+						if (lastAtomRect.y + lastAtomRect.height > 0)
+							localY = lastAtomRect.y + lastAtomRect.height;
+					}
+				}
+				
+				result = computeSelectionIndexInLine(textFlow, textLine, localX, localY);
+			}
+
             // trace("computeSelectionIndexInContainer:(",origX,origY,")",textFlow.flowComposer.getControllerIndex(controller).toString(),lineIndex.toString(),result.toString());
             return result != -1 ? result : firstCharVisible + length;   
         }
-		
-		static private function locateNearestCell(container:ContainerController, localX:Number, localY:Number, wm:String, direction:String):TableDataCellElement
+		/*
+		static private function locateNearestCell(container:ContainerController, localX:Number, localY:Number, wm:String, direction:String):TableCellElement
 		{
 			var cellIdx:int = 0;
 			//if we only have 1 column, no need to perform calculation...
 			var columnState:ColumnState = container.columnState;
 			
 			var isFound:Boolean = false;
-			var curCell:TableDataCellElement = null;
+			var curCell:TableCellElement = null;
 			
 			//we need to compare the current column to the nextColmn
 			while(cellIdx < columnState.cellCount - 1)
@@ -984,7 +1564,7 @@
 			}
 			return isFound? curCell : null;
 		}
-        
+        */
         static private function locateNearestColumn(container:ContainerController, localX:Number, localY:Number, wm:String, direction:String):int
         {
             var colIdx:int = 0;
@@ -1141,9 +1721,8 @@
             else  // Left to right case, right is "end" unicode
                 paraSelectionIdx = leanRight ? textLine.getAtomTextBlockEndIndex(elemIdx) : textLine.getAtomTextBlockBeginIndex(elemIdx);
 
-            //we again need to do some fixup here.  Unfortunately, we don't have the index into the paragraph until
-            
-            return rtline.paragraph.getAbsoluteStart() + paraSelectionIdx;
+			//we again need to do some fixup here.  Unfortunately, we don't have the index into the paragraph until
+            return rtline.paragraph.getTextBlockAbsoluteStart(textLine.textBlock) + paraSelectionIdx;
         }
         
         static private function checkForDisplayed(container:DisplayObject):Boolean
@@ -1164,6 +1743,142 @@
             return false;   // not on the stage
 
         }
+		/** @private - find a controller and adjusts the x and y values of localPoint if necessary */
+		private static function findController(textFlow:TextFlow, target:Object, currentTarget:Object, localPoint:Point):ContainerController
+		{
+			var localX:Number = localPoint.x;
+			var localY:Number = localPoint.y;
+			var controller:ContainerController;
+			var containerPoint:Point; // scratch
+			
+			var globalPoint:Point = DisplayObject(target).localToGlobal(new Point(localX, localY));
+
+			for (var idx:int = 0; idx < textFlow.flowComposer.numControllers; idx++)
+			{
+				var testController:ContainerController = textFlow.flowComposer.getControllerAt(idx); 
+				if (testController.container == target || testController.container == currentTarget)
+				{
+					controller = testController;
+					break;
+				}
+			}
+			if (controller)
+			{   
+				if (target != controller.container)
+				{
+					containerPoint = DisplayObject(controller.container).globalToLocal(globalPoint);
+					localPoint.x = containerPoint.x;
+					localPoint.y = containerPoint.y;
+				}
+				return controller;         
+			} 
+			
+			//the point is someplace else on stage.  Map the target 
+			//to the textFlow.container.
+			CONFIG::debug { assert(textFlow.flowComposer && textFlow.flowComposer.numControllers,"findController: invalid textFlow"); }
+			
+			
+			
+			// result of the search
+			var controllerCandidate:ContainerController = null;
+			var candidateLocalX:Number;
+			var candidateLocalY:Number;
+			var relDistance:Number = Number.MAX_VALUE;
+			
+			for (var containerIndex:int = 0; containerIndex < textFlow.flowComposer.numControllers; containerIndex++)
+			{
+				var curContainerController:ContainerController = textFlow.flowComposer.getControllerAt(containerIndex);
+				
+				// displayed??
+				if (!checkForDisplayed(curContainerController.container as DisplayObject))
+					continue;
+				
+				// handle measured containers??
+				var bounds:Rectangle = curContainerController.getContentBounds();
+				var containerWidth:Number = isNaN(curContainerController.compositionWidth) ? curContainerController.getTotalPaddingLeft()+bounds.width : curContainerController.compositionWidth;
+				var containerHeight:Number = isNaN(curContainerController.compositionHeight) ? curContainerController.getTotalPaddingTop()+bounds.height : curContainerController.compositionHeight;
+				
+				containerPoint = DisplayObject(curContainerController.container).globalToLocal(globalPoint);
+				
+				// remove scrollRect effects for the distance test but add it back in for the result
+				var adjustX:Number = 0;
+				var adjustY:Number = 0;
+				
+				if (curContainerController.hasScrollRect)
+				{
+					containerPoint.x -= (adjustX = curContainerController.container.scrollRect.x);
+					containerPoint.y -= (adjustY = curContainerController.container.scrollRect.y);
+				}
+				
+				if ((containerPoint.x >= 0) && (containerPoint.x <= containerWidth) &&
+					(containerPoint.y >= 0) && (containerPoint.y <= containerHeight))
+				{
+					controllerCandidate = curContainerController;
+					candidateLocalX = containerPoint.x+adjustX;
+					candidateLocalY = containerPoint.y+adjustY;
+					break;
+				}
+				
+				// figure minimum distance of containerPoint to curContainerController - 8 cases
+				var relDistanceX:Number = 0;
+				var relDistanceY:Number = 0;
+				
+				if (containerPoint.x < 0)
+				{
+					relDistanceX = containerPoint.x;
+					if (containerPoint.y < 0)
+						relDistanceY = containerPoint.y;
+					else if (containerPoint.y > containerHeight)
+						relDistanceY = containerPoint.y-containerHeight;
+				}
+				else if (containerPoint.x > containerWidth)
+				{
+					relDistanceX = containerPoint.x-containerWidth;
+					if (containerPoint.y < 0)
+						relDistanceY = containerPoint.y;
+					else if (containerPoint.y > containerHeight)
+						relDistanceY = containerPoint.y-containerHeight;
+				}
+				else if (containerPoint.y < 0)
+					relDistanceY = -containerPoint.y;
+				else
+					relDistanceY = containerPoint.y-containerHeight;
+				var tempDist:Number = relDistanceX*relDistanceX + relDistanceY*relDistanceY;    // could do sqrt but why bother - there is no Math.hypot function
+				if (tempDist <= relDistance)
+				{
+					relDistance = tempDist;
+					controllerCandidate = curContainerController;
+					candidateLocalX = containerPoint.x+adjustX;
+					candidateLocalY = containerPoint.y+adjustY;
+				}
+			}
+			localPoint.x = candidateLocalX;
+			localPoint.y = candidateLocalY;
+			return controllerCandidate;
+
+		}
+		/** @private - given a target and location compute the CellCoordinates */
+		static tlf_internal function computeCellCoordinates(textFlow:TextFlow, target:Object, currentTarget:Object, localX:Number, localY:Number):CellCoordinates
+		{
+			var rslt:CellCoordinates;
+			var containerPoint:Point; // scratch
+			
+
+			if (target is TextLine)
+				return null;
+			if(target is CellContainer)
+			{
+				var cell:TableCellElement = (target as CellContainer).element;
+				return new CellCoordinates(cell.rowIndex, cell.colIndex, cell.getTable());
+			}
+			var localPoint:Point = new Point(localX, localY);
+			var controller:ContainerController = findController(textFlow, target, currentTarget, localPoint);
+			if(!controller)
+				return null;
+			
+			return controller.findCellAtPosition(localPoint);
+		}
+
         /** @private - given a target and location compute the selectionIndex */
         static tlf_internal function computeSelectionIndex(textFlow:TextFlow, target:Object, currentTarget:Object, localX:Number,localY:Number):int
         {           
@@ -1196,112 +1911,9 @@
                 rslt = computeSelectionIndexInLine(textFlow, TextLine(target), localX, localY);
             else
             {
-                var controller:ContainerController;
-                for (var idx:int = 0; idx < textFlow.flowComposer.numControllers; idx++)
-                {
-                    var testController:ContainerController = textFlow.flowComposer.getControllerAt(idx); 
-                    if (testController.container == target || testController.container == currentTarget)
-                    {
-                        controller = testController;
-                        break;
-                    }
-                }
-                if (controller)
-                {   
-                    if (target != controller.container)
-                    {
-                        containerPoint = DisplayObject(target).localToGlobal(new Point(localX, localY));
-                        containerPoint = DisplayObject(controller.container).globalToLocal(containerPoint);
-                        localX = containerPoint.x;
-                        localY = containerPoint.y;
-                    }
-                    rslt = computeSelectionIndexInContainer(textFlow, controller, localX, localY);          
-                } 
-                else 
-                {
-                    //the point is someplace else on stage.  Map the target 
-                    //to the textFlow.container.
-                    CONFIG::debug { assert(textFlow.flowComposer && textFlow.flowComposer.numControllers,"computeSelectionIndex: invalid textFlow"); }
-                    
-                    
-                    // result of the search
-                    var controllerCandidate:ContainerController = null;
-                    var candidateLocalX:Number;
-                    var candidateLocalY:Number;
-                    var relDistance:Number = Number.MAX_VALUE;
-                    
-                    for (var containerIndex:int = 0; containerIndex < textFlow.flowComposer.numControllers; containerIndex++)
-                    {
-                        var curContainerController:ContainerController = textFlow.flowComposer.getControllerAt(containerIndex);
-                        
-                        // displayed??
-                        if (!checkForDisplayed(curContainerController.container as DisplayObject))
-                            continue;
-
-                        // handle measured containers??
-                        var bounds:Rectangle = curContainerController.getContentBounds();
-                        var containerWidth:Number = isNaN(curContainerController.compositionWidth) ? curContainerController.getTotalPaddingLeft()+bounds.width : curContainerController.compositionWidth;
-                        var containerHeight:Number = isNaN(curContainerController.compositionHeight) ? curContainerController.getTotalPaddingTop()+bounds.height : curContainerController.compositionHeight;
-                        
-                        containerPoint = DisplayObject(target).localToGlobal(new Point(localX, localY));
-                        containerPoint = DisplayObject(curContainerController.container).globalToLocal(containerPoint);
-                        
-                        // remove scrollRect effects for the distance test but add it back in for the result
-                        var adjustX:Number = 0;
-                        var adjustY:Number = 0;
-                        
-                        if (curContainerController.hasScrollRect)
-                        {
-                            containerPoint.x -= (adjustX = curContainerController.container.scrollRect.x);
-                            containerPoint.y -= (adjustY = curContainerController.container.scrollRect.y);
-                        }
-                        
-                        if ((containerPoint.x >= 0) && (containerPoint.x <= containerWidth) &&
-                            (containerPoint.y >= 0) && (containerPoint.y <= containerHeight))
-                        {
-                            controllerCandidate = curContainerController;
-                            candidateLocalX = containerPoint.x+adjustX;
-                            candidateLocalY = containerPoint.y+adjustY;
-                            break;
-                        }
-                        
-                        // figure minimum distance of containerPoint to curContainerController - 8 cases
-                        var relDistanceX:Number = 0;
-                        var relDistanceY:Number = 0;
-
-                        if (containerPoint.x < 0)
-                        {
-                            relDistanceX = containerPoint.x;
-                            if (containerPoint.y < 0)
-                                relDistanceY = containerPoint.y;
-                            else if (containerPoint.y > containerHeight)
-                                relDistanceY = containerPoint.y-containerHeight;
-                        }
-                        else if (containerPoint.x > containerWidth)
-                        {
-                            relDistanceX = containerPoint.x-containerWidth;
-                            if (containerPoint.y < 0)
-                                relDistanceY = containerPoint.y;
-                            else if (containerPoint.y > containerHeight)
-                                relDistanceY = containerPoint.y-containerHeight;
-                        }
-                        else if (containerPoint.y < 0)
-                            relDistanceY = -containerPoint.y;
-                        else
-                            relDistanceY = containerPoint.y-containerHeight;
-                        var tempDist:Number = relDistanceX*relDistanceX + relDistanceY*relDistanceY;    // could do sqrt but why bother - there is no Math.hypot function
-                        if (tempDist <= relDistance)
-                        {
-                            relDistance = tempDist;
-                            controllerCandidate = curContainerController;
-                            candidateLocalX = containerPoint.x+adjustX;
-                            candidateLocalY = containerPoint.y+adjustY;
-                        }
-                    }
-
-
-                    rslt = controllerCandidate ? computeSelectionIndexInContainer(textFlow, controllerCandidate, candidateLocalX, candidateLocalY) : -1;
-                }
+				var localPoint:Point = new Point(localX,localY);
+                var controller:ContainerController = findController(textFlow, target, currentTarget, localPoint);
+				rslt = controller ? computeSelectionIndexInContainer(textFlow, controller, localPoint.x, localPoint.y) : -1;
             }
             
             if (rslt >= textFlow.textLength)
@@ -1339,7 +1951,50 @@
          */ 
         public function mouseDownHandler(event:MouseEvent):void
         {
-            handleMouseEventForSelection(event, event.shiftKey);
+			if(subManager)
+				subManager.selectRange(-1,-1);
+			
+			var cell:TableCellElement = _textFlow.parentElement as TableCellElement;
+			var coords:CellCoordinates;
+			if(!cell)
+				coords = computeCellCoordinates(textFlow,event.target,event.currentTarget,event.localX, event.localY);
+			if(cell || coords)
+			{
+				if(coords)
+					cell = coords.table.findCell(coords);
+				
+				superManager = cell.getTextFlow().interactionManager;
+				if(event.shiftKey && cell.getTable() == superManager.currentTable)
+				{
+					// expand cell selection if applicable
+					coords = new CellCoordinates(cell.rowIndex,cell.colIndex);
+					if(
+						!CellCoordinates.areEqual(coords,superManager.anchorCellPosition) ||
+						superManager.activeCellPosition.isValid()
+					){
+						superManager.selectCellRange(superManager.anchorCellPosition,coords);
+						superManager.subManager = null;
+						allowOperationMerge = false;
+						event.stopPropagation();
+						return;
+					}
+				}
+				if(superManager == this)
+				{
+					if(cell.textFlow.interactionManager)
+					{
+						cell.textFlow.interactionManager.mouseDownHandler(event);
+					}
+					return;
+				}
+				superManager.currentTable = cell.getTable();
+				superManager.deselect();
+				//superManager.setSelectionState(new SelectionState(superManager.textFlow,-1,-1) );
+				superManager.anchorCellPosition.column = cell.colIndex;
+				superManager.anchorCellPosition.row = cell.rowIndex;
+				superManager.subManager = this;
+			}
+            handleMouseEventForSelection(event, event.shiftKey, cell != null);
         }
         
         /**
@@ -1348,17 +2003,56 @@
          * @playerversion AIR 1.5
          * @langversion 3.0
          */ 
-        public function mouseMoveHandler(event:MouseEvent):void
-        {
+        public function mouseMoveHandler(event:MouseEvent):void {
             var wmode:String = textFlow.computedFormat.blockProgression;            
-            if (wmode != BlockProgression.RL) 
-                setMouseCursor(MouseCursor.IBEAM);          
+			
+			if (wmode != BlockProgression.RL) {
+                setMouseCursor(MouseCursor.IBEAM);
+			}
+			
+			
             if (event.buttonDown)
-                handleMouseEventForSelection(event, true);
+			{
+				var cell:TableCellElement = _textFlow.parentElement as TableCellElement;
+				
+				// if the event is owned by a cell, we need to check if the mouse is now above another cell to select a cell range.
+				if (cell) {
+					
+					do {
+						var cellCoords:CellCoordinates = new CellCoordinates(cell.rowIndex, cell.colIndex, cell.getTable());
+						var coords:CellCoordinates = computeCellCoordinates(cell.getTextFlow(), event.target, event.currentTarget, event.localX, event.localY);
+						if(!coords)
+							break;
+						if(CellCoordinates.areEqual(cellCoords, coords) &&
+							(!superManager.activeCellPosition.isValid() || CellCoordinates.areEqual(coords, superManager.activeCellPosition))
+						)
+							break;
+						if(coords.table != cellCoords.table)
+							break;
+						
+						superManager = cell.getTextFlow().interactionManager;
+						if(
+							!CellCoordinates.areEqual(coords, superManager.activeCellPosition)
+						){
+							allowOperationMerge = false;
+							superManager.selectCellRange(superManager.anchorCellPosition, coords);
+							event.stopPropagation();
+							return;
+						}
+
+						
+					}while(0);
+				}
+				if(superManager && superManager.getCellRange())
+					return;
+				
+				handleMouseEventForSelection(event, true, _textFlow.parentElement != null);
+
+			}
         }
         
         /** @private */
-        tlf_internal function handleMouseEventForSelection(event:MouseEvent, allowExtend:Boolean):void
+        tlf_internal function handleMouseEventForSelection(event:MouseEvent, allowExtend:Boolean,stopPropogate:Boolean=false):void
         {
             var startSelectionActive:Boolean = hasSelection();
             
@@ -1371,6 +2065,8 @@
                     addSelectionShapes();
             }       
             allowOperationMerge = false;
+			if(stopPropogate)
+				event.stopPropagation();
         }
         
         /** 
@@ -1479,10 +2175,60 @@
         {
             _mouseOverSelectionArea = true;
             var wmode:String = textFlow.computedFormat.blockProgression;
-            if (wmode != BlockProgression.RL) 
-                setMouseCursor(MouseCursor.IBEAM);  
-            else 
-                setMouseCursor(MouseCursor.AUTO);                               
+			
+            if (wmode != BlockProgression.RL) {
+				var cell:TableCellElement = _textFlow.parentElement as TableCellElement;
+				
+				// set the cursor if around the edge of the table
+				if (cell) {
+					var leftEdge:int = 5;
+					var topEdge:int = 5;
+					var globalPoint:Point = new Point(event.stageX, event.stageY);
+					var cellContainer:CellContainer = event.currentTarget as CellContainer;
+					var point:Point;
+					
+					if (cellContainer) {
+						var cellContainerPoint:Point = cellContainer.localToGlobal(new Point);
+						point = globalPoint.subtract(cellContainerPoint);
+					}
+					if(useTableSelectionCursors)
+					{
+						// set cursor for row, table or column
+						if (cell.colIndex==0 && point.x<leftEdge && point.y>topEdge)
+						{
+							event.stopPropagation();
+							event.stopImmediatePropagation();
+							setMouseCursor(SelectTableRow);
+						}
+						else if (cell.rowIndex==0 && cell.colIndex==0 &&
+							point.x<leftEdge && point.y<topEdge)
+						{
+							event.stopPropagation();
+							event.stopImmediatePropagation();
+							setMouseCursor(SelectTable);
+						}
+						else if (cell.rowIndex==0 && point.x>leftEdge && point.y<topEdge)
+						{
+							event.stopPropagation();
+							event.stopImmediatePropagation();
+							setMouseCursor(SelectTableColumn);
+						}
+						else {
+							setMouseCursor(MouseCursor.IBEAM);
+						}
+						
+					}
+					else {
+						setMouseCursor(MouseCursor.IBEAM);
+					}
+				}
+				else {
+                	setMouseCursor(MouseCursor.IBEAM);
+				}
+			}
+            else {
+                setMouseCursor(MouseCursor.AUTO);
+			}
         }
 
         /** 
@@ -1873,6 +2619,9 @@
             }
             else if (event.keyCode == Keyboard.ESCAPE)
                 handleKeyEvent(event);
+			if(_textFlow.parentElement)
+				event.stopPropagation();
+			
         }
 
         /** 
@@ -2098,9 +2847,26 @@
         {
             var idx:int = marks.indexOf(mark);
             if (idx != -1)
-                marks.splice(idx,idx+1);
+                marks.splice(idx,1);
         }
-        
+
+		private var cellMarks:Array = [];
+		
+		/** @private */
+		tlf_internal function createCellMark():CellCoordinates
+		{
+			var mark:CellCoordinates = new CellCoordinates(-1,-1);
+			cellMarks.push(mark);
+			return mark;
+		}
+		/** @private */
+		tlf_internal function removeCellMark(mark:CellCoordinates):void
+		{
+			var idx:int = cellMarks.indexOf(mark);
+			if (idx != -1)
+				marks.splice(idx,1);
+		}
+
         /** 
          * @copy ISelectionManager#notifyInsertOrDelete()
          * 
@@ -2126,5 +2892,138 @@
                 }
             }
         }
+
+		/**
+		 * The ISelectionManager object used to for cell selections nested within the TextFlow managed by this ISelectionManager.
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */		 		 
+		public function get subManager():ISelectionManager

+		{
+			return _subManager;
+		}
+		public function set subManager(value:ISelectionManager):void

+		{
+			if(_subManager)
+				_subManager.selectRange(-1,-1);
+			_subManager = value;
+		}
+		/**
+		 * The ISelectionManager object used to manage the parent TextFlow of this ISelectionManager (i.e. for cell ISelectionManagers).
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */		 		 
+
+		public function get superManager():ISelectionManager

+		{
+			return _superManager;
+		}
+
+		public function set superManager(value:ISelectionManager):void

+		{
+			_superManager = value;
+		}
+
+		/** Anchor point of the current cell selection, as coordinates within the table. */
+		public function get anchorCellPosition():CellCoordinates

+		{
+			return _anchorCellPosition;
+		}
+		public function set anchorCellPosition(value:CellCoordinates):void

+		{
+			_anchorCellPosition = value;
+		}
+
+		/** Active end of the current cell selection, as coordinates within the table. */
+		public function get activeCellPosition():CellCoordinates

+		{
+			return _activeCellPosition;
+		}
+		public function set activeCellPosition(value:CellCoordinates):void

+		{
+			_activeCellPosition = value;
+		}
+		
+		public var selectTableCursorPoints:Vector.<Number> = new <Number>[1,3, 11,3, 11,0, 12,0, 16,4, 12,8, 11,8, 11,5, 1,5, 1,3];
+		public var selectTableCursorDrawCommands:Vector.<int> = new <int>[1,2,2,2,2,2,2,2,2,2];
+		
+		
+		/**
+		 * Create a select table cursor
+		 */
+		public function createSelectTableCursor():MouseCursorData {
+			var cursorData:Vector.<BitmapData> = new Vector.<BitmapData>();
+			var cursorShape:Shape = new Shape();
+			cursorShape.graphics.beginFill(0x0, 1);
+			cursorShape.graphics.lineStyle(0, 0xFFFFFF, 1, true);
+			cursorShape.graphics.drawPath( selectTableCursorDrawCommands, selectTableCursorPoints);
+			cursorShape.graphics.endFill();
+			var transformer:Matrix = new Matrix();
+			var cursorFrame:BitmapData = new BitmapData(32, 32, true, 0);
+			var angle:int = 8;
+			var rotation:Number = 0.785398163;
+			transformer.translate(-angle,-angle);
+			transformer.rotate(rotation);
+			transformer.translate(angle, angle);
+			cursorFrame.draw(cursorShape, transformer);
+			cursorData.push(cursorFrame);
+			var mouseCursorData:MouseCursorData = new MouseCursorData();
+			mouseCursorData.data = cursorData;
+			mouseCursorData.hotSpot = new Point(16, 10);
+			mouseCursorData.frameRate = 1;
+			return mouseCursorData;
+		}
+		
+		/**
+		 * Create a select row cursor
+		 */
+		public function createSelectTableRowCursor():MouseCursorData {
+			var cursorData:Vector.<BitmapData> = new Vector.<BitmapData>();
+			var cursorShape:Shape = new Shape();
+			cursorShape.graphics.beginFill(0x0, 1);
+			cursorShape.graphics.lineStyle(0, 0xFFFFFF, 1, true);
+			cursorShape.graphics.drawPath(selectTableCursorDrawCommands, selectTableCursorPoints);
+			cursorShape.graphics.endFill();
+			var transformer:Matrix = new Matrix();
+			var cursorFrame:BitmapData = new BitmapData(32, 32, true, 0);
+			cursorFrame.draw(cursorShape, transformer);
+			cursorData.push(cursorFrame);
+			var mouseCursorData:MouseCursorData = new MouseCursorData();
+			mouseCursorData.data = cursorData;
+			mouseCursorData.hotSpot = new Point(16, 4);
+			mouseCursorData.frameRate = 1;
+			return mouseCursorData;
+		}
+		
+		/**
+		 * Create a select table column cursor
+		 */
+		public function createSelectTableColumnCursor():MouseCursorData {
+			var cursorData:Vector.<BitmapData> = new Vector.<BitmapData>();
+			var cursorShape:Shape = new Shape();
+			cursorShape.graphics.beginFill(0x0, 1 );
+			cursorShape.graphics.lineStyle(0, 0xFFFFFF, 1, true );
+			cursorShape.graphics.drawPath(selectTableCursorDrawCommands, selectTableCursorPoints);
+			cursorShape.graphics.endFill();
+			var transformer:Matrix = new Matrix();
+			var cursorFrame:BitmapData = new BitmapData(32, 32, true, 0);
+			var angle:int = 16;
+			var rotation:Number = 0.785398163;
+			transformer.translate(-angle,-angle);
+			transformer.rotate(rotation * 2);
+			transformer.translate(angle, angle);
+			cursorFrame.draw(cursorShape, transformer);
+			cursorData.push(cursorFrame);
+			var mouseCursorData:MouseCursorData = new MouseCursorData();
+			mouseCursorData.data = cursorData;
+			mouseCursorData.hotSpot = new Point(28, 16);
+			mouseCursorData.frameRate = 1;
+			return mouseCursorData;
+		}
+
     }
 }
diff --git a/textLayout/src/flashx/textLayout/edit/SelectionState.as b/textLayout/src/flashx/textLayout/edit/SelectionState.as
index 4d500e4..379f023 100644
--- a/textLayout/src/flashx/textLayout/edit/SelectionState.as
+++ b/textLayout/src/flashx/textLayout/edit/SelectionState.as
@@ -27,6 +27,8 @@
 	use namespace tlf_internal;
 	
 	import flashx.textLayout.tlf_internal;
+	import flashx.textLayout.elements.CellRange;
+
 	use namespace tlf_internal;
 	/**
 	 * The SelectionState class represents a selection in a text flow.  
@@ -49,6 +51,8 @@
 		/** Format that are associated with the caret position & will be applied to inserted text */
 		private var _pointFormat:ITextLayoutFormat;
 		
+		private var _cellRange:CellRange;
+		
 		private var _selectionManagerOperationState:Boolean;
 
 		/** 
@@ -72,11 +76,12 @@
 		 * @playerversion AIR 1.5
  	 	 * @langversion 3.0
 		 */		
-		public function SelectionState(root:TextFlow,anchorPosition:int,activePosition:int,format:ITextLayoutFormat = null)
+		public function SelectionState(root:TextFlow,anchorPosition:int,activePosition:int,format:ITextLayoutFormat = null,cellRange:CellRange = null)
 		{
 			super(root, anchorPosition, activePosition);
 			if (format)
 				_pointFormat = format;
+			_cellRange = cellRange;
 		}
 		
 		/** 
@@ -126,5 +131,20 @@
 		/** @private */
 		tlf_internal function clone():SelectionState
 		{ return new SelectionState(textFlow,anchorPosition,activePosition,pointFormat); }
+
+		/** Range of table cells in selection (null if no cells selected)*/
+		public function get cellRange():CellRange

+		{
+			return _cellRange;
+		}
+
+		/**
+		 * @private
+		 */

+		public function set cellRange(value:CellRange):void

+		{
+			_cellRange = value;
+		}
+
 	}
 }
\ No newline at end of file
diff --git a/textLayout/src/flashx/textLayout/edit/SelectionType.as b/textLayout/src/flashx/textLayout/edit/SelectionType.as
new file mode 100644
index 0000000..7188bb8
--- /dev/null
+++ b/textLayout/src/flashx/textLayout/edit/SelectionType.as
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 flashx.textLayout.edit
+{
+	public class SelectionType
+	{
+		public static const TEXT:String = "text";
+		public static const CELLS:String = "cells";
+		public static const NONE:String = "none";
+		public function SelectionType()
+		{
+		}
+	}
+}
diff --git a/textLayout/src/flashx/textLayout/edit/TextFlowEdit.as b/textLayout/src/flashx/textLayout/edit/TextFlowEdit.as
index 347aa53..235cd08 100644
--- a/textLayout/src/flashx/textLayout/edit/TextFlowEdit.as
+++ b/textLayout/src/flashx/textLayout/edit/TextFlowEdit.as
@@ -1114,10 +1114,14 @@
 		/** if parent is a singleton element, deletes it, then repeats deletion of singletons up the parent chain.  Used after paragraph merge. */
 		tlf_internal static function removeEmptyParentChain(parent:FlowGroupElement):IMemento
 		{
+			if(parent is ParagraphElement)
+				ParagraphElement(parent).removeEmptyTerminator();
 			var mementoList:MementoList = new MementoList(parent.getTextFlow());
 			while(parent && (parent.numChildren == 0))
 			{
 				var grandParent:FlowGroupElement = parent.parent;
+				if(grandParent is ParagraphElement)
+					ParagraphElement(grandParent).removeEmptyTerminator();
 				if(grandParent)
 				{
 					var parentIdx:int = grandParent.getChildIndex(parent);
diff --git a/textLayout/src/flashx/textLayout/elements/BackgroundManager.as b/textLayout/src/flashx/textLayout/elements/BackgroundManager.as
index adee363..b29accf 100644
--- a/textLayout/src/flashx/textLayout/elements/BackgroundManager.as
+++ b/textLayout/src/flashx/textLayout/elements/BackgroundManager.as
@@ -32,6 +32,7 @@
 	import flashx.textLayout.compose.ParcelList;
 	import flashx.textLayout.compose.StandardFlowComposer;
 	import flashx.textLayout.compose.TextFlowLine;
+	import flashx.textLayout.compose.TextFlowTableBlock;
 	import flashx.textLayout.container.ContainerController;
 	import flashx.textLayout.container.TextContainerManager;
 	import flashx.textLayout.debug.assert;
@@ -45,7 +46,6 @@
 	
 	use namespace tlf_internal;
 	
-	[ExcludeClass]
 	/** @private Manages bounds calculation and rendering of backgroundColor character format. */
 	public class BackgroundManager
 	{
@@ -109,6 +109,34 @@
 			}
 		}
 		
+		public static function collectTableBlock(_textFlow:TextFlow,block:TextFlowTableBlock,controller:ContainerController):void
+		{
+			// add block rect for each cell in table block
+			
+			var bb:BackgroundManager;
+			var r:Rectangle;
+			var composer:IFlowComposer;
+
+			var cells:Vector.<TableCellElement> = block.getTableCells();
+			for each(var cell:TableCellElement in cells){
+				if(BackgroundManager.hasBorderOrBackground(cell))
+				{
+					if(!_textFlow.backgroundManager)
+						_textFlow.getBackgroundManager();
+					bb = _textFlow.backgroundManager;
+
+					bb.addBlockElement(cell);
+
+					var row:TableRowElement = cell.getRow();
+					r = new Rectangle(cell.x, cell.y + block.y, cell.width, row.composedHeight);
+					bb.addBlockRect(cell, r, controller);
+
+				}
+			}
+			block.y;
+			
+		}
+		
 		public static function collectBlock(_textFlow:TextFlow, elem:FlowGroupElement, _parcelList:ParcelList = null, tableComposeNotFromBeginning:Boolean = false, tableOutOfView:Boolean = false):void
 		{
 			var bb:BackgroundManager;
@@ -118,61 +146,8 @@
 
 			if(elem)
 			{
-				//The height of TableDataCellElement can only be identified after all the cells in the row are composed.
-				//So, pick it out of the common process 
-				if(elem is TableRowElement)
-				{
-					var tabRow:TableRowElement = elem as TableRowElement;
-					//for table cells
-					var cell:TableDataCellElement;
-					var cellParcel:Parcel;
-					for(var cIdx:Number = 0; cIdx < elem.numChildren; cIdx++)
-					{
-						cell = elem.getChildAt(cIdx) as TableDataCellElement;
-						if(BackgroundManager.hasBorderOrBackground(cell) || BackgroundManager.hasBorderOrBackground(elem))
-						{
-							//mark the paragraph that has border or background
-							if(!_textFlow.backgroundManager)
-								_textFlow.getBackgroundManager();
-							bb = _textFlow.backgroundManager;
-							
-							//BackgroundManager should not be null here
-							CONFIG::debug { assert(_textFlow.backgroundManager != null ,"BackgroundManager should not be null"); }
-							
-							bb.addBlockElement(cell);
-							
-							cellParcel = _parcelList.getParcelAt(cell.parcelIndex);
-							if(cellParcel)
-							{
-								r = new Rectangle(cell.x, cell.y, cell.width, tabRow.height);
-								bb.addBlockRect(cell, r, cellParcel.controller);
-							}
-						}
-					}
-					
-					//for table rows
-					/*if(BackgroundManager.hasBorderOrBackground(elem))
-					{
-						//mark the paragraph that has border or background
-						if(!_textFlow.backgroundManager)
-							_textFlow.getBackgroundManager();
-						bb = _textFlow.backgroundManager;
-						
-						//BackgroundManager should not be null here
-						CONFIG::debug { assert(_textFlow.backgroundManager != null ,"BackgroundManager should not be null"); }
-						
-						bb.addBlockElement(elem);
-						
-						var parentTable:TableElement = elem.parent as TableElement;
-						var rowParcel:Parcel = _parcelList.getParcelAt(tabRow.parcelIndex);
-						if(parentTable && rowParcel){
-							r = new Rectangle(parentTable.x + rowParcel.x, tabRow.y + rowParcel.y, parentTable.computedWidth, tabRow.height);
-							bb.addBlockRect(elem, r, rowParcel.controller);
-						}
-					}*/
-				}
-				//for the other elements
-				else if(BackgroundManager.hasBorderOrBackground(elem))
+
+				if(BackgroundManager.hasBorderOrBackground(elem))
 				{
 					//mark the paragraph that has border or background
 					if(!_textFlow.backgroundManager)
@@ -189,61 +164,9 @@
 					{
 						if(elem is TableElement)
 						{
+							// Do we need to do anything for table elements? Not sure...
 							var tab:TableElement = elem as TableElement;
-							var parcel:Parcel;
-							if(tab.numAcrossParcels == 0)
-							{
-								r = new Rectangle();
-								parcel = _parcelList.getParcelAt(tab.originParcelIndex);
-								if(parcel)
-								{
-									if(tableComposeNotFromBeginning)
-									{
-										r.x = parcel.x;
-										r.y = parcel.y;
-									}
-									else
-									{
-										r.x = tab.x;
-										r.y = tab.y;
-									}
-									r.width = tab.computedWidth;
-									r.height = tab.height;
-									bb.addBlockRect(elem, r, parcel.controller);
-								}
-							}else
-							{
-								for(var tIdx:Number = 0; tIdx <= tab.numAcrossParcels; tIdx++)
-								{
-									r = new Rectangle();
-									parcel = _parcelList.getParcelAt(tab.originParcelIndex + tIdx);
-									if(parcel)
-									{
-										if(tIdx == 0 && !tableComposeNotFromBeginning)
-										{
-											r.x = tab.x;
-											r.y = tab.y;
-											r.width = tab.computedWidth;
-											r.height = tab.heightArray[tIdx];
-											bb.addBlockRect(elem, r, parcel.controller, BackgroundManager.BOTTOM_EXCLUDED);
-										}else if (tIdx == tab.numAcrossParcels && !tableOutOfView)
-										{
-											r.x = parcel.x + tab.computedFormat.marginLeft;
-											r.y = parcel.y;
-											r.width = tab.computedWidth;
-											r.height = tab.totalRowDepth;
-											bb.addBlockRect(elem, r, parcel.controller, BackgroundManager.TOP_EXCLUDED);
-										}else
-										{
-											r.x = parcel.x + tab.computedFormat.marginLeft;
-											r.y = parcel.y;
-											r.width = tab.computedWidth;
-											r.height = tab.heightArray[tIdx];
-											bb.addBlockRect(elem, r, parcel.controller, BackgroundManager.TOP_AND_BOTTOM_EXCLUDED);
-										}
-									}
-								}
-							}
+
 						}
 						else //for elements like ParagraphElement, DivElement, ListItemElement, ListElement, TextFlow
 						{	
@@ -461,8 +384,9 @@
 					//draw background
 					if(style.backgroundColor != BackgroundColor.TRANSPARENT)
 					{
-						g.lineStyle(0, style.backgroundColor, style.backgroundAlpha, true);
-						g.beginFill(style.backgroundColor);
+						// The value 0 indicates hairline thickness; 
+						g.lineStyle(NaN, style.backgroundColor, style.backgroundAlpha, true);
+						g.beginFill(style.backgroundColor, style.backgroundAlpha);
 						g.drawRect(rec.x, rec.y, rec.width, rec.height);
 						g.endFill();
 					}
@@ -577,8 +501,9 @@
 							//draw background
 							if(style.backgroundColor != BackgroundColor.TRANSPARENT)
 							{
-								g.lineStyle(0, style.backgroundColor, style.backgroundAlpha, true);
-								g.beginFill(style.backgroundColor);
+								// The value 0 indicates hairline thickness; NaN removes line
+								g.lineStyle(NaN, style.backgroundColor, style.backgroundAlpha, true);
+								g.beginFill(style.backgroundColor, style.backgroundAlpha);
 								g.drawRect(rec.x, rec.y, rec.width, rec.height);
 								g.endFill();
 							}
@@ -618,7 +543,11 @@
 				//draw background for span	
 				for(var childIdx:int = 0; childIdx<controller.textLines.length; ++childIdx)
 				{
-					var tl:TextLine = controller.textLines[childIdx];
+					var line:* = controller.textLines[childIdx];
+					// skip TextFlowTableBlocks
+					if(!(line is TextLine))
+						continue;
+					var tl:TextLine = line;
 					var entry:Array = _lineDict[tl];
 		
 					if (entry)
diff --git a/textLayout/src/flashx/textLayout/elements/CellContainer.as b/textLayout/src/flashx/textLayout/elements/CellContainer.as
index 336da5a..256c495 100644
--- a/textLayout/src/flashx/textLayout/elements/CellContainer.as
+++ b/textLayout/src/flashx/textLayout/elements/CellContainer.as
@@ -20,13 +20,37 @@
 {
 	import flash.display.Sprite;
 	
-	public class CellContainer extends Sprite
+	//import mx.core.IIMESupport;
+	
+	public class CellContainer extends Sprite// implements IIMESupport
 	{
-		public var userData:Object=null;
-		
-		public function CellContainer()
+		private var _imeMode:String;
+		private var _enableIME:Boolean;
+		public var element:TableCellElement;
+
+		public function CellContainer(imeEnabled:Boolean = true)
 		{
-			super();
+			_enableIME = imeEnabled;
+		}
+		
+		public function get enableIME():Boolean
+		{
+			return false;
+		}
+		
+		public function set enableIME(value:Boolean):void
+		{
+			_enableIME = value;
+		}
+		
+		public function get imeMode():String
+		{
+			return _imeMode;
+		}
+		
+		public function set imeMode(value:String):void
+		{
+			_imeMode = value;
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/textLayout/src/flashx/textLayout/elements/CellCoordinates.as b/textLayout/src/flashx/textLayout/elements/CellCoordinates.as
new file mode 100644
index 0000000..d28a100
--- /dev/null
+++ b/textLayout/src/flashx/textLayout/elements/CellCoordinates.as
@@ -0,0 +1,89 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 flashx.textLayout.elements
+{
+	/**
+	 * Describes the location of table cell by row and column 
+	 **/
+	public class CellCoordinates
+	{
+		private var _column:int;
+		private var _row:int;
+		
+		/**
+		 * @constructor
+		 **/
+		public function CellCoordinates(row:int, column:int, table:TableElement = null)
+		{
+			_row = row;
+			_column = column;
+			this.table = table;
+		}
+
+		/**
+		 * The column the cell belongs to
+		 **/
+		public function get column():int

+			{return _column;}
+		
+		/**
+		 * @private
+		 **/
+		public function set column(value:int):void

+			{_column = value;}
+
+		/**
+		 * The row the cell belongs to
+		 **/
+		public function get row():int

+			{return _row;}
+		
+		/**
+		 * @private
+		 **/
+		public function set row(value:int):void

+			{_row = value;}
+		
+		/**
+		 * Checks if two coordiates are in the same location
+		 **/
+		public static function areEqual(coords1:CellCoordinates, coords2:CellCoordinates):Boolean
+		{
+			return coords1.row == coords2.row && coords1.column == coords2.column;
+		}
+		
+		/**
+		 * Returns true if the column and row are greater than -1
+		 **/
+		public function isValid():Boolean
+		{
+			return column > -1 && row > -1;
+		}
+		
+		/**
+		 * Creates a new CellCoordinates with the same row and column values
+		 **/
+		public function clone():CellCoordinates
+		{
+			return new CellCoordinates(row, column);
+		}
+
+		public var table:TableElement;
+	}
+}
\ No newline at end of file
diff --git a/textLayout/src/flashx/textLayout/elements/CellRange.as b/textLayout/src/flashx/textLayout/elements/CellRange.as
new file mode 100644
index 0000000..8e83003
--- /dev/null
+++ b/textLayout/src/flashx/textLayout/elements/CellRange.as
@@ -0,0 +1,150 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 flashx.textLayout.elements
+{
+	import flashx.textLayout.tlf_internal;
+	
+	use namespace tlf_internal;
+	
+	/**
+	 * A read only class that describes a range of contiguous table cells. Such a range occurs when you select a
+	 * section of table cells. The range consists of the anchor point of the selection, <code>anchorPosition</code>,
+	 * and the point that is to be modified by actions, <code>activePosition</code>.  As block selections are 
+	 * modified and extended <code>anchorPosition</code> remains fixed and <code>activePosition</code> is modified.  
+	 * The anchor position may be placed in the text before or after the active position.
+	 * @playerversion Flash 10
+	 * @playerversion AIR 1.5
+	 * @langversion 3.0
+	 *
+	 * @see flashx.textLayout.elements.TextFlow TextFlow
+	 * @see flashx.textLayout.edit.SelectionState SelectionState
+	 */
+	public class CellRange
+	{
+		
+		private var _table:TableElement
+		
+		// current range of selection
+		private var _anchorCoords:CellCoordinates;
+		private var _activeCoords:CellCoordinates;
+		
+		/**
+		 * Limits the row and column values to 0 or the number of rows or column. 
+		 **/
+		private function clampToRange(coords:CellCoordinates):CellCoordinates
+		{
+			if(coords == null)
+				return null;
+			if (coords.row < 0)
+				coords.row = 0;
+			if (coords.column < 0)
+				coords.column = 0;
+			if(_table == null)
+				return coords;
+			
+			if (coords.row >= _table.numRows)
+				coords.row = _table.numRows-1;
+			if (coords.column >= _table.numColumns)
+				coords.column = _table.numColumns-1;
+			return coords;
+		}
+
+		public function CellRange(table:TableElement, anchorCoords:CellCoordinates, activeCoords:CellCoordinates)
+		{
+			_table = table;
+			_anchorCoords = clampToRange(anchorCoords);
+			_activeCoords = clampToRange(activeCoords);
+			
+		}
+		
+		/** 
+		 * Update the range with new anchor or active position values.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *  @param newAnchorPosition	the anchor index of the selection.
+		 *  @param newActivePosition	the active index of the selection.
+		 *  @return true if selection is changed.
+		 */
+		public function updateRange(newAnchorCoordinates:CellCoordinates, newActiveCoordinates:CellCoordinates):Boolean
+		{
+			clampToRange(newAnchorCoordinates);
+			clampToRange(newActiveCoordinates);
+			
+			if (!CellCoordinates.areEqual(_anchorCoords, newAnchorCoordinates) || !CellCoordinates.areEqual(_activeCoords, newActiveCoordinates))
+			{
+				_anchorCoords = newAnchorCoordinates;
+				_activeCoords = newActiveCoordinates;
+				return true;
+			}
+			return false;
+		}
+
+		/** The TableElement of the selection.
+		 */
+		public function get table():TableElement

+		{
+			return _table;
+		}
+
+		/**
+		 * @private
+		 */

+		public function set table(value:TableElement):void

+		{
+			_table = value;
+		}
+
+		/** 
+		 * Anchor point of the current selection, as a CellCoordinates in the TableElement. 
+		 */
+		public function get anchorCoordinates():CellCoordinates

+		{
+			return _anchorCoords;
+		}
+
+		/**
+		 * @private
+		 */

+		public function set anchorCoordinates(value:CellCoordinates):void

+		{
+			_anchorCoords = value;
+		}
+
+		/** 
+		 * Active end of the current selection, as a CellCoordinates in the TableElement. 
+		 */
+		public function get activeCoordinates():CellCoordinates

+		{
+			return _activeCoords;
+		}
+
+		/**
+		 * @private
+		 */

+		public function set activeCoordinates(value:CellCoordinates):void

+		{
+			_activeCoords = value;
+		}
+
+
+	}
+}
\ No newline at end of file
diff --git a/textLayout/src/flashx/textLayout/elements/FlowElement.as b/textLayout/src/flashx/textLayout/elements/FlowElement.as
index 6ef5053..12ba288 100644
--- a/textLayout/src/flashx/textLayout/elements/FlowElement.as
+++ b/textLayout/src/flashx/textLayout/elements/FlowElement.as
@@ -805,6 +805,14 @@
 		{ setStyle(styleProp,undefined); }
 		
 		/**
+		 * Called when an element is removed. Used for container elements to run any clean up code. 
+		 **/
+		tlf_internal function removed():void
+		{
+			// override in sub classes
+		}
+		
+		/**
 		 * Called whenever the model is modified.  Updates the TextFlow and notifies the selection manager - if it is set.
 		 * This method has to be called while the element is still in the flow
 		 * @param changeType - type of change
@@ -1024,6 +1032,24 @@
 		}
 		
 		
+		public function isInTable():Boolean
+		{
+			var tf:TextFlow = getTextFlow();
+			return tf && tf.parentElement && tf.parentElement is TableCellElement;
+		}
+		
+		public function getParentCellElement():TableCellElement
+		{
+			var tf:TextFlow = getTextFlow();
+			
+			if(!tf)
+				return null;
+			if(tf.parentElement && tf.parentElement is TableCellElement)
+				return tf.parentElement as TableCellElement;
+			return null;
+		}
+
+		
 		/** 
 		 * Returns the FlowElement object that contains this FlowElement object, if this element is contained within 
 		 * an element of a particular type. 
diff --git a/textLayout/src/flashx/textLayout/elements/FlowGroupElement.as b/textLayout/src/flashx/textLayout/elements/FlowGroupElement.as
index b02a22d..62567dd 100644
--- a/textLayout/src/flashx/textLayout/elements/FlowGroupElement.as
+++ b/textLayout/src/flashx/textLayout/elements/FlowGroupElement.as
@@ -582,7 +582,7 @@
 		}
 		
 		/** @private */
-		tlf_internal function createContentAsGroup():GroupElement
+		tlf_internal function createContentAsGroup(pos:int=0):GroupElement
 		{
 			CONFIG::debug { assert(false,"invalid call to createContentAsGroup"); }
 			return null;
@@ -626,7 +626,7 @@
 		 */
 		tlf_internal function canOwnFlowElement(elem:FlowElement):Boolean
 		{
-			return !(elem is TextFlow) && !(elem is FlowLeafElement) && !(elem is SubParagraphGroupElementBase) && !(elem is ListItemElement);
+			return !(elem is TextFlow) && !(elem is FlowLeafElement) && !(elem is SubParagraphGroupElementBase) && !(elem is ListItemElement) && !(elem is TableElement);
 		}
 		
 		/** @private */	
@@ -687,6 +687,7 @@
 				{
 					child = this.getChildAt(beginChildIndex);
 					this.modelChanged(ModelChange.ELEMENT_REMOVAL, child, child.parentRelativeStart, child.textLength);
+					child.removed();
 					len += child.textLength;
 					
 					child.setParentAndRelativeStart(null,0);
@@ -770,8 +771,10 @@
 								relStartIdx = beginChildIndex == _numChildren ? textLength : getChildAt(beginChildIndex).parentRelativeStart;
 							}
 						}
-						if (!canOwnFlowElement(newChild))
-							throw ArgumentError(GlobalSettings.resourceStringFunction("invalidChildType"));
+						
+						if (!canOwnFlowElement(newChild)) {
+							throw ArgumentError(GlobalSettings.resourceStringFunction("invalidChildType") + ". " + defaultTypeName + " cannot own " + newChild.defaultTypeName);
+						}
 						
 						// manage as an array or a single child
 						if (childrenToAdd == 0)
@@ -988,6 +991,7 @@
 				parent.replaceChildren(myidx+1,myidx+1,newSibling);
 			}
 
+			newSibling.normalizeRange(0,newSibling.textLength);
 			return newSibling;
 		}
 
diff --git a/textLayout/src/flashx/textLayout/elements/FlowLeafElement.as b/textLayout/src/flashx/textLayout/elements/FlowLeafElement.as
index 22bfe40..894a362 100644
--- a/textLayout/src/flashx/textLayout/elements/FlowLeafElement.as
+++ b/textLayout/src/flashx/textLayout/elements/FlowLeafElement.as
@@ -337,6 +337,8 @@
 		{
 			if (!_blockElement)
 				createContentElement();
+			if(!_blockElement)
+				return null;
 			var ef:ElementFormat = _blockElement.elementFormat;
 			if (!ef)
 				return null;
diff --git a/textLayout/src/flashx/textLayout/elements/ParagraphElement.as b/textLayout/src/flashx/textLayout/elements/ParagraphElement.as
index f4579fc..51b8283 100644
--- a/textLayout/src/flashx/textLayout/elements/ParagraphElement.as
+++ b/textLayout/src/flashx/textLayout/elements/ParagraphElement.as
@@ -31,9 +31,9 @@
 	import flash.text.engine.TextLine;
 	import flash.text.engine.TextLineValidity;
 	import flash.text.engine.TextRotation;
+	import flash.utils.Dictionary;
 	import flash.utils.getQualifiedClassName;
 	
-	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.compose.TextFlowLine;
 	import flashx.textLayout.container.ContainerController;
 	import flashx.textLayout.debug.Debugging;
@@ -50,6 +50,7 @@
 	import flashx.textLayout.formats.TextJustify;
 	import flashx.textLayout.formats.TextLayoutFormat;
 	import flashx.textLayout.property.Property;
+	import flashx.textLayout.tlf_internal;
 	import flashx.textLayout.utils.CharacterUtil;
 	import flashx.textLayout.utils.LocaleUtil;
 	
@@ -79,7 +80,8 @@
 	 
 	public final class ParagraphElement extends ParagraphFormattedElement
 	{
-		private var _textBlock:TextBlock;
+		//private var _textBlock:TextBlock;
+		private var _textBlockChildren:Dictionary;
 		private var _terminatorSpan:SpanElement;
 		
 		private var _interactiveChildrenCount:int;
@@ -95,6 +97,7 @@
 			super();
 			_terminatorSpan = null;
 			_interactiveChildrenCount = 0 ;
+			_textBlockChildren = new Dictionary();
 		}
 		tlf_internal function get interactiveChildrenCount():int
 		{
@@ -106,26 +109,83 @@
 		{
 			CONFIG::debug { assert(_textBlock == null,"createTextBlock called when there is already a textblock"); }
 			computedFormat;	// recreate the format BEFORE the _textBlock is created
-			_textBlock = new TextBlock();
+			var tbs:Vector.<TextBlock> = getTextBlocks();
+			//tbs.length = 0;
+			var tableCount:int = 0;
+			if(tbs.length == 0 && !(getChildAt(0) is TableElement) )
+				tbs.push(new TextBlock());
+			//getTextBlocks()[0] = new TextBlock();
 			CONFIG::debug { Debugging.traceFTECall(_textBlock,null,"new TextBlock()"); }
 			for (var i:int = 0; i < numChildren; i++)
 			{
 				var child:FlowElement = getChildAt(i);
+				if(child is TableElement)
+					tableCount++;
+//					tbs.push(new TextBlock());
+				else
+				{
+					//child.releaseContentElement();
+					//child.createContentElement();
+				}
+			}
+			while(tableCount >= tbs.length)
+				tbs.push(new TextBlock());
+			
+			for (i = 0; i < numChildren; i++)
+			{
+				child = getChildAt(i);
 				child.createContentElement();
 			}
-			updateTextBlock();
+			tbs.length = tableCount + 1;
+			var tb:TextBlock;
+			for each(tb in tbs){
+				updateTextBlock(tb);
+			}
 		}
-		
-		/** @private */
-		
-		tlf_internal function releaseTextBlock():void
+		private function updateTextBlockDict():void
 		{
-			if (!_textBlock)
-				return;
-				
-			if (_textBlock.firstLine)	// A TextBlock may have no firstLine if it has previously been released.
+			var tbs:Vector.<TextBlock> = getTextBlocks();
+			if(tbs.length == 0)
+				return;//nothing to do
+			var tbIdx:int = 0;
+			var tb:TextBlock = tbs[tbIdx];
+			var items:Array = [];
+			var child:FlowElement;
+			for (var i:int = 0; i < numChildren; i++)
 			{
-				for (var textLineTest:TextLine = _textBlock.firstLine; textLineTest != null; textLineTest = textLineTest.nextLine)
+				child = getChildAt(i);
+				if(child is TableElement)
+				{
+					_textBlockChildren[tb] = items;
+					tb = tbs[++tbIdx];
+					items = [];
+					continue;
+				}
+				items.push(child);
+			}
+			_textBlockChildren[tb] = items;
+		}
+		private function removeTextBlock(tb:TextBlock):void
+		{
+			var tbs:Vector.<TextBlock> = getTextBlocks();
+			if(tbs)
+			{
+				var idx:int = getTextBlocks().indexOf(tb);
+				if(idx > -1)
+				{
+					tbs.splice(idx,1);
+					delete _textBlockChildren[tb];
+				}
+			}
+		}
+		private function releaseTextBlockInternal(tb:TextBlock):void
+		{
+			if (!tb)
+				return;
+			
+			if (tb.firstLine)	// A TextBlock may have no firstLine if it has previously been released.
+			{
+				for (var textLineTest:TextLine = tb.firstLine; textLineTest != null; textLineTest = textLineTest.nextLine)
 				{	
 					if(textLineTest.numChildren != 0)
 					{	
@@ -138,55 +198,171 @@
 					}
 				}
 				
-				CONFIG::debug { Debugging.traceFTECall(null,_textBlock,"releaseLines",_textBlock.firstLine, _textBlock.lastLine); }				
-				_textBlock.releaseLines(_textBlock.firstLine, _textBlock.lastLine);	
+				CONFIG::debug { Debugging.traceFTECall(null,tb,"releaseLines",tb.firstLine, tb.lastLine); }				
+				tb.releaseLines(tb.firstLine, tb.lastLine);	
 			}	
-
-			_textBlock.content = null;
-			for (var i:int = 0; i < numChildren; i++)
+			var items:Array = _textBlockChildren[tb];
+			var len:int = items.length;
+			for (var i:int = 0; i < len; i++)
 			{
-				var child:FlowElement = getChildAt(i);
+				var child:FlowElement = items[i];
 				child.releaseContentElement();
 			}
-			_textBlock = null;
+			items.length = 0;
+			tb.content = null;
+			removeTextBlock(tb);
+		}
+		/** @private */
+		tlf_internal function releaseTextBlock(tb:TextBlock=null):void
+		{
+			updateTextBlockDict();
+			if(tb)
+			{
+				releaseTextBlockInternal(tb);
+				return;
+			}
+			var tbs:Vector.<TextBlock> = getTextBlocks();
+			for each(var textBlock:TextBlock in tbs)
+			{
+				releaseTextBlockInternal(textBlock);
+			}
+			//_textBlock = null;
 			if (_computedFormat)
 				_computedFormat = null;
 		}
-		
+		private var _textBlocks:Vector.<TextBlock>;
+		tlf_internal function getTextBlocks():Vector.<TextBlock>
+		{
+			if(_textBlocks == null)
+				_textBlocks = new Vector.<TextBlock>();
+			return _textBlocks;
+		}
 		/** TextBlock where the text of the paragraph is kept. @private */
 		tlf_internal function getTextBlock():TextBlock
-		{ 
-			if (!_textBlock)
+		{
+			if (!getTextBlocks().length)
 				createTextBlock();
-			return _textBlock; 
+			
+			return getTextBlocks()[0]; 
+		}
+		/** Last TextBlock where the text of the paragraph is kept. @private */
+		tlf_internal function getLastTextBlock():TextBlock
+		{
+			var tbs:Vector.<TextBlock> = getTextBlocks();
+			if(!tbs.length)
+				createTextBlock();
+			
+			return tbs[tbs.length-1];
+		}
+
+		/** Get TextBlock at specified position. @private */
+		tlf_internal function getTextBlockAtPosition(pos:int):TextBlock
+		{
+			var curPos:int = 0;
+			var posShift:int = 0;
+			var tables:Vector.<TableElement> = getTables();
+			if(!tables.length)
+				return getTextBlock();
+			
+			for each(var table:TableElement in tables)
+			{
+				if(table.getElementRelativeStart(this) < pos)
+					posShift++;
+			}
+			var tbs:Vector.<TextBlock> = getTextBlocks();
+			for each(var tb:TextBlock in tbs)
+			{
+				if(tb.content == null)
+					return tb;
+				curPos += tb.content.rawText.length;
+				if(curPos + posShift > pos)
+				{
+					if(getTextBlockStart(tb) > pos)
+						return null;
+					return tb;
+				}
+			}
+			return null;
 		}
 		
+		tlf_internal function getTextBlockAbsoluteStart(tb:TextBlock):int
+		{
+			var start:int = getTextBlockStart(tb);
+			if(start < 0)
+				start = 0;
+			return getAbsoluteStart() + start;
+		}
+		tlf_internal function getTextBlockStart(tb:TextBlock):int
+		{
+			var i:int;
+			var curPos:int = 0;
+			var tbs:Vector.<TextBlock> = getTextBlocks();
+			if(tbs.length == 0)
+				return -1;
+			var tables:Vector.<TableElement> = getTables();
+			for each(var curTB:TextBlock in tbs)
+			{
+				for each(var table:TableElement in tables)
+				{
+					if(table.getElementRelativeStart(this) <= curPos)
+					{
+						curPos++;
+						tables.splice(tables.indexOf(table),1);
+					}
+				}
+				if(tb == curTB)
+					return curPos;
+				if(tb.content)
+					curPos += curTB.content.rawText.length;
+			}
+			
+			return -1;
+		}
+		
+		private function getTables():Vector.<TableElement>
+		{
+			var tables:Vector.<TableElement> = new Vector.<TableElement>();
+			for (var i:int = 0; i < numChildren; i++)
+			{
+				var child:FlowElement = getChildAt(i);
+				if(child is TableElement)
+					tables.push(child as TableElement);
+			}
+			return tables;
+		}
+
 		/** TextBlock where the text of the paragraph is kept, or null if we currently don't have one. @private */
 		tlf_internal function peekTextBlock():TextBlock
 		{ 
-			return _textBlock; 
+			return getTextBlocks().length == 0 ? null : getTextBlocks()[0];
 		}
 		
 		/** @private */
 		tlf_internal function releaseLineCreationData():void
 		{
 			CONFIG::debug { assert(Configuration.playerEnablesArgoFeatures,"bad call to releaseLineCreationData"); }
-			if (_textBlock)
-				_textBlock["releaseLineCreationData"]();
+			var tbs:Vector.<TextBlock> = getTextBlocks();
+			for each(var tb:TextBlock in tbs)
+			{
+				tb["releaseLineCreationData"]();
+			}
 		}
 		
 		/** @private */
-		tlf_internal override function createContentAsGroup():GroupElement
-		{ 			
-			var group:GroupElement = _textBlock.content as GroupElement;
+		tlf_internal override function createContentAsGroup(pos:int=0):GroupElement
+		{
+			var tb:TextBlock = getTextBlockAtPosition(pos);
+			if(!tb)
+				tb = getTextBlockAtPosition(pos-1);
+			var group:GroupElement = tb.content as GroupElement;
 			if (!group)
 			{
-				var originalContent:ContentElement = _textBlock.content;
+				var originalContent:ContentElement = tb.content;
 				
 				group = new GroupElement();
 				CONFIG::debug { Debugging.traceFTECall(group,null,"new GroupElement()"); }
-				_textBlock.content = group;
-				CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",group); }
+				tb.content = group;
+				CONFIG::debug { Debugging.traceFTEAssign(tb,"content",group); }
 
 				if (originalContent)
 				{
@@ -199,7 +375,7 @@
 				}
 				
 				// Now we've got to force damage the entire paragraph, because we restructured it in FTE.
-				if (_textBlock.firstLine && textLength)
+				if (tb.firstLine && textLength)
 				{
 					var textFlow:TextFlow = getTextFlow();
 					if (textFlow)
@@ -212,7 +388,15 @@
  		/** @private */
 		tlf_internal override function removeBlockElement(child:FlowElement, block:ContentElement):void
 		{
-			if (numChildren == 1)
+			var tb:TextBlock = getTextBlockAtPosition(child.getElementRelativeStart(this));
+			if(!tb)
+				tb = getTextBlock();
+			
+			if(tb.content == null)
+				return;
+			var relativeStart:int = child.getElementRelativeStart(this);
+
+			if (getChildrenInTextBlock(relativeStart).length < 2)
 			{
 				if (block is GroupElement)
 				{
@@ -221,18 +405,20 @@
 					CONFIG::debug { assert(_textBlock.content is GroupElement,"removeBlockElement: bad content"); }
 					CONFIG::debug { assert(GroupElement(_textBlock.content).elementCount == 1,"removeBlockElement: bad element count"); }
 					CONFIG::debug { assert(GroupElement(_textBlock.content).getElementAt(0) == block,"removeBlockElement: bad group content"); }
-					GroupElement(_textBlock.content).replaceElements(0,1,null);
+					GroupElement(tb.content).replaceElements(0,1,null);
 					CONFIG::debug { Debugging.traceFTECall(null,_textBlock.content,"replaceElements",0,1,null); }
 				}
-				_textBlock.content = null;
+				tb.content = null;
 				CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",null); }
 			}
-			else
+			else if(block.groupElement)
 			{
-				var idx:int = this.getChildIndex(child);
-				var group:GroupElement = GroupElement(_textBlock.content);
+				var idx:int = getChildIndexInBlock(child);
+				var group:GroupElement = GroupElement(tb.content);
 				CONFIG::debug { assert(group.elementCount == numChildren,"Mismatched group and elementCount"); }
 				group.replaceElements(idx,idx+1,null);
+				if(group.elementCount == 0)
+					return;
 				CONFIG::debug { Debugging.traceFTECall(null,group,"replaceElements",idx,idx+1,null); }
 				if (numChildren == 2)	// its going to be one so ungroup
 				{
@@ -243,18 +429,22 @@
 					{
 						group.replaceElements(0,1,null);
 						CONFIG::debug { Debugging.traceFTECall(null,group,"replaceElements",0,1,null); }
-						_textBlock.content = elem;
-						CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",elem); }
+						tb.content = elem;
+						CONFIG::debug { Debugging.traceFTEAssign(tb,"content",elem); }
 					}
 				}
 			}
+			else {
+				//trace("1");
+				//tb.content = null;
+			}
 		}
 		
 		
 		/** @private */
 		tlf_internal override function hasBlockElement():Boolean
 		{
-			return _textBlock != null;
+			return getTextBlocks().length > 0;
 		}
 		
 		/** @private */
@@ -264,9 +454,48 @@
 		}
 		
 		/** @private */
+		private function getChildrenInTextBlock(pos:int):Array
+		{
+			var retVal:Array = [];
+			if(numChildren == 0)
+				return retVal;
+			if(numChildren == 1)
+			{
+				retVal.push(getChildAt(0));
+				return retVal
+			}
+			var chldrn:Array = mxmlChildren.slice();
+			for(var i:int = 0; i<chldrn.length;i++)
+			{
+				if(chldrn[i] is TableElement)
+				{
+					if(chldrn[i].parentRelativeStart < pos)
+					{
+						retVal.length = 0;
+						continue;
+					}
+					if(chldrn[i].parentRelativeStart >= pos)
+						break;
+				}
+				retVal.push(chldrn[i]);		
+			}
+			return retVal;
+		}
+		
+		/** @private */
 		tlf_internal override function insertBlockElement(child:FlowElement, block:ContentElement):void
 		{
-			if (_textBlock == null)
+			var relativeStart:int = child.getElementRelativeStart(this);
+			var tb:TextBlock = getTextBlockAtPosition(relativeStart);
+			if(!tb)
+				tb = getTextBlockAtPosition(relativeStart-1);
+			
+			if(!tb)
+			{
+				child.releaseContentElement();
+				return;
+			}
+			if (getTextBlocks().length == 0)
 			{
 				child.releaseContentElement();
 				createTextBlock();	// does the whole tree
@@ -274,7 +503,7 @@
 			}
 			var gc:Vector.<ContentElement>;	// scratch var
 			var group:GroupElement;			// scratch
-			if (numChildren == 1)
+			if (getChildrenInTextBlock(relativeStart).length < 2)
 			{
 				if (block is GroupElement)
 				{
@@ -285,19 +514,23 @@
 					CONFIG::debug { Debugging.traceFTECall(null,gc,"push",block); }
 					group = new GroupElement(gc);
 					CONFIG::debug { Debugging.traceFTECall(group,null,"new GroupElement",gc); }
-					_textBlock.content = group;
+					tb.content = group;
 					CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",group); }
 				}
 				else
 				{
-					_textBlock.content = block;
+					if(block.groupElement)
+					{
+						block.groupElement.elementCount;
+					}
+					tb.content = block;
 					CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",block);  }
 				}
 			}
 			else
 			{
-				group = createContentAsGroup();
-				var idx:int = this.getChildIndex(child);
+				group = createContentAsGroup(relativeStart);
+				var idx:int = getChildIndexInBlock(child);
 				gc = new Vector.<ContentElement>();
 				CONFIG::debug { Debugging.traceFTECall(gc,null,"new Vector.<ContentElement>") }
 				gc.push(block);
@@ -307,6 +540,21 @@
 			}
 		}
 		
+		private function getChildIndexInBlock(elem:FlowElement):int
+		{
+			var relIdx:int = 0;
+			for (var i:int = 0; i < numChildren; i++)
+			{
+				var child:FlowElement = getChildAt(i);
+				if(child == elem)
+					return relIdx;
+				relIdx++;
+				if(child is TableElement)
+					relIdx = 0;
+			}
+			return -1;
+		}
+		
 		/** @private */
 		override protected function get abstract():Boolean
 		{ return false;	}	
@@ -315,24 +563,63 @@
 		tlf_internal override function get defaultTypeName():String
 		{ return "p"; }
 
+		tlf_internal function removeEmptyTerminator():void
+		{
+			if(numChildren == 1 && _terminatorSpan && _terminatorSpan.textLength == 1)
+			{
+				_terminatorSpan.removeParaTerminator();
+				super.replaceChildren(0, 1);
+				this._terminatorSpan = null;
+			}
+		}
 		/** @private */
 		public override function replaceChildren(beginChildIndex:int,endChildIndex:int,...rest):void
 		{
 			var applyParams:Array;
-			
-			// makes a measurable difference - rest.length zero and one are the common cases
-			if (rest.length == 1)
-				applyParams = [beginChildIndex, endChildIndex, rest[0]];
-			else
-			{
-				applyParams = [beginChildIndex, endChildIndex];
-				if (rest.length != 0)
-					applyParams = applyParams.concat.apply(applyParams, rest);
-			}
 
-			super.replaceChildren.apply(this, applyParams);
+			do{
+				if(_terminatorSpan)
+				{
+					var termIdx:int = getChildIndex(_terminatorSpan);
+					if(termIdx !=0 && _terminatorSpan.textLength == 1)
+					{
+						super.replaceChildren(termIdx, termIdx+1);
+						_terminatorSpan = null;
+						if(beginChildIndex >= termIdx)
+						{
+							beginChildIndex--;
+							if(rest.length == 0) // delete of terminator was already done.
+								break;
+						}
+						if(endChildIndex >= termIdx && beginChildIndex != endChildIndex)
+							endChildIndex--;
+					}
+				}
+				
+				// makes a measurable difference - rest.length zero and one are the common cases
+				if (rest.length == 1)
+					applyParams = [beginChildIndex, endChildIndex, rest[0]];
+				else
+				{
+					applyParams = [beginChildIndex, endChildIndex];
+					if (rest.length != 0)
+						applyParams = applyParams.concat.apply(applyParams, rest);
+				}
+				
+				super.replaceChildren.apply(this, applyParams);
+				
+			}while(false);
 			
 			ensureTerminatorAfterReplace();
+			// ensure correct text blocks
+			createTextBlock();
+		}
+		
+		public override function splitAtPosition(relativePosition:int):FlowElement
+		{
+			// need to handle multiple TextBlocks
+			// maybe not. It might be handled in replaceChildren().
+			return super.splitAtPosition(relativePosition);
 		}
 		/** @private */
 		tlf_internal function ensureTerminatorAfterReplace():void
@@ -340,27 +627,43 @@
 			var newLastLeaf:FlowLeafElement = getLastLeaf();
 			if (_terminatorSpan != newLastLeaf)
 			{
-				if (_terminatorSpan)
+				if (newLastLeaf && _terminatorSpan)
 				{
 					_terminatorSpan.removeParaTerminator();
+					if(_terminatorSpan.textLength == 0)
+					{
+						var termIdx:int = getChildIndex(_terminatorSpan);
+						super.replaceChildren(termIdx, termIdx+1);
+					}
 					this._terminatorSpan = null;
 				}
 				
-				if (newLastLeaf)
+				if (newLastLeaf is SpanElement)
 				{
-					if (newLastLeaf is SpanElement)
-					{
-						(newLastLeaf as SpanElement).addParaTerminator();
-						this._terminatorSpan = newLastLeaf as SpanElement;
-					}
-					else
-					{
-						var s:SpanElement = new SpanElement();
-						super.replaceChildren(numChildren,numChildren,s);
-						s.format = newLastLeaf.format;
-						s.addParaTerminator();
-						this._terminatorSpan = s;
-					}
+					(newLastLeaf as SpanElement).addParaTerminator();
+					this._terminatorSpan = newLastLeaf as SpanElement;
+				}
+				else
+				{
+					var s:SpanElement = new SpanElement();
+					super.replaceChildren(numChildren,numChildren,s);
+					s.format = newLastLeaf ? newLastLeaf.format : _terminatorSpan.format;
+					s.addParaTerminator();
+					this._terminatorSpan = s;
+				}
+			}
+			//merge terminator span to previous if possible
+			if(_terminatorSpan && _terminatorSpan.textLength == 1)
+			{
+				var prev:FlowLeafElement = _terminatorSpan.getPreviousLeaf(this);
+				if(prev && prev is SpanElement)
+				{
+					_terminatorSpan.removeParaTerminator();
+					termIdx = getChildIndex(_terminatorSpan);
+					super.replaceChildren(termIdx, termIdx+1);
+					s = prev as SpanElement;
+					s.addParaTerminator();
+					this._terminatorSpan = s;
 				}
 			}
 		}
@@ -387,7 +690,7 @@
 						child.bindableElement = true;
 					
 					// Note: calling super.replaceChildren because we don't want to transfer para terminator each time
-					super.replaceChildren(numChildren, numChildren, child as FlowElement); 
+					super.replaceChildren(numChildren, numChildren, child as FlowElement);
 				}
 				else if (child is String)
 				{
@@ -404,6 +707,9 @@
 			
 			// Now ensure para terminator
 			ensureTerminatorAfterReplace();
+			
+			// recreate text blocks to handle possible TableElement changes
+			createTextBlock();
 		}
 		
 		/** @private
@@ -411,17 +717,26 @@
 		public override function getText(relativeStart:int=0, relativeEnd:int=-1, paragraphSeparator:String="\n"):String
 		{
 			// Optimization for getting text of the entire paragraph
-			if (relativeStart == 0 && (relativeEnd == -1 || relativeEnd >= textLength-1) && _textBlock)
+			if (relativeStart == 0 && (relativeEnd == -1 || relativeEnd >= textLength-1) && getTextBlocks().length)
 			{
-				if (_textBlock.content && _textBlock.content.rawText)
+				var tb:TextBlock;
+				var tbs:Vector.<TextBlock> = getTextBlocks();
+				var text:String = "";
+				for each(tb in tbs)
 				{
-					var text:String = _textBlock.content.rawText;
-					return text.substring(0, text.length - 1);
+					text = text + getTextInBlock(tb);
 				}
-				return "";		// content is null
+				if(tb.content && tb.content.rawText)
+					return text.substring(0, text.length - 1);
+				return text;
 			}
 			return super.getText(relativeStart, relativeEnd, paragraphSeparator);
 		}
+		private function getTextInBlock(tb:TextBlock):String{
+			if(!tb.content || !tb.content.rawText)
+				return "";
+			return tb.content.rawText;
+		}
 		
 		/** Returns the paragraph that follows this one, or null if there are no more paragraphs. 
 		 *
@@ -478,39 +793,46 @@
 		 
 		public function findPreviousAtomBoundary(relativePosition:int):int
 		{
+			var tb:TextBlock = getTextBlockAtPosition(relativePosition);
+			var tbStart:int = getTextBlockStart(tb);
+			var textBlockPos:int = relativePosition - tbStart;
 			if (ContainerController.tlf_internal::usesDiscretionaryHyphens)
 			{
-				var textBlock:TextBlock = getTextBlock();
-				var tl:TextLine = textBlock.getTextLineAtCharIndex(relativePosition);
-				var currentAtomIndex:int = tl.getAtomIndexAtCharIndex(relativePosition);
+				var tl:TextLine = tb.getTextLineAtCharIndex(textBlockPos);
+				var currentAtomIndex:int = tl.getAtomIndexAtCharIndex(textBlockPos);
                 //trace("relpos", relativePosition, "atomIndex", currentAtomIndex);
                 var isRTL:Boolean = tl.getAtomBidiLevel(currentAtomIndex) == 1;
                 if (isRTL)
                 {
-                   var foo:int = getTextBlock().findPreviousAtomBoundary(relativePosition);
+                   var foo:int = tb.findPreviousAtomBoundary(textBlockPos);
                    if (currentAtomIndex == 0)
                    {
                        // when cursor is left of all characters (end of line)
                        // atomIndex is 0, so compensate
                        if (tl.atomCount > 0)
                        {
-                           while (--relativePosition)
+                           while (--textBlockPos)
                            {
-                               if (tl.getAtomIndexAtCharIndex(relativePosition) != currentAtomIndex)
+							   --relativePosition;
+                               if (tl.getAtomIndexAtCharIndex(textBlockPos) != currentAtomIndex)
                                    break;
                            }
                        }
                    }
                    else
                    {
-                       while (--relativePosition)
+                       while (--relativePosition && --textBlockPos)
                        {
-                           if (tl.getAtomIndexAtCharIndex(relativePosition) != currentAtomIndex)
+                           if (tl.getAtomIndexAtCharIndex(textBlockPos) != currentAtomIndex)
                                break;
                        }
                    }
                    if (CharacterUtil.isLowSurrogate(getText(relativePosition, relativePosition + 1).charCodeAt(0)))
-                       relativePosition--;
+				   {
+					   relativePosition--;
+					   textBlockPos--;
+				   }
+				   
                    //trace("previous", relativePosition, foo);
                 }
                 else
@@ -521,21 +843,26 @@
     					if (!tl)
     						return -1;
     					// need this when 0x2028 line separator in use
-    					if (tl.textBlockBeginIndex + tl.rawTextLength == relativePosition)
-    						return tl.textBlockBeginIndex + tl.rawTextLength - 1;
-    					return tl.textBlockBeginIndex + tl.rawTextLength;
+    					if (tl.textBlockBeginIndex + tl.rawTextLength == textBlockPos)
+    						return tl.textBlockBeginIndex + tl.rawTextLength - 1 + tbStart;
+    					return tl.textBlockBeginIndex + tl.rawTextLength + tbStart;
     				}
-    				while (--relativePosition)
+    				while (--relativePosition && --textBlockPos)
     				{
-    					if (tl.getAtomIndexAtCharIndex(relativePosition) < currentAtomIndex)
+    					if (tl.getAtomIndexAtCharIndex(textBlockPos) < currentAtomIndex)
     						break;
     				}
                     if (CharacterUtil.isLowSurrogate(getText(relativePosition, relativePosition + 1).charCodeAt(0)))
-                        relativePosition--;
+					{
+						relativePosition--;
+						textBlockPos--;
+					}
                 }
 				return relativePosition;
 			}
-            var pos:int = getTextBlock().findPreviousAtomBoundary(relativePosition);
+            var pos:int = tb.findPreviousAtomBoundary(textBlockPos);
+			if(pos >= 0)
+				pos += tbStart;
             //trace("previous", relativePosition, pos);
 			return pos;
 		}
@@ -560,34 +887,41 @@
 		 
 		public function findNextAtomBoundary(relativePosition:int):int
 		{
+			var tb:TextBlock = getTextBlockAtPosition(relativePosition);
+			var tbStart:int = getTextBlockStart(tb);
+			var textBlockPos:int = relativePosition - tbStart;
 			if (ContainerController.tlf_internal::usesDiscretionaryHyphens)
 			{
-				var textBlock:TextBlock = getTextBlock();
-				var tl:TextLine = textBlock.getTextLineAtCharIndex(relativePosition);
-				var currentAtomIndex:int = tl.getAtomIndexAtCharIndex(relativePosition);
+				var tl:TextLine = tb.getTextLineAtCharIndex(textBlockPos);
+				var currentAtomIndex:int = tl.getAtomIndexAtCharIndex(textBlockPos);
                 //trace("relpos", relativePosition, "atomIndex", currentAtomIndex);
                 var isRTL:Boolean = tl.getAtomBidiLevel(currentAtomIndex) == 1;
                 if (isRTL)
                 {
-                    var foo:int = getTextBlock().findNextAtomBoundary(relativePosition);
+                    var foo:int = tb.findNextAtomBoundary(textBlockPos);
                     if (currentAtomIndex == 0)
                     {
-                        while (++relativePosition)
+                        while (++textBlockPos)
                         {
-                            if (tl.getAtomIndexAtCharIndex(relativePosition) != currentAtomIndex)
+							++relativePosition;
+                            if (tl.getAtomIndexAtCharIndex(textBlockPos) != currentAtomIndex)
                                 break;
                         }
                     }
                     else
                     {
-                        while (++relativePosition)
+                        while (++textBlockPos)
                         {
-                            if (tl.getAtomIndexAtCharIndex(relativePosition) != currentAtomIndex)
+							++relativePosition;
+                            if (tl.getAtomIndexAtCharIndex(textBlockPos) != currentAtomIndex)
                                 break;
                         }
                     }
                     if (CharacterUtil.isHighSurrogate(getText(relativePosition, relativePosition + 1).charCodeAt(0)))
-                        relativePosition++;
+					{
+						relativePosition++;
+						textBlockPos++;
+					}
                     //trace("next", relativePosition, foo);
                 }
                 else
@@ -597,19 +931,25 @@
     					tl = tl.nextLine;
     					if (!tl)
     						return -1;
-    					return tl.textBlockBeginIndex;
+    					return tl.textBlockBeginIndex + tbStart;
     				}
-    				while (++relativePosition)
+    				while (++textBlockPos)
     				{
-    					if (tl.getAtomIndexAtCharIndex(relativePosition) > currentAtomIndex)
+						++relativePosition;
+    					if (tl.getAtomIndexAtCharIndex(textBlockPos) > currentAtomIndex)
     						break;
     				}
                     if (CharacterUtil.isHighSurrogate(getText(relativePosition, relativePosition + 1).charCodeAt(0)))
-                        relativePosition++;
+					{
+						relativePosition++;
+						textBlockPos++;
+					}
                 }
 				return relativePosition;
 			}
-			var pos:int = getTextBlock().findNextAtomBoundary(relativePosition);
+			var pos:int = tb.findNextAtomBoundary(textBlockPos);
+			if(pos >= 0)
+				pos += tbStart;
             //trace("next", relativePosition, pos);
             return pos;
 		}
@@ -617,7 +957,27 @@
 		/** @private */
 		public override function getCharAtPosition(relativePosition:int):String
 		{
-			return getTextBlock().content.rawText.charAt(relativePosition);
+			var foundTB:TextBlock = getTextBlockAtPosition(relativePosition);
+			if(!foundTB)
+				return "\u0016";
+			var tables:Vector.<TableElement> = getTables();
+			var pos:int = relativePosition;
+			for each(var table:TableElement in tables)
+			{
+				if(table.getElementRelativeStart(this) < pos)
+					relativePosition--;
+			}
+			var tbs:Vector.<TextBlock> = getTextBlocks();
+			for each(var tb:TextBlock in tbs)
+			{
+				if(foundTB == tb)
+					break;
+				if(tb)
+					relativePosition -= tb.content.rawText.length;
+				else
+					relativePosition -= 1;this.getText()
+			}
+			return foundTB.content.rawText.charAt(relativePosition);
 		} 
 
 		/** 
@@ -650,7 +1010,13 @@
 				}
 				return relativePosition;
 			}
-			return getTextBlock().findPreviousWordBoundary(relativePosition);
+			var block:TextBlock = getTextBlockAtPosition(relativePosition);
+			if(block == null)
+				block = getTextBlockAtPosition(--relativePosition);
+			var pos:int = getTextBlockStart(block);
+			if(pos < 0)
+				pos = 0;
+			return relativePosition == pos ? pos : pos + block.findPreviousWordBoundary(relativePosition - pos);
 		}
 
 		/** 
@@ -683,7 +1049,13 @@
 				}
 				return relativePosition;
 			}
-			return getTextBlock().findNextWordBoundary(relativePosition);
+			var block:TextBlock = getTextBlockAtPosition(relativePosition);
+			if(block == null)
+				block = getTextBlockAtPosition(--relativePosition);
+			var pos:int = getTextBlockStart(block);
+			if(pos < 0)
+				pos = 0;
+			return pos + block.findNextWordBoundary(relativePosition - pos);
 		}
 		
 		static private var _defaultTabStops:Vector.<TabStop>;
@@ -697,8 +1069,10 @@
 				_defaultTabStops[i] = new TabStop(TextAlign.START, defaultTabWidth * i);
 		}
 		
-		private function updateTextBlock():void
+		private function updateTextBlock(textBlock:TextBlock=null):void
 		{
+			if(!textBlock)
+				textBlock = getTextBlock();
 			// find the ancestor with a container and use its format for various settings
 			var containerElement:ContainerFormattedElement = getAncestorWithContainer();
 			if (!containerElement)
@@ -746,10 +1120,10 @@
 				}
 
 				CONFIG::debug { Debugging.traceFTECall(spaceJustifier,null,"new SpaceJustifier",_computedFormat.locale,lineJust,spaceJustifier.letterSpacing); }
-				_textBlock.textJustifier = spaceJustifier;
-				CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"textJustifier",spaceJustifier); }
-				_textBlock.baselineZero = getLeadingBasis(this.getEffectiveLeadingModel());
-				CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"baselineZero",_textBlock.baselineZero);  }
+				textBlock.textJustifier = spaceJustifier;
+				CONFIG::debug { Debugging.traceFTEAssign(textBlock,"textJustifier",spaceJustifier); }
+				textBlock.baselineZero = getLeadingBasis(this.getEffectiveLeadingModel());
+				CONFIG::debug { Debugging.traceFTEAssign(textBlock,"baselineZero",textBlock.baselineZero);  }
 			}
 			else
 			{
@@ -758,21 +1132,21 @@
 					eastAsianJustifier.composeTrailingIdeographicSpaces = true;
 				}
 				CONFIG::debug { Debugging.traceFTECall(eastAsianJustifier,null,"new EastAsianJustifier",_computedFormat.locale,lineJust,makeJustRuleStyle); }
-				_textBlock.textJustifier = eastAsianJustifier as EastAsianJustifier;
-				CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"textJustifier",eastAsianJustifier);  }
-				_textBlock.baselineZero = getLeadingBasis(this.getEffectiveLeadingModel());
-				CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"baselineZero",_textBlock.baselineZero);  }
+				textBlock.textJustifier = eastAsianJustifier as EastAsianJustifier;
+				CONFIG::debug { Debugging.traceFTEAssign(textBlock,"textJustifier",eastAsianJustifier);  }
+				textBlock.baselineZero = getLeadingBasis(this.getEffectiveLeadingModel());
+				CONFIG::debug { Debugging.traceFTEAssign(textBlock,"baselineZero",textBlock.baselineZero);  }
 			}
 			
-			_textBlock.bidiLevel = _computedFormat.direction == Direction.LTR ? 0 : 1;
-			CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"bidiLevel",_textBlock.bidiLevel);  }
+			textBlock.bidiLevel = _computedFormat.direction == Direction.LTR ? 0 : 1;
+			CONFIG::debug { Debugging.traceFTEAssign(textBlock,"bidiLevel",textBlock.bidiLevel);  }
 
-			_textBlock.lineRotation = containerElementFormat.blockProgression == BlockProgression.RL ? TextRotation.ROTATE_90 : TextRotation.ROTATE_0;
-			CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"lineRotation",_textBlock.lineRotation);  }
+			textBlock.lineRotation = containerElementFormat.blockProgression == BlockProgression.RL ? TextRotation.ROTATE_90 : TextRotation.ROTATE_0;
+			CONFIG::debug { Debugging.traceFTEAssign(textBlock,"lineRotation",textBlock.lineRotation);  }
 			
 			if (_computedFormat.tabStops && _computedFormat.tabStops.length != 0)
 			{
-				//create a vector of TabStops and assign it to tabStops in _textBlock
+				//create a vector of TabStops and assign it to tabStops in textBlock
 				var tabStops:Vector.<TabStop> = new Vector.<TabStop>();
 				CONFIG::debug { Debugging.traceFTECall(tabStops,null,"new Vector.<TabStop>()"); }
 				for each(var tsa:TabStopFormat in _computedFormat.tabStops)
@@ -786,8 +1160,8 @@
 					tabStops.push(tabStop);
 					CONFIG::debug { Debugging.traceFTECall(null,tabStops,"push",tabStop); }
 				}
-				_textBlock.tabStops = tabStops;
-				CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"tabStops",tabStops);  }
+				textBlock.tabStops = tabStops;
+				CONFIG::debug { Debugging.traceFTEAssign(textBlock,"tabStops",tabStops);  }
 			} 
 			else if (GlobalSettings.enableDefaultTabStops && !Configuration.playerEnablesArgoFeatures)
 			{
@@ -795,13 +1169,13 @@
 				//	is true, TLF will set up default tabStops in the case where there are no tabs defined. 
 				if (_defaultTabStops == null)
 					initializeDefaultTabStops();
-				_textBlock.tabStops = _defaultTabStops;
-				CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"tabStops",_defaultTabStops);  }
+				textBlock.tabStops = _defaultTabStops;
+				CONFIG::debug { Debugging.traceFTEAssign(textBlock,"tabStops",_defaultTabStops);  }
 			}
 			else
 			{
-				_textBlock.tabStops = null;
-				CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"tabStops",null);  }
+				textBlock.tabStops = null;
+				CONFIG::debug { Debugging.traceFTEAssign(textBlock,"tabStops",null);  }
 			}		 
 		}
 		
@@ -811,8 +1185,10 @@
 			if (!_computedFormat)
 			{
 				super.computedFormat;
-				if (_textBlock)
-					updateTextBlock();
+				var tbs:Vector.<TextBlock> = getTextBlocks();
+				for each(var tb:TextBlock in tbs)
+					updateTextBlock(tb);
+					
 			}
 			return _computedFormat;
 		}
@@ -820,7 +1196,7 @@
 		/** @private */
 		tlf_internal override function canOwnFlowElement(elem:FlowElement):Boolean
 		{
-			return elem is FlowLeafElement || elem is SubParagraphGroupElementBase;
+			return elem is FlowLeafElement || elem is SubParagraphGroupElementBase || elem is TableElement;
 		}
 		
 		/** @private */
@@ -884,32 +1260,6 @@
 			}
 		}
 		
-		// mjzhang : new API for table feature, to discuss
-		public function isInTable():Boolean
-		{
-			var parent:FlowElement = this.parent;
-			while ( parent )
-			{
-				if ( (parent is TableDataCellElement) )
-					return true;
-				parent = parent.parent;
-			}
-				
-			return false;
-		}
-		
-		public function getTableDataCellElement():TableDataCellElement
-		{
-			var parent:FlowElement = this.parent;
-			while ( parent )
-			{
-				if ( (parent is TableDataCellElement) )
-					return parent as TableDataCellElement;
-				parent = parent.parent;
-			}
-			
-			return null;
-		}
 		/** @private */
 		tlf_internal function getEffectiveLeadingModel():String
 		{
@@ -938,19 +1288,20 @@
 		/** @private */
 		CONFIG::debug public override function debugCheckFlowElement(depth:int = 0, extraData:String = ""):int
 		{
-			var rslt:int = super.debugCheckFlowElement(depth," fte:"+getDebugIdentity(_textBlock)+" "+extraData);
+			var tb:TextBlock = getTextBlock();
+			var rslt:int = super.debugCheckFlowElement(depth," fte:"+getDebugIdentity(tb)+" "+extraData);
 			
 			// now check the character count and then the last character 
 			
-			if (_textBlock)
+			if (tb)
 			{
-				var contentLength:int = _textBlock.content && _textBlock.content.rawText ? _textBlock.content.rawText.length : 0;
+				var contentLength:int = tb.content && tb.content.rawText ? tb.content.rawText.length : 0;
 				rslt += assert(contentLength == textLength,"Bad paragraph length mode:"+textLength.toString()+" _textBlock:" + contentLength.toString());
 
-				var groupElement:GroupElement = _textBlock.content as GroupElement;
+				var groupElement:GroupElement = tb.content as GroupElement;
 				if (groupElement)
 					assert(groupElement.elementCount == numChildren,"Mismatched group and elementCount"); 
-				else if (_textBlock.content)
+				else if (tb.content)
 					assert(1 == numChildren,"Mismatched group and elementCount"); 
 				else 
 					assert(0 == numChildren,"Mismatched group and elementCount"); 
@@ -1013,5 +1364,11 @@
 		{
 			return _interactiveChildrenCount != 0 ;
 		}
+
+		tlf_internal function get terminatorSpan():SpanElement

+		{
+			return _terminatorSpan;
+		}
+
 	}
 }
diff --git a/textLayout/src/flashx/textLayout/elements/SpanElement.as b/textLayout/src/flashx/textLayout/elements/SpanElement.as
index c16adff..9e37058 100644
--- a/textLayout/src/flashx/textLayout/elements/SpanElement.as
+++ b/textLayout/src/flashx/textLayout/elements/SpanElement.as
@@ -395,6 +395,8 @@
 					assert(_blockElement.rawText.charAt(_blockElement.rawText.length-1) != SpanElement.kParagraphTerminator,"adding para terminator twice");
 			}
 
+			if(_text && _text.substr(-1) == SpanElement.kParagraphTerminator)// terminator exists. Bail out.
+				return;
 			replaceTextInternal(textLength,textLength,SpanElement.kParagraphTerminator);
 			
 			CONFIG::debug 
@@ -414,6 +416,9 @@
 				assert(_text && _text.length && _text.charAt(_text.length-1) == SpanElement.kParagraphTerminator,
 					"attempting to remove para terminator when it doesn't exist");
 			}
+			if(!_text || _text.substr(-1) != SpanElement.kParagraphTerminator)// no terminator exists. Bail out.
+				return;
+
 			replaceTextInternal(textLength-1,textLength,"");
 			modelChanged(ModelChange.TEXT_DELETED,this,textLength > 0 ? textLength-1 : 0,1);
 		}
@@ -464,7 +469,7 @@
 				{
 					// optimized version leverages player APIs
 					// TODO: Jeff to add split on TextElement so we don't have to go find a group every time
-					var group:GroupElement = parent.createContentAsGroup();
+					var group:GroupElement = parent.createContentAsGroup(getElementRelativeStart(parent));
 					
 					var elementIndex:int = group.getElementIndex(_blockElement);
 					
diff --git a/textLayout/src/flashx/textLayout/elements/SubParagraphGroupElementBase.as b/textLayout/src/flashx/textLayout/elements/SubParagraphGroupElementBase.as
index c65e138..2affda3 100644
--- a/textLayout/src/flashx/textLayout/elements/SubParagraphGroupElementBase.as
+++ b/textLayout/src/flashx/textLayout/elements/SubParagraphGroupElementBase.as
@@ -190,7 +190,7 @@
 		}
 		
 		/** @private */
-		tlf_internal override function createContentAsGroup():GroupElement
+		tlf_internal override function createContentAsGroup(pos:int=0):GroupElement
 		{ return groupElement; }
 
 		/** @private */
diff --git a/textLayout/src/flashx/textLayout/elements/TableBlockContainer.as b/textLayout/src/flashx/textLayout/elements/TableBlockContainer.as
new file mode 100644
index 0000000..d5aef05
--- /dev/null
+++ b/textLayout/src/flashx/textLayout/elements/TableBlockContainer.as
@@ -0,0 +1,31 @@
+package flashx.textLayout.elements
+{
+	import flash.display.Sprite;
+	
+	import flashx.textLayout.compose.TextFlowTableBlock;
+	
+	/**
+	 * The sprite that contains the table cells. 
+	 **/
+	public class TableBlockContainer extends Sprite
+	{
+		
+		public function TableBlockContainer()
+		{
+			super();
+		}
+		
+		/**
+		 * A reference to the TextFlowTableBlock
+		 **/
+		public var userData:TextFlowTableBlock;
+		public function getTableWidth():Number
+		{
+			if(!userData)
+				return NaN;
+			if(!userData.parentTable)
+				return NaN;
+			return userData.parentTable.width;
+		}
+	}
+}
\ No newline at end of file
diff --git a/textLayout/src/flashx/textLayout/elements/TableCellElement.as b/textLayout/src/flashx/textLayout/elements/TableCellElement.as
new file mode 100644
index 0000000..197d4c9
--- /dev/null
+++ b/textLayout/src/flashx/textLayout/elements/TableCellElement.as
@@ -0,0 +1,461 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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 flashx.textLayout.elements
+{
+	import flash.display.Graphics;
+	import flash.display.Shape;
+	import flash.display.Sprite;
+	import flash.events.MouseEvent;
+	import flash.geom.Point;
+	import flash.text.engine.GraphicElement;
+	import flash.utils.getDefinitionByName;
+	import flash.utils.getQualifiedClassName;
+	
+	import flashx.textLayout.compose.FlowDamageType;
+	import flashx.textLayout.compose.TextFlowLine;
+	import flashx.textLayout.container.ContainerController;
+	import flashx.textLayout.edit.EditManager;
+	import flashx.textLayout.edit.IEditManager;
+	import flashx.textLayout.edit.ISelectionManager;
+	import flashx.textLayout.events.DamageEvent;
+	import flashx.textLayout.events.ModelChange;
+	import flashx.textLayout.formats.BlockProgression;
+	import flashx.textLayout.formats.Float;
+	import flashx.textLayout.tlf_internal;
+	import flashx.undo.UndoManager;
+	
+	use namespace tlf_internal;
+	
+	/** 
+	 * TableCellElement is an item in a TableElement. It most commonly contains one or more ParagraphElement objects.
+	 *
+	 * 
+	 * @playerversion Flash 10
+	 * @playerversion AIR 1.5
+	 * @langversion 3.0
+	 *
+	 */
+	public final class TableCellElement extends TableFormattedElement
+	{		
+		private var _x:Number;
+		private var _y:Number;
+		private var _width:Number;
+		private var _height:Number;
+
+		private var _parcelIndex:int;
+		private var _container:CellContainer;
+		private var _enableIME:Boolean = true;
+		private var _damaged:Boolean = true;
+		private var _controller:ContainerController;
+
+		private var _rowSpan:uint = 1;
+		private var _columnSpan:uint = 1;
+		private var _rowIndex:int = -1;
+		private var _colIndex:int = -1;
+		private var _includeDescentInCellBounds:Boolean;
+		
+		public function TableCellElement()
+		{
+			super();
+			_controller = new ContainerController(container,NaN,NaN);
+		}
+
+		/** @private */
+		override protected function get abstract():Boolean
+		{ return false; }
+		
+		/** @private */
+		tlf_internal override function get defaultTypeName():String
+		{ return "td"; }
+		
+		/** @private */
+		tlf_internal override function canOwnFlowElement(elem:FlowElement):Boolean
+		{// Table cells have no TLF children. Instead it contains its own TextFlow.
+			return (elem is FlowElement);
+		}
+
+		public function isDamaged():Boolean {
+			return _damaged || (_textFlow && _textFlow.flowComposer.isDamaged(_textFlow.textLength));
+		}
+		
+		private var _savedPaddingTop:Number = 0;
+		private var _savedPaddingBottom:Number = 0;
+		private var _savedPaddingLeft:Number = 0;
+		private var _savedPaddingRight:Number = 0;
+		
+		public function compose():Boolean {
+			
+			var pt:Number = getEffectivePaddingTop();
+			var pb:Number = getEffectivePaddingBottom();
+			var pl:Number = getEffectivePaddingLeft();
+			var pr:Number = getEffectivePaddingRight();
+
+			if(pt != _savedPaddingTop)
+			{
+				_controller.paddingTop = _savedPaddingTop = pt;
+			}
+			if(pb != _savedPaddingBottom)
+			{
+				_controller.paddingBottom = _savedPaddingBottom = pb;
+			}
+			if(pl != _savedPaddingLeft)
+			{
+				_controller.paddingLeft = _savedPaddingLeft = pl;
+			}
+			if(pr != _savedPaddingRight)
+			{
+				_controller.paddingRight = _savedPaddingRight = pr;
+			}
+
+			var table:TableElement = getTable();
+			
+			_damaged = false;
+			
+			var compWidth:Number = 0;
+			for(var i:int=0;i<columnSpan;i++)
+			{
+				if (table && table.getColumnAt(colIndex+i)) {
+					compWidth += table.getColumnAt(colIndex+i).columnWidth;
+				}
+				
+			}
+			width = compWidth;
+
+			if (_textFlow && _textFlow.flowComposer) {
+				return _textFlow.flowComposer.compose();
+			}
+			
+			return false;
+		}
+		
+		public function update():Boolean
+		{
+			if(_textFlow && _textFlow.flowComposer){
+				return _textFlow.flowComposer.updateAllControllers();
+			}
+			return false;
+		}
+		
+		public function get parcelIndex():int
+		{
+			return _parcelIndex;
+		}
+		
+		public function set parcelIndex(value:int):void
+		{
+			_parcelIndex = value;
+		}
+		
+		public function get rowIndex():int
+		{
+			return _rowIndex;
+		}
+		
+		public function set rowIndex(value:int):void
+		{
+			_rowIndex = value;
+		}
+		
+		public function get colIndex():int
+		{
+			return _colIndex;
+		}
+		
+		public function set colIndex(value:int):void
+		{
+			_colIndex = value;
+		}
+		
+		protected var _textFlow:TextFlow;
+		
+		public function get textFlow():TextFlow {
+			
+			if (_textFlow == null) {
+				var flow:TextFlow = new TextFlow();
+				
+				if (table && table.getTextFlow() && table.getTextFlow().interactionManager is IEditManager) {
+					flow.interactionManager = new EditManager(IEditManager(_textFlow.interactionManager).undoManager);
+				}
+				else if(table && table.getTextFlow() && table.getTextFlow().interactionManager) {
+					var im:Class = getDefinitionByName(getQualifiedClassName(table.getTextFlow().interactionManager)) as Class;
+					flow.interactionManager = new im();
+				}
+				else {
+					flow.normalize();
+				}
+				
+				textFlow = flow;
+
+			}
+			
+			return _textFlow;
+		}
+		
+		public function set textFlow(value:TextFlow):void
+		{
+			if (_textFlow) {
+				_textFlow.removeEventListener(DamageEvent.DAMAGE, handleCellDamage);
+				_textFlow.flowComposer.removeAllControllers();
+			}
+			
+			_textFlow = value;
+			_textFlow.parentElement = this;
+			_textFlow.flowComposer.addController(_controller);
+			_textFlow.addEventListener(DamageEvent.DAMAGE, handleCellDamage);
+			
+		}
+		
+		public function get controller():ContainerController {
+			return _controller;
+		}
+		
+		private function handleCellDamage(ev:DamageEvent):void{
+			damage();
+		}
+
+		public function get enableIME():Boolean

+		{
+			return _enableIME;
+		}
+
+		public function set enableIME(value:Boolean):void

+		{
+			_enableIME = value;
+		}
+		
+		public function get container():CellContainer{
+			if(!_container){
+				_container = new CellContainer(enableIME);
+				_container.element = this;
+			}
+			
+			return _container;
+		}
+
+		/**
+		 * Gets the width.
+		 **/
+		public function get width():Number

+		{
+			return _width;
+		}
+
+		/**
+		 * @private
+		 **/
+		public function set width(value:Number):void

+		{
+			if(_width != value) {
+				_damaged = true;
+			}
+			
+			_width = value;
+			
+			_controller.setCompositionSize(_width, _controller.compositionHeight);
+		}
+		
+		/**
+		 * Returns the height of the cell. 
+		 **/
+		public function get height():Number

+		{
+			//return getRowHeight(); not sure if we should always use row height
+			return _height;
+		}
+
+		/**
+		 * @private
+		 **/
+		public function set height(value:Number):void

+		{
+			if (_height != value) {
+				_damaged = true;
+			}
+			
+			_height = value;
+			
+			_controller.setCompositionSize(_controller.compositionWidth, _height);
+		}
+		
+		public function getComposedHeight():Number
+		{
+			var descent:Number = 0;
+			if(!includeDescentInCellBounds)
+			{
+				if(_textFlow.flowComposer && _textFlow.flowComposer.numLines)
+				{
+					var lastLine:TextFlowLine = _textFlow.flowComposer.getLineAt(_textFlow.flowComposer.numLines-1);
+					if(lastLine)
+						descent = lastLine.descent;
+				}
+			}
+			return (_controller.getContentBounds().height - descent);
+		}
+		
+		public function getRowHeight():Number
+		{
+			return getRow() ? getRow().composedHeight : NaN;
+		}
+
+		public function get rowSpan():uint

+		{
+			return _rowSpan;
+		}
+
+		public function set rowSpan(value:uint):void

+		{
+			if(value >= 1)
+				_rowSpan = value;
+		}
+
+		public function get columnSpan():uint

+		{
+			return _columnSpan;
+		}
+
+		public function set columnSpan(value:uint):void

+		{
+			if(value >= 1)
+				_columnSpan = value;
+		}
+		
+		public function updateCompositionShapes():void{
+			_controller.updateCompositionShapes();
+		}
+		
+		/**
+		 * Return the row that this cell is part of or null 
+		 * if not part of a row.
+		 **/
+		public function getRow():TableRowElement
+		{
+			return table ? table.getRowAt(rowIndex) : null;
+		}
+		
+		/**
+		 * Returns the next cell in the table or null if not part of a
+		 * table or no cells exist after this cell.
+		 **/
+		public function getNextCell():TableCellElement {
+			return table ? table.getNextCell(this) : null;
+		}
+		
+		/**
+		 * Returns the previous cell in the table or null if not part of a
+		 * table or no cells exist before this cell.
+		 **/
+		public function getPreviousCell():TableCellElement {
+			return table ? table.getPreviousCell(this) : null;
+		}
+
+		public function get x():Number

+		{
+			return container.x;
+		}
+
+		public function set x(value:Number):void

+		{
+			container.x = value;
+		}
+
+		public function get y():Number

+		{
+			return container.y;
+		}
+
+		public function set y(value:Number):void

+		{
+			container.y = value;
+		}
+
+		public function damage():void
+		{
+			if (table) {
+				table.hasCellDamage = true;
+			}
+			
+			_damaged = true;
+		}
+		
+		/**
+		 * Adds in the table cell spacing, border stroke width. 
+		 * We may be able to set this value when the format changes. 
+		 * For now we just want to get it to work. 
+		 **/
+		public function getTotalPaddingWidth():Number {
+			var paddingAmount:Number = 0;
+			
+			// no textflow is no padding
+			if (!textFlow) {
+				return 0;
+			}
+			
+			if (table && table.cellSpacing!=undefined) {
+				paddingAmount += table.cellSpacing;
+			}
+			
+			if (textFlow.computedFormat.blockProgression == BlockProgression.RL) {
+				paddingAmount += Math.max(getEffectivePaddingTop() + getEffectivePaddingBottom(), getEffectiveBorderTopWidth() + getEffectiveBorderBottomWidth());
+			}
+			else {
+				paddingAmount += Math.max(getEffectivePaddingLeft() + getEffectivePaddingRight(), getEffectiveBorderLeftWidth() + getEffectiveBorderRightWidth());
+			}
+			
+			return paddingAmount;
+		}
+		
+		/**
+		 * Adds in the table cell spacing, border stroke height. 
+		 * We may be able to set this value when the format changes. 
+		 **/
+		public function getTotalPaddingHeight():Number {
+			var paddingAmount:Number = 0;
+			
+			// no textflow is no padding
+			if (!textFlow) {
+				return 0;
+			}
+			
+			if (table && table.cellSpacing!=undefined) {
+				paddingAmount += table.cellSpacing;
+			}
+			
+			if (textFlow.computedFormat.blockProgression == BlockProgression.RL) {
+				paddingAmount += Math.max(getEffectivePaddingLeft() + getEffectivePaddingRight(), getEffectiveBorderLeftWidth() + getEffectiveBorderRightWidth());
+			}
+			else {
+				paddingAmount += Math.max(getEffectivePaddingTop() + getEffectivePaddingBottom(), getEffectiveBorderTopWidth() + getEffectiveBorderBottomWidth());
+			}
+			
+			return paddingAmount;
+		}
+
+		public function get includeDescentInCellBounds():Boolean

+		{
+			return _includeDescentInCellBounds;
+		}
+
+		public function set includeDescentInCellBounds(value:Boolean):void

+		{
+			_includeDescentInCellBounds = value;
+		}
+
+		
+	}
+}
diff --git a/textLayout/src/flashx/textLayout/elements/TableColElement.as b/textLayout/src/flashx/textLayout/elements/TableColElement.as
index 85a1d9e..342b9ce 100644
--- a/textLayout/src/flashx/textLayout/elements/TableColElement.as
+++ b/textLayout/src/flashx/textLayout/elements/TableColElement.as
@@ -18,6 +18,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 package flashx.textLayout.elements
 {
+	import flashx.textLayout.formats.ITextLayoutFormat;
 	import flashx.textLayout.tlf_internal;
 	
 	use namespace tlf_internal;
@@ -36,6 +37,15 @@
 	{		
 		//public var height:Number;
 		public var x:Number;
+		public var colIndex:int;
+		
+		public function TableColElement(format:ITextLayoutFormat=null)
+		{
+			super();
+			if(format)
+				this.format = format;
+		}
+
 		
 		/** @private */
 		override protected function get abstract():Boolean
@@ -56,6 +66,29 @@
 		{
 			super.modelChanged(changeType,elem,changeStart,changeLen,needNormalize,bumpGeneration);
 		}
-
+		
+		/**
+		 * Get a Vector of cells or null if the column contains no cells
+		 **/
+		public function get cells():Vector.<TableCellElement> {
+			
+			if (!table) {
+				return null;
+			}
+			
+			return table.getCellsForColumn(this);
+		}
+		
+		/**
+		 * Returns the number of cells in this column. 
+		 **/
+		public function get numCells():int {
+			
+			if (!table) {
+				return 0;
+			}
+			
+			return table.getCellsForColumn(this).length;
+		}
 	}
 }
diff --git a/textLayout/src/flashx/textLayout/elements/TableElement.as b/textLayout/src/flashx/textLayout/elements/TableElement.as
index 92cefcc..c7f2fbc 100644
--- a/textLayout/src/flashx/textLayout/elements/TableElement.as
+++ b/textLayout/src/flashx/textLayout/elements/TableElement.as
@@ -18,19 +18,17 @@
 ////////////////////////////////////////////////////////////////////////////////
 package flashx.textLayout.elements
 {
-	import flash.display.Graphics;
-	import flash.events.Event;
-	import flash.events.EventDispatcher;
-	import flash.events.IEventDispatcher;
-	import flash.events.MouseEvent;
-	import flash.geom.Point;
-	import flash.text.engine.TextBlock;
-	import flash.text.engine.TextLine;
+	import flash.display.Sprite;
+	import flash.text.engine.ContentElement;
+	import flash.text.engine.GraphicElement;
+	import flash.utils.Dictionary;
 	
-	import flashx.textLayout.events.FlowElementEventDispatcher;
-	import flashx.textLayout.events.FlowElementMouseEventManager;
+	import flashx.textLayout.compose.TextFlowTableBlock;
+	import flashx.textLayout.edit.SelectionFormat;
 	import flashx.textLayout.events.ModelChange;
-	import flashx.textLayout.formats.*;
+	import flashx.textLayout.formats.FormatValue;
+	import flashx.textLayout.formats.ITextLayoutFormat;
+	import flashx.textLayout.formats.TextLayoutFormat;
 	import flashx.textLayout.tlf_internal;
 	
 	use namespace tlf_internal;
@@ -41,7 +39,6 @@
 	 * A TableElement's children must be of type TableRowElement, TableColElement, TableColGroupElement, TableBodyElement.
 	 * 
 	 * 
-	 * 
 	 * @playerversion Flash 10
 	 * @playerversion AIR 1.5
 	 * @langversion 3.0
@@ -49,41 +46,38 @@
 	 */
 	public class TableElement extends TableFormattedElement 
 	{
-		private var _row:int;
-		private var _column:int;
 		
-		private var _height:Array = []; // parcel-indexed
-		public var computedWidth:Number;
+		private var _computedWidth:Number;
 		
 		public var x:Number;
 		public var y:Number;
 		
-		//These attributes is from the original loop prototype. Maybe changed later
-		public var totalRowDepth:Number = undefined;
-		public var originParcelIndex:Number;
-		public var numAcrossParcels:int;
-        public var curRowIdx:int = 0; // this value should be only used while composing
-        public var outOfLastParcel:Boolean = false; 
-			
-		private var arColumn:Array = [];
+		private var columns:Vector.<TableColElement> = new Vector.<TableColElement>();
+		private var rows:Vector.<TableRowElement> = new Vector.<TableRowElement>();
+		private var damagedColumns:Vector.<TableColElement> = new Vector.<TableColElement>();
+		private var damageRows:Vector.<TableRowElement> = new Vector.<TableRowElement>();
+		private var _hasCellDamage:Boolean = true;
+		
+		private var _headerRowCount:uint = 0;
+		private var _footerRowCount:uint = 0;
+		private var _tableRowsComputed:Boolean;
+		
+		private var _headerRows:Vector.< Vector.<TableCellElement> >;
+		private var _footerRows:Vector.< Vector.<TableCellElement> >;
+		private var _bodyRows:Vector.< Vector.<TableCellElement> >;
+		private var _composedRowIndex:uint = 0;
+		
+		private var _tableBlocks:Vector.<TextFlowTableBlock>;
+		private var _tableBlockIndex:uint = 0;
+		private var _tableBlockDict:Dictionary;
+		
+		private var _leaf:TableLeafElement;
 		
 		public function TableElement()
 		{
 			super();
 		}
 		
-		public function initTableElement(row:Number, column:Number):void
-		{
-			_row = row;
-			_column = column;
-			
-			for ( var i:int = 0; i < column; i ++ )
-			{
-				var col:TableColElement = new TableColElement();	
-				arColumn[i] = col;
-			}
-		}
-		
 		/** @private */
 		override protected function get abstract():Boolean
 		{ return false; }
@@ -95,35 +89,735 @@
 		/** @private */
 		tlf_internal override function canOwnFlowElement(elem:FlowElement):Boolean
 		{
-			return  (elem is TableBodyElement) || (elem is TableRowElement) || (elem is TableColElement) || (elem is TableColGroupElement);
+			return (elem is TableCellElement) || (elem is TableRowElement) || (elem is TableColElement);// || (elem is TableBodyElement) || (elem is TableColGroupElement);
 		}
 		
 		/** @private if its in a numbered list expand the damage to all list items - causes the numbers to be regenerated */
 		tlf_internal override function modelChanged(changeType:String, elem:FlowElement, changeStart:int, changeLen:int, needNormalize:Boolean = true, bumpGeneration:Boolean = true):void
 		{
+			if (changeType==ModelChange.ELEMENT_ADDED) {
+				
+			}
+			else if (changeType==ModelChange.ELEMENT_REMOVAL) {
+				if (headerRowCount > 0 || footerRowCount > 0) {
+					
+				}
+			}
+			
 			super.modelChanged(changeType,elem,changeStart,changeLen,needNormalize,bumpGeneration);
 		}
 		
-		public function get row():int
+		override public function set cellSpacing(cellSpacingValue:*):void
 		{
-			return _row;
+			
+			markCellsDamaged();
+			hasCellDamage = true;
+			normalizeCells();
+			
+			super.cellSpacing = cellSpacingValue;
 		}
 		
-		public function get column():int
+		public function get numRows():int
 		{
-			return _column;
+			return rows.length;
+		}
+		
+		public function get numColumns():int
+		{
+			return columns.length;
+		}
+		
+		/**
+		 * Total number of cells
+		 **/
+		public function get numCells():int
+		{
+			return getCells().length;
+		}
+		
+		/**
+		 * Total number of rows in the table. If set to a value lower than
+		 * the current number of rows the rows at the end of the table are removed. 
+		 * If the set to a value greater than the current number of rows additional
+		 * rows are added to the table. 
+		 **/
+		public function set numRows(value:int):void
+		{
+			while(value < numRows){
+				rows.pop();
+			}
+			var num:int = numRows;
+			for(var i:int = num;i<value;i++) {
+				var row:TableRowElement = createRowElement(i, defaultRowFormat);
+				rows.push(row);
+			}
 		}
 
-		public function getColumnAt(columnIndex:int):TableColElement
+		/**
+		 * Total number of columns in the table. If set to a value lower than
+		 * the current number of columns the columns at the end of the table are removed. 
+		 * If the set to a value greater than the current number of columns additional
+		 * columns are added to the table. 
+		 **/
+		public function set numColumns(value:int):void
 		{
-			if ( columnIndex < 0 || columnIndex >= _column )
-				return null;
-			return arColumn[columnIndex];
+			while(value < numColumns){
+				columns.pop();
+			}
+			var num:int = numColumns;
+			for(var i:int = num;i<value;i++) {
+				var column:TableColElement = createColumnElement(i, defaultColumnFormat);
+				columns.push(column);
+			}
+		}
+		private var _defaultRowFormat:ITextLayoutFormat;
+
+		/**
+		 * Gets the row format for new rows. 
+		 **/
+		public function get defaultRowFormat():ITextLayoutFormat

+		{
+			if(!_defaultRowFormat)
+				_defaultRowFormat = new TextLayoutFormat(computedFormat);
+			return _defaultRowFormat;
+		}
+
+		public function set defaultRowFormat(value:ITextLayoutFormat):void

+		{
+			_defaultRowFormat = value;
 		}
 		
+		private var _defaultColumnFormat:ITextLayoutFormat;
+
+		/**
+		 * Gets the column format for new columns. 
+		 **/
+		public function get defaultColumnFormat():ITextLayoutFormat

+		{
+			if(!_defaultColumnFormat)
+				_defaultColumnFormat = new TextLayoutFormat(computedFormat);
+			return _defaultColumnFormat;
+		}
+
+		public function set defaultColumnFormat(value:ITextLayoutFormat):void

+		{
+			_defaultColumnFormat = value;
+		}
+		
+		/**
+		 * Adds a table cell element to the table. 
+		 * @inheritDoc
+		 **/
+		override public function addChild(child:FlowElement):FlowElement
+		{
+			
+			if (child is TableFormattedElement) {
+				TableFormattedElement(child).table = this;
+			}
+			
+			super.addChild(child);
+			
+			return child;
+		}
+		
+		/**
+		 * Removes a table cell element from the table. 
+		 * @inheritDoc
+		 **/
+		override public function removeChild(child:FlowElement):FlowElement
+		{
+			super.removeChild(child);
+			
+			if (child is TableFormattedElement) {
+				TableFormattedElement(child).table = null;
+			}
+			
+			return child;
+		}
+		
+		/**
+		 * Add a row at the end of the table. You would use this if you want to add a row
+		 * without changing the table cells. 
+		 * @see addRowAt
+		 * @see insertRow
+		 * @see insertRowAt
+		 **/
+		public function addRow(format:ITextLayoutFormat=null):void{
+			addRowAt(rows.length,format);
+		}
+		
+		/**
+		 * Add a row at the index specified. 
+		 * @see addRow
+		 * @see insertRow
+		 * @see insertRowAt
+		 **/
+		public function addRowAt(idx:int, format:ITextLayoutFormat=null):void{
+			if(idx < 0 || idx > rows.length)
+				throw RangeError(GlobalSettings.resourceStringFunction("badPropertyValue"));
+			
+			var row:TableRowElement = createRowElement(idx, format);
+			rows.splice(idx, 0, row);
+			row.composedHeight = row.computedFormat.minCellHeight;
+			row.isMaxHeight = row.computedFormat.minCellHeight == row.computedFormat.maxCellHeight;
+			row.setParentAndRelativeStartOnly(this, 1);
+		}
+
+		/**
+		 * Adds a column. You would use this if you want to add a column without changing the table cells. 
+		 * The cells would reflow, so a cell in row 2 might move up to row 1.
+		 * @see addColumnAt
+		 * @see insertColumn
+		 * @see insertColumnAt
+		 **/
+		public function addColumn(format:ITextLayoutFormat=null):void{
+			addColumnAt(columns.length,format);
+		}
+		
+		/**
+		 * Adds a column at the index specified. 
+		 * @see addColumn
+		 * @see insertColumn
+		 * @see insertColumnAt
+		 **/
+		public function addColumnAt(idx:int, format:ITextLayoutFormat=null):void{
+			if(idx < 0 || idx > columns.length)
+				throw RangeError(GlobalSettings.resourceStringFunction("badPropertyValue"));
+			if(!format) {
+				format = defaultColumnFormat;
+			}
+			var column:TableColElement = createColumnElement(idx, format);
+			
+			columns.splice(idx, 0, column);
+		}
+
+		/**
+		 * Returns the column at the index specified or null if the index is out of range. 
+		 **/
+		public function getColumnAt(columnIndex:int):TableColElement
+		{
+			if ( columnIndex < 0 || columnIndex >= numColumns )
+				return null;
+			return columns[columnIndex];
+		}
+		
+		/**
+		 * Returns the row at the index specified or null if the index is out of range. 
+		 **/
+		public function getRowAt(rowIndex:int):TableRowElement
+		{
+			if ( rowIndex < 0 || rowIndex >= numRows )
+				return null;
+			return rows[rowIndex];
+		}
+		
+		/**
+		 * Return the index of the row provided or -1 if the row is not found. 
+		 **/
+		public function getRowIndex(row:TableRowElement):int
+		{
+			for(var i:int=0;i<rows.length;i++)
+			{
+				if(rows[i] == row)
+					return i;
+			}
+			return -1;
+		}
+		
+		/**
+		 * Returns a vector of the cells for the row specified. 
+		 **/
+		public function getCellsForRow(row:TableRowElement):Vector.<TableCellElement>{
+			
+			return getCellsForRowAt(row.rowIndex);
+		}
+		
+		/**
+		 * Returns a vector of the cells for the row specified. 
+		 **/
+		public function getCellsForRowArray(row:TableRowElement):Array {
+			
+			return getCellsForRowAtArray(row.rowIndex);
+		}
+		
+		/**
+		 * Returns a vector of the cells for the row at the specified index. 
+		 **/
+		public function getCellsForRowAt(index:int):Vector.<TableCellElement>{
+			var cells:Vector.<TableCellElement> = new Vector.<TableCellElement>();
+			
+			if (index < 0) {
+				return cells;
+			}
+			
+			for each(var cell:TableCellElement in mxmlChildren){
+				if (cell.rowIndex == index) {
+					cells.push(cell);
+				}
+			}
+			
+			return cells;
+		}
+		
+		/**
+		 * Returns an array of the cells for the row specified. 
+		 **/
+		public function getCellsForRowAtArray(index:int):Array {
+			var cells:Array = [];
+			
+			if (index < 0) {
+				return cells;
+			}
+			
+			for each(var cell:TableCellElement in mxmlChildren){
+				if (cell.rowIndex == index) {
+					cells.push(cell);
+				}
+			}
+			
+			return cells;
+		}
+		
+		/**
+		 * Returns a Vector of the TableCellElements for the column specified. 
+		 **/
+		public function getCellsForColumn(column:TableColElement):Vector.<TableCellElement> {
+			if(columns.indexOf(column) < 0)
+				return null;
+			
+			return getCellsForColumnAt(column.colIndex);
+		}
+		
+		/**
+		 * Returns a Vector of the TableCellElements for the column at the specified index. 
+		 **/
+		public function getCellsForColumnAt(index:int):Vector.<TableCellElement> {
+			var cells:Vector.<TableCellElement> = new Vector.<TableCellElement>();
+			
+			if (index < 0) {
+				return cells;
+			}
+			
+			for each(var cell:TableCellElement in mxmlChildren){
+				if (cell.colIndex == index) {
+					cells.push(cell);
+				}
+			}
+			
+			return cells;
+		}
+		
+		/**
+		 * Convenience method for checking if table has merged cells
+		 * 
+		 */
+		public function hasMergedCells():Boolean
+		{
+			var cell:TableCellElement;
+			var child:*;
+			if(mxmlChildren == null)
+				return false;
+			for each(child in mxmlChildren)
+			{
+				cell = child as TableCellElement;
+				if( cell && (cell.columnSpan > 1 || cell.rowSpan > 1) )
+					return true;
+			}
+			return false;
+		}
+		
+		/**
+		 * Inserts a column at the end of the table. If a column is not provided one is created. 
+		 * 
+		 * @see addColumn
+		 * @see addColumnAt
+		 * @see insertColumnAt
+		 **/
+		public function insertColumn(column:TableColElement=null,cells:Array = null):Boolean{
+			return insertColumnAt(numColumns,column,cells);
+		}
+		
+		/**
+		 * Inserts a column at the column specified. If the column is not provided it
+		 * creates a new column containing the cells supplied or creates the cells
+		 * based on the number of rows in the table. 
+		 * @see addColumn
+		 * @see addColumnAt
+		 * @see insertColumn
+		 **/
+		public function insertColumnAt(idx:int,column:TableColElement=null,cells:Array = null):Boolean{
+			
+			if (idx < 0 || idx > columns.length) {
+				throw RangeError(GlobalSettings.resourceStringFunction("badPropertyValue"));
+			}
+			
+			if (!column) {
+				column = createColumnElement(idx, defaultColumnFormat);
+			}
+			
+			columns.splice(idx,0,column);
+			
+			var blockedCoords:Vector.<CellCoords> = getBlockedCoords(-1,idx);
+			var cellIdx:int = getCellIndex(0,idx);
+			if(cellIdx < 0)
+				cellIdx = numChildren;
+			var rowIdx:int = 0;
+			
+			if (cells==null) cells = []; 
+			
+			while(cells.length < numRows){
+				cells.push(new TableCellElement());
+			}
+			
+			for each(var cell:TableCellElement in cells){
+				while(blockedCoords.length && blockedCoords[0].row == rowIdx){
+					rowIdx++;
+					blockedCoords.shift();
+				}
+				cellIdx = getCellIndex(rowIdx,idx);
+				if(cellIdx < 0)
+					cellIdx = numChildren;
+				
+				if(rowIdx < numRows){
+					addChildAt(cellIdx,cell);
+				}
+				rowIdx++;
+			}
+
+
+			return true;
+		}
+		
+		/**
+		 * Inserts a row at the end of the table. If a row is not provided one is created. 
+		 * @see insertRowAt
+		 **/
+		public function insertRow(row:TableRowElement=null,cells:Array = null):Boolean{
+			return insertRowAt(numRows,row,cells);
+		}
+		
+		/**
+		 * Inserts a row at the index specified. If the row is not provided it
+		 * creates a new row containing the cells supplied or creates the cells
+		 * based on the number of columns in the table. 
+		 **/
+		public function insertRowAt(idx:int,row:TableRowElement=null,cells:Array = null):Boolean{
+			if (idx < 0 || idx > rows.length) {
+				throw RangeError(GlobalSettings.resourceStringFunction("badPropertyValue"));
+			}
+			
+			if (!row) {
+				row = createRowElement(idx, defaultRowFormat);
+			}
+			
+			rows.splice(idx,0,row);
+			row.composedHeight = row.computedFormat.minCellHeight;
+			row.isMaxHeight = row.computedFormat.minCellHeight == row.computedFormat.maxCellHeight;
+
+			var blockedCoords:Vector.<CellCoords> = getBlockedCoords(idx);
+			var cellIdx:int = getCellIndex(idx,0);
+			if(cellIdx < 0)
+				cellIdx = numChildren;
+
+			var colIdx:int = 0;
+			
+			if (cells==null) cells = [];
+			
+			// create more cells 
+			while(cells.length < numColumns){
+				cells.push(new TableCellElement());
+			}
+			
+			for each(var cell:TableCellElement in cells){
+				while(blockedCoords.length && blockedCoords[0].column == colIdx){
+					colIdx++;
+					blockedCoords.shift();
+				}
+				if(colIdx < numColumns){
+					addChildAt(cellIdx++,cell);
+					cell.damage();
+				}
+			}
+			return true;
+		}
+		
+		/**
+		 * Removes the row
+		 **/
+		public function removeRow(row:TableRowElement):TableRowElement {
+			var i:int = rows.indexOf(row);
+			if(i < 0)
+				return null;
+			return removeRowAt(i);
+		}
+		
+		/**
+		 * Removes the row and the cells it contains.
+		 **/
+		public function removeRowWithContent(row:TableRowElement):Array
+		{
+			var i:int = rows.indexOf(row);
+			if(i < 0)
+				return null;
+			return removeRowWithContentAt(i);
+		}
+		
+		/**
+		 * Removes the row at the index specified.
+		 * @see removeRowWithContentAt
+		 **/
+		public function removeRowAt(idx:int):TableRowElement {
+			if(idx < 0 || idx > rows.length - 1)
+				return null;
+			
+			var row:TableRowElement = TableRowElement(rows.splice(idx,1)[0]);
+			normalizeCells();
+			hasCellDamage = true;
+			return row;
+			
+		}
+		
+		/**
+		 * Removes the row at the index specified and the cells it contains.
+		 **/
+		public function removeRowWithContentAt(idx:int):Array
+		{
+
+			var removedCells:Array = [];
+			
+			if(mxmlChildren){
+				for (var i:int = mxmlChildren.length-1;i>=0;i--){
+					var child:* = mxmlChildren[i];
+					if(!(child is TableCellElement))
+						continue;
+					var cell:TableCellElement = child as TableCellElement;
+					if(cell.rowIndex == idx){
+						removedCells.unshift(removeChild(cell));
+					}
+				}
+			}
+			
+			removeRowAt(idx);
+			return removedCells;
+		}
+		
+		/**
+		 * Removes all the rows and the cells.
+		 **/
+		public function removeAllRowsWithContent():void
+		{
+			var rowCount:int;
+			var cellCount:int;
+			
+			if (numRows>-1) {
+				rowCount = numRows-1;
+				
+				for (;rowCount>-1;) {
+					removeRowWithContentAt(rowCount--);
+				}
+				
+			}
+		}
+		
+		/**
+		 * Removes all the rows. Does not remove the cells.
+		 * @see removeAllRowsWithContent
+		 **/
+		public function removeAllRows():void
+		{
+			var rowCount:int;
+			var cellCount:int;
+			
+			if (numRows>-1) {
+				rowCount = numRows;
+				
+				for (var i:int; i < rowCount; i++) {
+					removeRowAt(i);
+				}
+				
+			}
+		}
+		
+		/**
+		 * Removes the column
+		 **/
+		public function removeColumn(column:TableColElement):TableColElement {
+			var i:int = columns.indexOf(column);
+			if(i < 0)
+				return null;
+			return removeColumnAt(i);
+		}
+		
+		/**
+		 * Removes the column and the cells it contains.
+		 **/
+		public function removeColumnWithContent(column:TableColElement):Array
+		{
+			var i:int = columns.indexOf(column);
+			if(i < 0)
+				return null;
+			return removeColumnWithContentAt(i);
+		}
+
+		/**
+		 * Removes the column at the index specified
+		 **/
+		public function removeColumnAt(idx:int):TableColElement {
+			if(idx < 0 || idx > columns.length - 1)
+				return null;
+			
+			var col:TableColElement = columns.splice(idx,1)[0];
+			normalizeCells();
+			hasCellDamage = true;
+			return col;
+		}
+		
+		/**
+		 * Removes the column at the index specified and the cells it contains. 
+		 **/
+		public function removeColumnWithContentAt(idx:int):Array
+		{
+			
+			var removedCells:Array = [];
+			if(mxmlChildren){
+				for (var i:int = mxmlChildren.length-1;i>=0;i--){
+					var child:* = mxmlChildren[i];
+					if(!(child is TableCellElement))
+						continue;
+					var cell:TableCellElement = child as TableCellElement;
+					if(cell.colIndex == idx){
+						removedCells.unshift(removeChild(cell));
+					}
+				}
+			}
+			removeColumnAt(idx);
+
+			return removedCells;
+		}
+		
+		/**
+		 * Remove all cells
+		 * @inheritDoc
+		 **/
+		override tlf_internal function removed():void
+		{
+			hasCellDamage = true;
+			//removeAllRowsWithContent();
+		}
+		
+		/**
+		 * @private
+		 * Gets table coordinates which represents the space occupied by cells spanning rows or columns
+		 **/
+		private function getBlockedCoords(inRow:int = -1, inColumn:int = -1):Vector.<CellCoords>{
+			var coords:Vector.<CellCoords> = new Vector.<CellCoords>();
+			
+			if(mxmlChildren) {
+				for each(var child:* in mxmlChildren){
+					var cell:TableCellElement = child as TableCellElement;
+					if (cell==null) continue;
+					if(cell.columnSpan == 1 && cell.rowSpan == 1)
+						continue;
+					var curRow:int = cell.rowIndex;
+					if(inRow >= 0 && curRow != inRow)
+						continue;
+					if(inColumn >= 0 && inColumn != curColumn)
+						continue;
+					var curColumn:int = cell.colIndex;
+					var endRow:int = curRow + cell.rowSpan - 1;
+					var endColumn:int = curColumn + cell.columnSpan -1;
+					for(var rowIdx:int = curRow;rowIdx <= endRow;rowIdx++){
+						for(var colIdx:int = curColumn;colIdx <=endColumn;colIdx++){
+							if(rowIdx == curRow && colIdx == curColumn){
+								continue;
+							}
+							coords.push( new CellCoords(colIdx, rowIdx) );
+						}
+					}
+
+				}
+			}
+			return coords;
+		}
+		
+		/**
+		 * Sets the row and column indices of the cells in the table to match their logical position as described by the table columns and rows
+		 **/
+		public function normalizeCells():void
+		{
+			this.numColumns;this.numRows;
+			var i:int;
+			var blockedCoords:Vector.<CellCoords> = new Vector.<CellCoords>();
+			
+			if (!mxmlChildren) {
+				return;
+			}
+			
+			var curRow:int = 0;
+			var curColumn:int = 0;
+			
+			for each(var child:* in mxmlChildren) {
+				
+				if (!(child is TableCellElement)) {
+					continue;
+				}
+				
+				var cell:TableCellElement = child as TableCellElement;
+				
+				if (cell.rowIndex != curRow || cell.colIndex != curColumn) {
+					cell.rowIndex = curRow;
+					cell.colIndex = curColumn;
+					cell.damage();
+				}
+				
+				// add blocked coords if the cell spans rows or columns
+				var endRow:int = curRow + cell.rowSpan - 1;
+				var endColumn:int = curColumn + cell.columnSpan -1;
+				
+				for(var rowIdx:int = curRow;rowIdx <= endRow;rowIdx++){
+					for(var colIdx:int = curColumn;colIdx <=endColumn;colIdx++){
+						if(rowIdx == curRow && colIdx == curColumn){
+							continue;
+						}
+						blockedCoords.push(new CellCoords(colIdx,rowIdx) );
+					}
+				}
+				
+				// advance coordinates while checking blocked ones from spans
+				do {
+					curColumn++;
+					
+					if (curColumn >= numColumns){
+						curColumn = 0;
+						curRow++;
+					}
+					
+					var advanced:Boolean = true;
+					
+					for (i=0;i<blockedCoords.length;i++){
+						if(blockedCoords[i].column == curColumn && blockedCoords[i].row == curRow){
+							advanced = false;
+							blockedCoords.splice(i,1);
+						}
+					}
+					
+					if (advanced) {
+						break;
+					}
+					
+				} while(1);
+				
+			}
+			
+		}
+		
+		/**
+		 * Set the width of the specified column. The value can be a number or percent. 
+		 **/
 		public function setColumnWidth(columnIndex:int, value:*):Boolean
 		{
-			var tableColElement:TableColElement = getColumnAt(columnIndex) as TableColElement;
+			//TODO: changing the column width probably requires a recompose of all cells in that column. Mark the cells in that row damaged.
+			var tableColElement:TableColElement = getColumnAt(columnIndex);
 			if ( ! tableColElement )
 				return false;
 			
@@ -131,6 +825,22 @@
 			return true;
 		}
 		
+		/**
+		 * Set the height of the specified row. The value can be a number or percent. 
+		 **/
+		public function setRowHeight(rowIdx:int, value:*):Boolean{
+			//TODO: setting the row height might change the composition height of the cells. We'll need to do some housekeeping here.
+			// I'm not sure this function makes sense. We need to handle both min and max values to allow for expanding cells.
+			var row:TableRowElement = getRowAt(rowIdx);
+			if(!row)
+				return false;
+			
+			return true;
+		}
+		
+		/**
+		 * Get the width of the column. 
+		 **/
 		public function getColumnWidth(columnIndex:int):*
 		{
 			var tableColElement:TableColElement = getColumnAt(columnIndex) as TableColElement;
@@ -138,26 +848,715 @@
 				return tableColElement.tableColumnWidth;
 			return 0;
         }
-        
-        public function get height():Number
-        {
-            return _height[numAcrossParcels];
-        }
-        
-        public function set height(val:*):void
-        {
-            _height[numAcrossParcels] = val;
-        }
-        
-        public function get heightArray():Array
-        {
-            return _height;
-        }
-        
-        public function set heightArray(newArray:Array):void
-        {
-            _height = newArray;
-        }
 		
+		/**
+		 * Sizes and positions the cells in the table. 
+		 **/
+		public function composeCells():void{
+			normalizeCells();
+			_composedRowIndex = 0;
+			
+			// make sure the height that defines the row height did not change. If it did we might need to change the row height.
+			if(!hasCellDamage)
+				return;
+			var damagedCells:Vector.<TableCellElement> = getDamagedCells();
+			var cell:TableCellElement;
+			
+			for each(cell in damagedCells){
+				// recompose the cells while tracking row height if necessary
+				cell.compose();
+			}
+			
+			// set row heights to minimum
+			for each (var row:TableRowElement in rows){
+				var minH:Number = row.computedFormat.minCellHeight;
+				var maxH:Number = row.computedFormat.maxCellHeight;
+				row.totalHeight = row.composedHeight = minH;
+				if(maxH > minH)
+					row.isMaxHeight = false;
+				else
+					row.isMaxHeight = true;
+				
+			}
+			
+			// set column positions...
+			var xPos:Number = 0;
+			for each (var col:TableColElement in columns){
+				col.x = xPos;
+				xPos += col.columnWidth;
+			}
+			
+			if (mxmlChildren) {
+				for(var i:int=0;i<mxmlChildren.length;i++){
+					if( !(mxmlChildren[i] is TableCellElement) )
+						continue;
+					cell = mxmlChildren[i] as TableCellElement;
+					while(rows.length < cell.rowIndex+1){
+						addRow(defaultRowFormat);
+					}
+					row = getRowAt(cell.rowIndex);
+					if(!row)
+						throw new Error("this should not happen...");
+					if(row.isMaxHeight) {
+						continue;
+					}
+					
+					var cellHeight:Number = cell.getComposedHeight();
+					if(cell.rowSpan > 1)
+					{
+						// figure out the total height taking into account fixed height rows and the total span.
+						
+						// for now, we're taking the easy way out assuming the rows are not fixed...
+						row.totalHeight = Math.max(row.totalHeight, cellHeight);
+						
+					}
+					else
+					{
+						row.composedHeight = Math.max(row.composedHeight, cellHeight);
+						row.composedHeight = Math.min(row.composedHeight, row.computedFormat.maxCellHeight);
+						row.totalHeight = Math.max(row.composedHeight, row.totalHeight);
+					}
+					if(row.composedHeight == row.computedFormat.maxCellHeight)
+						row.isMaxHeight = true;
+				}
+			}
+			
+			
+			if(!_tableRowsComputed)
+			{
+				// create arrays or rows to make table composition simpler
+				// For now we're assuming all cells have the correct row and column indices.
+				// For this assumption to remain valid, the interaction manager will have to update all indices when inserting rows and columns.
+				// actually, it probably makes sense for TableElement to handle that when adding rows and columns.
+				// we need to think this through.
+				_bodyRows = new Vector.< Vector.<TableCellElement> >();
+				
+				if (mxmlChildren) {
+					for(i=0;i<mxmlChildren.length;i++){
+						
+						if ( !(mxmlChildren[i] is TableCellElement) ) {
+							continue;
+						}
+						
+						cell = mxmlChildren[i] as TableCellElement;
+						
+						while(cell.rowIndex >= _bodyRows.length)
+							_bodyRows.push(new Vector.<TableCellElement>());
+							
+						var rowVec:Vector.<TableCellElement> = _bodyRows[cell.rowIndex] as Vector.<TableCellElement>;
+						
+						if(!rowVec){
+							rowVec = new Vector.<TableCellElement>();
+							_bodyRows[cell.rowIndex] = rowVec;
+						}
+						
+						if(rowVec.length > cell.colIndex && rowVec[cell.colIndex]) {
+							throw new Error("Two cells cannot have the same coordinates");
+						}
+						
+						rowVec.push(cell);
+					}
+				}
+				
+				if(headerRowCount > 0){
+					_headerRows = _bodyRows.splice(0,headerRowCount);
+				} else {
+					_headerRows = null;
+				}
+				
+				if(footerRowCount > 0){
+					_footerRows = _bodyRows.splice(-footerRowCount,Number.MAX_VALUE);
+				} else {
+					_footerRows = null;
+				}
+			}
+		}
+		
+		/**
+		 * returns the header rows for composition
+		 **/
+		public function getHeaderRows():Vector.< Vector.<TableCellElement> >{
+			return _headerRows;
+		}
+		
+		/**
+		 * returns the footer rows for composition
+		 **/
+		public function getFooterRows():Vector.< Vector.<TableCellElement> >{
+			return _footerRows;
+		}
+		
+		/**
+		 * returns the body rows (sans header and footer cells) for composition
+		 **/
+		public function getBodyRows():Vector.< Vector.<TableCellElement> >{
+			return _bodyRows;
+		}
+		
+		/**
+		 * returns a vector of table cells in the next row during composition
+		 **/
+		public function getNextRow():Vector.<TableCellElement>{
+			if(_composedRowIndex >= _bodyRows.length)
+				return null;
+			return _bodyRows[_composedRowIndex++];
+		}
+		
+		/**
+		 * Returns the next table cell after the supplied table cell
+		 **/
+		public function getNextCell(tableCell:TableCellElement):TableCellElement {
+			var cell:TableCellElement;
+			
+			for each (var element:FlowElement in mxmlChildren) {
+				cell = element as TableCellElement;
+				
+				if (cell) {
+					
+					// get next cell in same row 
+					if (cell.rowIndex==tableCell.rowIndex && cell.colIndex-1==tableCell.colIndex) {
+						return cell;
+					}
+					
+					// get first cell in next row
+					if (cell.rowIndex-1==tableCell.rowIndex && cell.colIndex==0) {
+						return cell;
+					}
+					
+				}
+			}
+			
+			return null;
+		}
+		
+		/**
+		 * Returns the previous table cell after the supplied table cell
+		 **/
+		public function getPreviousCell(tableCell:TableCellElement):TableCellElement {
+			var cell:TableCellElement;
+			var highestCellIndex:int = -1;
+			var rowIndex:int = -1;
+			
+			for each (var element:FlowElement in mxmlChildren) {
+				cell = element as TableCellElement;
+				
+				if (cell) {
+					
+					// get previous cell in same row 
+					if (cell.rowIndex==tableCell.rowIndex && cell.colIndex+1==tableCell.colIndex) {
+						return cell;
+					}
+					
+					// get last cell in previous row
+					if (cell.rowIndex+1==tableCell.rowIndex) {
+						rowIndex = cell.rowIndex;
+						
+						if (highestCellIndex<cell.colIndex) {
+							highestCellIndex = cell.colIndex;
+						}
+					}
+					
+				}
+			}
+			
+			if (rowIndex>-1 && highestCellIndex>-1) {
+				return getCellAt(rowIndex, highestCellIndex);
+			}
+			
+			return null;
+		}
+		
+		/**
+		 * Returns the table cell at the row and column specified.
+		 **/
+		public function getCellAt(rowIndex:int, columnIndex:int):TableCellElement {
+			var cell:TableCellElement;
+			
+			for each (var element:FlowElement in mxmlChildren) {
+				cell = element as TableCellElement;
+				
+				if (cell && cell.rowIndex==rowIndex && cell.colIndex==columnIndex) {
+					return cell;
+				}
+			}
+			
+			return null;
+		}
+		
+		/**
+		 * Computed height of the header cells
+		 **/
+		public function getHeaderHeight():Number{
+			//TODO: compute the header height from the header cells
+			return 0;
+		}
+		
+		/**
+		 * Computed height of the footer cells
+		 **/
+		public function getFooterHeight():Number{
+			//TODO: compute the footer height from the footer cells
+			return 0;
+			
+		}
+		
+		/**
+		 * Accepts a suggested table width and calculates the column widths. 
+		 **/
+		public function normalizeColumnWidths(suggestedWidth:Number = 600):void{
+			//TODO: before composition make sure all column widths are rational numbers
+			// We feed in a width to use if there's no width otherwise specified.
+			
+			// quick and dirty...
+			var setCount:* = computedFormat.columnCount;
+			if(!setCount){
+				// we need to figure this out...
+			} else if(setCount == FormatValue.AUTO){
+				// figure out...
+			} else {
+				var cCount:Number = computedFormat.columnCount;
+			}
+			
+			while (cCount > columns.length){
+				addColumn();
+			}
+			
+			var w:Number;
+			switch(typeof(computedFormat.tableWidth)){
+				case "number":
+					w = suggestedWidth;
+					break;
+				case "string":
+					if(computedFormat.tableWidth.indexOf("%") > 0){
+						w = suggestedWidth / (parseFloat(computedFormat.tableWidth)/100);
+						break;
+					}
+				default:
+					w = suggestedWidth;
+					break;
+			}
+			if(isNaN(w))
+				w = 600;
+			if(w > 20000)
+				w = 600;
+			
+			_computedWidth = w;
+
+			var numNonsetColumns:int = numColumns;
+			var col:TableColElement;
+			for each(col in columns){
+				// simply stomp on the settings. (need to finesse this...)
+				if(typeof(col.columnWidth) == "number")
+				{
+					w-= col.columnWidth;
+					numNonsetColumns--;
+				}
+			}
+
+			for each(col in columns)
+			{
+				// simply stomp on the settings. (need to finesse this...)
+				if(typeof(col.columnWidth) == "number")
+					continue;
+				col.columnWidth = w / numNonsetColumns;
+			}
+		}
+		
+		/**
+		 * Returns a vector of all the damaged cells in the table.
+		 **/
+		private function getDamagedCells():Vector.<TableCellElement>{
+			var cells:Vector.<TableCellElement> = new Vector.<TableCellElement>();
+			for each (var cell:* in this.mxmlChildren){
+				if((cell is TableCellElement) && cell.isDamaged())
+					cells.push(cell as TableCellElement);
+			}
+			return cells;
+		}
+		
+		/**
+		 * Marks all of the cells in the table as damaged.
+		 **/
+		private function markCellsDamaged():void {
+			if (!mxmlChildren) return;
+			
+			for each (var cell:* in this.mxmlChildren){
+				if (cell is TableCellElement) {
+					cell.damage();
+				}
+			}
+		}
+		
+		/**
+		 * Returns a vector of all the table cell elements in the table.
+		 **/
+		public function getCells():Vector.<TableCellElement> {
+			var cells:Vector.<TableCellElement> = new Vector.<TableCellElement>();
+			
+			for each (var cell:* in mxmlChildren){
+				if (cell is TableCellElement) {
+					cells.push(cell as TableCellElement);
+				}
+			}
+			
+			return cells;
+		}
+		
+		/**
+		 * Returns an array of all the table cells.
+		 **/
+		public function getCellsArray():Array {
+			var cells:Array = [];
+			
+			for each (var cell:* in mxmlChildren){
+				if (cell is TableCellElement) {
+					cells.push(cell as TableCellElement);
+				}
+			}
+			
+			return cells;
+		}
+		
+		/**
+		 * Returns the table width
+		 **/
+		public function get width():Number
+		{
+			return _computedWidth;
+		}
+		
+		/**
+		 * Sets the table width
+		 **/
+		public function set width(value:*):void
+		{
+			normalizeColumnWidths(value);
+		}
+		
+		
+		/**
+		 * Indicates elements in the table have been modified and the table must be recomposed.
+		 **/
+		public function get hasCellDamage():Boolean

+		{
+			return _hasCellDamage;
+		}
+
+		public function set hasCellDamage(value:Boolean):void

+		{
+			_hasCellDamage = value;
+		}
+
+		/**
+		 * Returns the number of header rows in the table
+		 **/
+		public function get headerRowCount():uint

+		{
+			return _headerRowCount;
+		}
+
+		/**
+		 * Sets the number of header rows in the table
+		 **/
+		public function set headerRowCount(value:uint):void

+		{
+			if(value != _headerRowCount)
+				_tableRowsComputed = false;
+			_headerRowCount = value;
+		}
+
+		/**
+		 * Returns the number of footer rows in the table
+		 **/
+		public function get footerRowCount():uint

+		{
+			return _footerRowCount;
+		}
+
+		/**
+		 * Sets the number of footer rows in the table
+		 **/
+		public function set footerRowCount(value:uint):void

+		{
+			if(value != _footerRowCount)
+				_tableRowsComputed = false;
+			_footerRowCount = value;
+		}
+		
+		/**
+		 * Gets the first TextFlowTableBlock in the table. 
+		 **/
+		public function getFirstBlock():TextFlowTableBlock{
+			if(_tableBlocks == null)
+				_tableBlocks = new Vector.<TextFlowTableBlock>();
+			if(_tableBlocks.length == 0)
+				_tableBlocks.push(new TextFlowTableBlock(0));
+			_tableBlockIndex = 0;
+			_tableBlocks[0].parentTable = this;
+			
+			return _tableBlocks[0];
+		}
+		
+		/**
+		 * Gets the next TextFlowTableBlock. 
+		 **/
+		public function getNextBlock():TextFlowTableBlock{
+			if(_tableBlocks == null)
+				_tableBlocks = new Vector.<TextFlowTableBlock>();
+			_tableBlockIndex++;
+			while(_tableBlocks.length <= _tableBlockIndex){
+				_tableBlocks.push( new TextFlowTableBlock(_tableBlocks.length) );
+			}
+			_tableBlocks[_tableBlockIndex].parentTable = this;
+			
+			return _tableBlocks[_tableBlockIndex];
+		}
+		
+		/**
+		 * Gets the total atom length of this flow element in the text flow.  
+		 * 
+		 * @inheritDoc
+		 **/
+		override public function get textLength():int{
+			return 1;
+		}
+		
+		/**
+		 * Returns the cell at the specified row and column. 
+		 **/
+		private function getCellIndex(rowIdx:int,columnIdx:int):int{
+			if(rowIdx == 0 && columnIdx == 0)
+				return 0;
+			for (var i:int=0;i<mxmlChildren.length;i++){
+				var item:* = mxmlChildren[i];
+				if(!(item is TableCellElement))
+					continue;
+				var cell:TableCellElement = item as TableCellElement;
+				if(cell.rowIndex == rowIdx && cell.colIndex == columnIdx)
+					return i;
+			}
+			return -1;
+			
+		}
+		
+		/**
+		 * Returns a vector of table cell elements in the given cell range. 
+		 **/
+		public function getCellsInRange(anchorCoords:CellCoordinates, activeCoords:CellCoordinates, block:TextFlowTableBlock=null):Vector.<TableCellElement>
+		{
+			var firstCoords:CellCoordinates = anchorCoords.clone();
+			var lastCoords:CellCoordinates = activeCoords.clone();
+			if(
+				lastCoords.row < firstCoords.row ||
+				(lastCoords.row == firstCoords.row && lastCoords.column < firstCoords.column)
+			)
+			{
+				firstCoords = activeCoords.clone();
+				lastCoords = anchorCoords.clone();
+			}
+			
+			// make sure the rectangle is not inversed
+			if(lastCoords.column < firstCoords.column)
+			{
+				var col:int = firstCoords.column;
+				firstCoords.column = lastCoords.column;
+				lastCoords.column = col;
+			}
+			var firstCell:TableCellElement = findCell(firstCoords);
+			var cells:Vector.<TableCellElement> = new Vector.<TableCellElement>();
+			if(!block || getCellBlock(firstCell) == block)
+				cells.push(firstCell);
+			var idx:int = mxmlChildren.indexOf(firstCell);
+			while(++idx < mxmlChildren.length)
+			{
+				var nextCell:TableCellElement = mxmlChildren[idx];
+				if(nextCell.rowIndex > lastCoords.row || (nextCell.rowIndex == lastCoords.row && nextCell.colIndex > lastCoords.column))
+					break;
+				// skip cells outside rectangle
+				if(nextCell.colIndex > lastCoords.column || nextCell.colIndex < firstCoords.column)
+					continue;
+				if(!block || getCellBlock(nextCell) == block)
+					cells.push(nextCell);
+			}
+			return cells;
+		}
+		
+		/**
+		 * Finds the cell at the specified cell coordinates or null if no cell is found. 
+		 **/
+		public function findCell(coords:CellCoordinates):TableCellElement
+		{
+			// get a guess of the cell location. If there's no holes (such as spans), it should theoretically pinpoint the index.
+			var idx:int = (coords.row+1) * (coords.column+1) -1;
+			if(idx >= numChildren)
+				idx = numChildren-1;
+			
+			var cell:TableCellElement = mxmlChildren[idx];
+			// look ahead to see if we're short (not sure if this is needed).
+			do
+			{
+				if(idx == numChildren-1)
+					break;
+				var nextCell:TableCellElement = mxmlChildren[idx+1];
+				if(nextCell.rowIndex > coords.row || (nextCell.rowIndex == coords.row && nextCell.colIndex > coords.column))
+					break;
+				
+				cell = nextCell;
+				idx++;
+				
+			}while(true);
+			// look behind accounting for spans
+			do
+			{
+				//check if the coords fall within the row and column span
+				if(
+					cell.colIndex <= coords.column && cell.colIndex + cell.columnSpan - 1 >= coords.column &&
+					cell.rowIndex <= coords.row && cell.rowIndex + cell.rowSpan - 1 >= coords.row
+				)
+					break;
+				//oops we hit the first cell without finding anything. At least return that...
+				if(cell.colIndex == 0 && cell.rowIndex == 0)
+					break;
+				if(idx == 0)
+					break;
+				var prevCell:TableCellElement = mxmlChildren[idx-1];
+				cell = prevCell;
+				idx--;
+			}while(true);
+			
+			return cell;
+		}
+		
+		/**
+		 * Adds the table cell container to the table block specified. 
+		 **/
+		public function addCellToBlock(cell:TableCellElement, block:TextFlowTableBlock):void
+		{
+			block.addCell(cell.container);
+			tableBlockDict[cell] = block;
+		}
+		
+		/**
+		 * Returns the table block for the given table cell. 
+		 **/
+		public function getCellBlock(cell:TableCellElement):TextFlowTableBlock
+		{
+			return tableBlockDict[cell];
+		}
+
+		/**
+		 * Keeps a reference to all the table blocks belonging to this table. 
+		 **/
+		private function get tableBlockDict():Dictionary

+		{
+			if(_tableBlockDict == null)
+				_tableBlockDict = new Dictionary();
+			return _tableBlockDict;
+		}
+		
+		/**
+		 * Returns a vector of the table blocks.
+		 **/
+		public function get tableBlocks():Vector.<TextFlowTableBlock>
+		{
+			return _tableBlocks;
+		}
+		
+		/**
+		 * Returns a vector of the table blocks in the specified cell range.
+		 **/
+		public function getTableBlocksInRange(start:CellCoordinates,end:CellCoordinates):Vector.<TextFlowTableBlock>
+		{
+			var coords:CellCoordinates = start.clone();
+			if(end.column < start.column)
+			{
+				coords = end.clone();
+				end = start.clone();
+			}
+			var blocks:Vector.<TextFlowTableBlock> = new Vector.<TextFlowTableBlock>();
+			var block:TextFlowTableBlock = getCellBlock(findCell(coords));
+			if(block)
+				blocks.push(block);
+			while(block)
+			{
+				coords.row++;
+				if(coords.row > end.row)
+					break;
+				if(getCellBlock(findCell(coords)) == block)
+					continue;
+				block = getCellBlock(findCell(coords));
+				if(block)
+					blocks.push(block);
+			}
+			return blocks;
+		}
+
+		/** @private */
+		tlf_internal override function getNextLeafHelper(limitElement:FlowGroupElement,child:FlowElement):FlowLeafElement
+		{
+			return parent.getNextLeafHelper(limitElement,this);
+		}
+		
+		/** @private */
+		tlf_internal override function getPreviousLeafHelper(limitElement:FlowGroupElement,child:FlowElement):FlowLeafElement
+		{
+			return parent.getPreviousLeafHelper(limitElement,this);
+		}
+
+		private function getLeaf():TableLeafElement
+		{
+			if(_leaf == null)
+				_leaf = new TableLeafElement(this);
+			return _leaf;
+		}
+		
+		public override function findLeaf(relativePosition:int):FlowLeafElement
+		{
+			return getLeaf();
+		}
+		public override function getLastLeaf(): FlowLeafElement
+		{
+			return getLeaf();
+		}
+		public override function getFirstLeaf():FlowLeafElement
+		{
+			return getLeaf();
+		}
+
+		tlf_internal override function createContentElement():void{}
+		/** @private 
+		 * Release the FTE data structure that corresponds to the FlowElement, so it can be gc'ed
+		 */
+		tlf_internal override function releaseContentElement():void{}
+
+		/**
+		 * Creates and returns a default row 
+		 **/
+		tlf_internal function createRowElement(index:int, defaultRowFormat:ITextLayoutFormat):TableRowElement {
+			var row:TableRowElement = new TableRowElement(defaultRowFormat);
+			row.rowIndex = index;
+			row.table = this;
+			return row;
+		}
+
+		/**
+		 * Creates and returns a default column 
+		 **/
+		tlf_internal function createColumnElement(index:int, defaultColumnFormat:ITextLayoutFormat):TableColElement {
+			var column:TableColElement = new TableColElement(defaultColumnFormat);
+			column.colIndex = index;
+			column.table = this;
+			return column;
+		}
+	}
+}
+class CellCoords
+{
+	public var column:int;
+	public var row:int;
+	public function CellCoords(colIdx:int,rowIdx:int)
+	{
+		column = colIdx;
+		row = rowIdx;
 	}
 }
diff --git a/textLayout/src/flashx/textLayout/elements/TableFormattedElement.as b/textLayout/src/flashx/textLayout/elements/TableFormattedElement.as
index 4b5bd0f..edad5e3 100644
--- a/textLayout/src/flashx/textLayout/elements/TableFormattedElement.as
+++ b/textLayout/src/flashx/textLayout/elements/TableFormattedElement.as
@@ -25,6 +25,9 @@
 			super();
 		}
 		
+		/**
+		 * Get a reference to the table. We could refactor this since it's accessed multiple times.
+		 **/
 		public function getTable():TableElement
 		{
 			// find the root element entry and either return null or the containing textFlow
@@ -32,6 +35,37 @@
 			while ((elem.parent) != null && !( elem.parent is TableElement))
 				elem = elem.parent;
 			return elem.parent as TableElement;		
-		}	
+		}
+		
+		
+		private var _table:TableElement;
+		
+		/**
+		 * Returns a reference to the table. For this to work we need to set the 
+		 * table to null when it's removed. 
+		 **/
+		public function get table():TableElement {
+			
+			if (_table) return _table;
+			
+			// find the root element entry and either return null or the containing textFlow
+			var elem:FlowGroupElement = this;
+			
+			while ((elem.parent) != null && !(elem.parent is TableElement)) {
+				elem = elem.parent;
+			}
+			
+			_table = elem.parent as TableElement;
+			
+			return _table;
+		}
+		
+		/**
+		 * @private
+		 **/
+		public function set table(element:TableElement):void {
+			_table = element;
+		}
 	}
-}
\ No newline at end of file
+}
+
diff --git a/textLayout/src/flashx/textLayout/elements/TableLeafElement.as b/textLayout/src/flashx/textLayout/elements/TableLeafElement.as
new file mode 100644
index 0000000..0fdf629
--- /dev/null
+++ b/textLayout/src/flashx/textLayout/elements/TableLeafElement.as
@@ -0,0 +1,125 @@
+package flashx.textLayout.elements
+{
+	import flash.text.engine.ElementFormat;
+	import flash.text.engine.TextElement;
+	import flash.text.engine.TextLine;
+	
+	import flashx.textLayout.compose.BaseCompose;
+	import flashx.textLayout.compose.IFlowComposer;
+	import flashx.textLayout.compose.ISWFContext;
+	import flashx.textLayout.formats.ITextLayoutFormat;
+	import flashx.textLayout.tlf_internal;
+	
+	use namespace tlf_internal;
+	
+	public class TableLeafElement extends FlowLeafElement
+	{
+		private var _table:TableElement;
+		public function TableLeafElement(table:TableElement)
+		{
+			super();
+			_table = table;
+		}
+
+		/** @private */
+		override tlf_internal function createContentElement():void
+		{
+			// not sure if this makes sense...
+			if (_blockElement)
+				return;
+			
+			computedFormat;	// BEFORE creating the element
+			var flowComposer:IFlowComposer = getTextFlow().flowComposer;
+			var swfContext:ISWFContext = flowComposer && flowComposer.swfContext ? flowComposer.swfContext : BaseCompose.globalSWFContext;
+
+			var format:ElementFormat = FlowLeafElement.computeElementFormatHelper (_table.computedFormat, _table.getParagraph(), swfContext) 
+			_blockElement = new TextElement(_text,format);
+			CONFIG::debug { Debugging.traceFTECall(_blockElement,null,"new TextElement()"); }
+			CONFIG::debug { Debugging.traceFTEAssign(_blockElement, "text", _text); }
+			super.createContentElement();
+
+		}
+
+		/** @private */
+		override protected function get abstract():Boolean
+		{ return false; }		
+		
+		/** @private */
+		tlf_internal override function get defaultTypeName():String
+		{ return "table"; }
+		
+		/** @private */
+		public override function get text():String
+		{
+			return "\u0016";
+		}
+		
+		/** @private */
+		public override function getText(relativeStart:int=0, relativeEnd:int=-1, paragraphSeparator:String="\n"):String
+		{
+			return _table.getText(relativeStart, relativeEnd, paragraphSeparator);
+		}
+		
+		/** @private */
+		tlf_internal override function normalizeRange(normalizeStart:uint,normalizeEnd:uint):void
+		{
+			// not sure what to do here (see SpanElement)...
+			super.normalizeRange(normalizeStart,normalizeEnd);
+		}
+		
+		/** @private */
+		tlf_internal override function mergeToPreviousIfPossible():Boolean
+		{
+			// not sure what to do here (see SpanElement)...
+			return false;
+		}
+		
+		public override function getNextLeaf(limitElement:FlowGroupElement=null):FlowLeafElement
+		{
+			return _table.getNextLeafHelper(limitElement,this);
+		}
+		
+		public override function getPreviousLeaf(limitElement:FlowGroupElement=null):FlowLeafElement
+		{
+			return _table.getPreviousLeafHelper(limitElement,this);
+		}
+		/** @private */
+		public override function getCharAtPosition(relativePosition:int):String
+		{
+			return getText(relativePosition,relativePosition);
+		}
+		public override function get computedFormat():ITextLayoutFormat
+		{
+			return _table.computedFormat;
+		}
+		public override function get textLength():int
+		{
+			return _table.textLength;
+		}
+		tlf_internal override function updateAdornments(tLine:TextLine, blockProgression:String):int
+		{
+			return 0;
+		}
+
+		override public function get parent():FlowGroupElement
+		{ 
+			return _table; 
+		}
+
+		override public function getTextFlow():TextFlow
+		{
+			return _table.getTextFlow();
+		}
+		
+		override public function getParagraph():ParagraphElement
+		{
+			return _table.getParagraph();
+		}
+		
+		override public function getElementRelativeStart(ancestorElement:FlowElement):int
+		{
+			return _table.getElementRelativeStart(ancestorElement);
+		}
+
+	}
+}
diff --git a/textLayout/src/flashx/textLayout/elements/TableRowElement.as b/textLayout/src/flashx/textLayout/elements/TableRowElement.as
index dca92d3..0a23abc 100644
--- a/textLayout/src/flashx/textLayout/elements/TableRowElement.as
+++ b/textLayout/src/flashx/textLayout/elements/TableRowElement.as
@@ -18,13 +18,18 @@
 ////////////////////////////////////////////////////////////////////////////////
 package flashx.textLayout.elements
 {
+	import flash.utils.getQualifiedClassName;
+	
 	import flashx.textLayout.tlf_internal;
+	import flashx.textLayout.edit.EditManager;
+	import flashx.textLayout.edit.SelectionManager;
+	import flashx.textLayout.formats.ITextLayoutFormat;
 	
 	use namespace tlf_internal;
 	
 	/** 
-	 * <p> TableRowElement is an item in a TableElement. It most commonly contains one or more TableDataCellElement objects, 
-	 * A TableRowElement always appears within a TableElement, TableBodyElement.</p>
+	 * TableRowElement is an item in a TableElement. It most commonly contains one or more TableCellElement objects, 
+	 * A TableRowElement always appears within a TableElement, TableBodyElement.
 	 *
 	 * 
 	 * @playerversion Flash 10
@@ -32,7 +37,7 @@
 	 * @langversion 3.0
 	 *
 	 */
-	public final class TableRowElement extends TableFormattedElement
+	public class TableRowElement extends TableFormattedElement
 	{		
 		public var x:Number;
 		public var y:Number;
@@ -45,6 +50,19 @@
 		public var columnIndex:Number = 0;
 		public var iMaxRowDepth:Number = 0;
 		public var beyondParcel:Boolean = false;
+		public var composedHeight:Number = 0;
+		public var totalHeight:Number = 0;// used to compute if a row will fit in parcel. Need a separate value for cells that span rows.
+		public var isMaxHeight:Boolean = false;
+		
+		public function TableRowElement(format:ITextLayoutFormat=null)
+		{
+			super();
+			
+			if (format) {
+				this.format = format;
+			}
+		}
+
 		
 		/** @private */
 		override protected function get abstract():Boolean
@@ -57,7 +75,7 @@
 		/** @private */
 		tlf_internal override function canOwnFlowElement(elem:FlowElement):Boolean
 		{
-			return (elem is TableDataCellElement);
+			return (elem is TableCellElement);
 		}
 		
 		/** @private if its in a numbered list expand the damage to all list items - causes the numbers to be regenerated */
@@ -65,6 +83,102 @@
 		{
 			super.modelChanged(changeType,elem,changeStart,changeLen,needNormalize,bumpGeneration);
 		}
+		
+		/**
+		 * Returns a vector of table cell elements or null if the row contains no cells
+		 **/
+		public function getCells():Vector.<TableCellElement>
+		{
+			var table:TableElement = getTable();
+			
+			if(!table) {
+				return null;
+			}
+			
+			return table.getCellsForRow(this);
+		}
+		
+		/**
+		 * Get an array of cells or null if the row contains no cells
+		 **/
+		public function get cells():Array
+		{
+			var table:TableElement = getTable();
+			
+			if (!table) {
+				return null;
+			}
+			
+			return table.getCellsForRowArray(this);
+		}
+		
+		/**
+		 * Returns the number of cells in this row. 
+		 **/
+		public function get numCells():int
+		{
+			var table:TableElement = getTable();
+			
+			if (!table) {
+				return 0;
+			}
+			
+			return table.getCellsForRow(this).length;
+		}
+		
+		/**
+		 * Returns the cell at the specified index or null if out of range. 
+		 **/
+		public function getCellAt(index:int):TableCellElement
+		{
+			var cells:Vector.<TableCellElement> = getCells();
+			
+			if(!cells || index<0 || index>=cells.length)
+				return null;
+			return cells[index];
+		}
+		
+		/**
+		 * Adds a table cell to the row
+		 **/
+		public function addCell(cell:TableCellElement):TableCellElement
+		{
+			var table:TableElement = getTable();
+			var cellLength:int = numChildren;
+			
+			if (!table) {
+				throw new Error("Table must be set");
+			}
+			
+			cell.rowIndex = rowIndex;
+			
+			if (cell.colIndex==-1) {
+				cell.colIndex = cellLength;
+			}
+			
+			cells.push(cell);
+			//var selectable:Boolean = textFlow.interactionManager is SelectionManager;
+			//var editable:Boolean = textFlow.interactionManager is EditManager;
+			
+			return cell;
+		}
+		
+		/**
+		 * Adds a table cell to the row
+		 **/
+		public function addCellAt(index:int):TableCellElement
+		{
+			throw new Error("Add cell at is not implemented");
+		}
+		
+		/**
+		 * Get an estimate column count for this row.
+		 * This is temporary. TODO loop through cells and check for column span.
+		 **/
+		public function getColumnCount():int
+		{
+			return numCells || numChildren;
+		}
 
 	}
 }
diff --git a/textLayout/src/flashx/textLayout/elements/TextFlow.as b/textLayout/src/flashx/textLayout/elements/TextFlow.as
index a248665..6ccf576 100644
--- a/textLayout/src/flashx/textLayout/elements/TextFlow.as
+++ b/textLayout/src/flashx/textLayout/elements/TextFlow.as
@@ -284,6 +284,9 @@
 		
 		// ILG count
 		private var _graphicObjectCount:int;
+		
+		// nested TextFlow support
+		private var _parentElement:FlowGroupElement;
 				
 		/** 
 		 * Constructor - creates a new TextFlow instance.
@@ -1180,6 +1183,29 @@
 				_formatResolver.invalidateAll(this);
 			formatChanged(true);
 		}
+
+		/** The parent element is the element that the TextFlow is nested inside (such as a TableCellElement).
+		 * This property is for support of nested TextFlows to handle things like selection and editing.
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 */		
+		public function get parentElement():FlowGroupElement

+		{
+			return _parentElement;
+		}
+
+		public function set parentElement(value:FlowGroupElement):void

+		{
+			_parentElement = value;
+		}
+		
+		public function nestedInTable():Boolean{
+			return parentElement && parentElement is TableCellElement;
+		}
+
 	} // end TextFlow class
 }
 import flash.utils.Dictionary;
diff --git a/textLayout/src/flashx/textLayout/factory/StringTextLineFactory.as b/textLayout/src/flashx/textLayout/factory/StringTextLineFactory.as
index 355e6c5..7074f5c 100644
--- a/textLayout/src/flashx/textLayout/factory/StringTextLineFactory.as
+++ b/textLayout/src/flashx/textLayout/factory/StringTextLineFactory.as
@@ -463,7 +463,7 @@
 				charPosition = line.getAtomTextBlockEndIndex(atomIndex);
 			}
 			
-			line.flushAtomData();
+			// line.flushAtomData(); // Warning: Now does nothing
 			return charPosition;
 		}
 		
diff --git a/textLayout/src/flashx/textLayout/factory/TextLineFactoryBase.as b/textLayout/src/flashx/textLayout/factory/TextLineFactoryBase.as
index c59999b..8092cd5 100644
--- a/textLayout/src/flashx/textLayout/factory/TextLineFactoryBase.as
+++ b/textLayout/src/flashx/textLayout/factory/TextLineFactoryBase.as
@@ -393,7 +393,7 @@
 			// 4. Get the char index for this atom index
 			var nextTruncationPosition:int = line.getAtomTextBlockBeginIndex(atomIndex) + paraStart;
 			
-			line.flushAtomData();
+			//line.flushAtomData(); // Warning: Now does nothing
 			
 			return nextTruncationPosition;
 		} 
diff --git a/textLayout/src/flashx/textLayout/formats/ITextLayoutFormat.as b/textLayout/src/flashx/textLayout/formats/ITextLayoutFormat.as
index e25b683..8e3099d 100644
--- a/textLayout/src/flashx/textLayout/formats/ITextLayoutFormat.as
+++ b/textLayout/src/flashx/textLayout/formats/ITextLayoutFormat.as
@@ -1161,6 +1161,62 @@
 		function get borderBottomColor():*;
 
 		/**
+		 * Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+		 * <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		function get borderLeftPriority():*;
+		
+		/**
+		 * Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+		 * <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		function get borderRightPriority():*;
+		
+		/**
+		 * Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+		 * <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		function get borderTopPriority():*;
+		
+		/**
+		 * Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+		 * <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		function get borderBottomPriority():*;
+
+		/**
 		 * left margin in pixels(adopts default value if undefined during cascade).
 		 * <p>Legal values are numbers from -8000 to 8000 and FormatValue.INHERIT.</p>
 		 * <p>Default value is undefined indicating not set.</p>
@@ -1245,7 +1301,7 @@
 		 * @langversion 3.0
 		 */
 		function get cellPadding():*;
-
+		
 		/**
 		 * Width of table element specifies the desired width of the entire table and is intended for visual user agents. When the value is a percentage value, the value is relative to the user agent's available horizontal space.
 		 * <p>Legal values as a number are from 0 to 8000.</p>
@@ -1277,6 +1333,36 @@
 		 * @langversion 3.0
 		 */
 		function get tableColumnWidth():*;
+		
+		/**
+		 * Minimum height of a table cell. If there is no maximum, the cell will grow in height to fit the content. Minimum and maximum of the same values will give the cell a fixed height.
+		 * <p>Legal values as a number are from 2 to 10000.</p>
+		 * <p>Legal values include FormatValue.INHERIT.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will have a value of 2.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		function get minCellHeight():*;
+		
+		/**
+		 * Maximum height of a table cell. If there is no maximum, the cell will grow in height to fit the content. Minimum and maximum of the same values will give the cell a fixed height.
+		 * <p>Legal values as a number are from 2 to 10000.</p>
+		 * <p>Legal values include FormatValue.INHERIT.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will have a value of 2.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		function get maxCellHeight():*;
 
 		/**
 		 * frame specifies which sides of the frame surrounding a table will be visible. Possible values:
diff --git a/textLayout/src/flashx/textLayout/formats/TextLayoutFormat.as b/textLayout/src/flashx/textLayout/formats/TextLayoutFormat.as
index b532c40..aa8166a 100644
--- a/textLayout/src/flashx/textLayout/formats/TextLayoutFormat.as
+++ b/textLayout/src/flashx/textLayout/formats/TextLayoutFormat.as
@@ -74,7 +74,7 @@
 
 	/**
 	 * The TextLayoutFormat class holds all of the text layout properties. These properties affect the format and style of a text flow at the container level, paragraph level, and text level.  Both the ContainerController class and the FlowElement base class have <code>format</code> properties that enable you to assign a TextLayoutFormat instance to them. Assign a TextLayoutFormat object to a container to affect the format of all of the container's content. Assign a TextLayoutFormat object to a FlowElement descendant to specify formatting for that particular element: TextFlow, ParagraphElement, DivElement, SpanElement, InlineGraphicElement, LinkElement, and TCYElement.
-	 * In addition to the <code>format</code> property, these classes also define each of the individual TextLayoutFormat properties so that you can override the setting of a particular style property for that element, if you wish. <p>Because you can set a given style at multiple levels, it is possible to have conflicts. For example, the color of the text at the TextFlow level could be set to black while a SpanElement object sets it to blue. The general rule is that the setting at the lowest level on the text flow tree takes precedence. So if the ligature level is set for a TextFlow instance and also set for a DivElement, the DivElement setting takes precedence. </p><p>Cascading styles refers to the process of adopting styles from a higher level in the text flow if a style value is undefined at a lower level. When a style is undefined on an element at the point it is about to be rendered, it either takes its default value or the value cascades or descends from the value on a parent element. For example, if the transparency (<code>textAlpha</code> property) of the text is undefined on a SpanElement object, but is set on the TextFlow, the value of the <code>TextFlow.textAlpha</code> property cascades to the SpanElement object and is applied to the text for that span. The result of the cascade, or the sum of the styles that is applied to the element, is stored in the element's <code>computedFormat</code> property.</p><p>In the same way, you can apply user styles using the <code>userStyles</code> property of the ContainerController and FlowElement classes. This  property allows you to read or write a dictionary of user styles and apply its settings to a container or a text flow element. The user styles dictionary is an object that consists of <em>stylename-value</em> pairs. Styles specified by the <code>userStyles</code> property take precedence over all others.</p><p>Most styles that are undefined inherit the value of their immediate parent during a cascade. A small number of styles, however, do not inherit their parentÕs value and take on their default values instead.</p><p><strong>Style properties that adopt their default values, if undefined, include:</strong> <code>backgroundAlpha</code>, <code>backgroundColor</code>, <code>columnCount</code>, <code>columnGap</code>, <code>columnWidth</code>, <code>lineBreak</code>, <code>paddingBottom</code>, <code>paddingLeft</code>, <code>paddingRight</code>, <code>paddingTop</code>, <code>verticalAlign</code></p>.
+	 * In addition to the <code>format</code> property, these classes also define each of the individual TextLayoutFormat properties so that you can override the setting of a particular style property for that element, if you wish. <p>Because you can set a given style at multiple levels, it is possible to have conflicts. For example, the color of the text at the TextFlow level could be set to black while a SpanElement object sets it to blue. The general rule is that the setting at the lowest level on the text flow tree takes precedence. So if the ligature level is set for a TextFlow instance and also set for a DivElement, the DivElement setting takes precedence. </p><p>Cascading styles refers to the process of adopting styles from a higher level in the text flow if a style value is undefined at a lower level. When a style is undefined on an element at the point it is about to be rendered, it either takes its default value or the value cascades or descends from the value on a parent element. For example, if the transparency (<code>textAlpha</code> property) of the text is undefined on a SpanElement object, but is set on the TextFlow, the value of the <code>TextFlow.textAlpha</code> property cascades to the SpanElement object and is applied to the text for that span. The result of the cascade, or the sum of the styles that is applied to the element, is stored in the element's <code>computedFormat</code> property.</p><p>In the same way, you can apply user styles using the <code>userStyles</code> property of the ContainerController and FlowElement classes. This  property allows you to read or write a dictionary of user styles and apply its settings to a container or a text flow element. The user styles dictionary is an object that consists of <em>stylename-value</em> pairs. Styles specified by the <code>userStyles</code> property take precedence over all others.</p><p>Most styles that are undefined inherit the value of their immediate parent during a cascade. A small number of styles, however, do not inherit their parent�s value and take on their default values instead.</p><p><strong>Style properties that adopt their default values, if undefined, include:</strong> <code>backgroundAlpha</code>, <code>backgroundColor</code>, <code>columnCount</code>, <code>columnGap</code>, <code>columnWidth</code>, <code>lineBreak</code>, <code>paddingBottom</code>, <code>paddingLeft</code>, <code>paddingRight</code>, <code>paddingTop</code>, <code>verticalAlign</code></p>.
 	 * @includeExample examples\TextLayoutFormatExample.as -noswf
 	 * @includeExample examples\TextLayoutFormatExample2.as -noswf
 	 * @see flashx.textLayout.elements.FlowElement#format
@@ -953,6 +953,38 @@
 			return _borderBottomColorProperty;
 		}
 		/** @private */
+		static private var _borderLeftPriorityProperty:Property;
+		static public function get borderLeftPriorityProperty():Property
+		{
+			if (!_borderLeftPriorityProperty)
+				_borderLeftPriorityProperty = Property.NewNumberProperty("borderLeftPriority",0,false,Vector.<String>([Category.TABLE,Category.TABLECELL,Category.TABLECOLUMN,Category.TABLEROW]),-8000,8000);
+			return _borderLeftPriorityProperty;
+		}
+		/** @private */
+		static private var _borderRightPriorityProperty:Property;
+		static public function get borderRightPriorityProperty():Property
+		{
+			if (!_borderRightPriorityProperty)
+				_borderRightPriorityProperty = Property.NewNumberProperty("borderRightPriority",0,false,Vector.<String>([Category.TABLE,Category.TABLECELL,Category.TABLECOLUMN,Category.TABLEROW]),-8000,8000);
+			return _borderRightPriorityProperty;
+		}
+		/** @private */
+		static private var _borderTopPriorityProperty:Property;
+		static public function get borderTopPriorityProperty():Property
+		{
+			if (!_borderTopPriorityProperty)
+				_borderTopPriorityProperty = Property.NewNumberProperty("borderTopPriority",0,false,Vector.<String>([Category.TABLE,Category.TABLECELL,Category.TABLECOLUMN,Category.TABLEROW]),-8000,8000);
+			return _borderTopPriorityProperty;
+		}
+		/** @private */
+		static private var _borderBottomPriorityProperty:Property;
+		static public function get borderBottomPriorityProperty():Property
+		{
+			if (!_borderBottomPriorityProperty)
+				_borderBottomPriorityProperty = Property.NewNumberProperty("borderBottomPriority",0,false,Vector.<String>([Category.TABLE,Category.TABLECELL,Category.TABLECOLUMN,Category.TABLEROW]),-8000,8000);
+			return _borderBottomPriorityProperty;
+		}
+		/** @private */
 		static private var _marginLeftProperty:Property;
 		static public function get marginLeftProperty():Property
 		{
@@ -1017,6 +1049,22 @@
 			return _tableColumnWidthProperty;
 		}
 		/** @private */
+		static private var _minCellHeightProperty:Property;
+		static public function get minCellHeightProperty():Property
+		{
+			if (!_minCellHeightProperty)
+				_minCellHeightProperty = Property.NewNumberOrEnumProperty("minCellHeight",2,false,Vector.<String>([Category.TABLE,Category.TABLECELL]),2,8000);
+			return _minCellHeightProperty;
+		}
+		/** @private */
+		static private var _maxCellHeightProperty:Property;
+		static public function get maxCellHeightProperty():Property
+		{
+			if (!_maxCellHeightProperty)
+				_maxCellHeightProperty = Property.NewNumberOrEnumProperty("maxCellHeight",8000,false,Vector.<String>([Category.TABLE,Category.TABLECELL]),2,8000);
+			return _maxCellHeightProperty;
+		}
+		/** @private */
 		static private var _frameProperty:Property;
 		static public function get frameProperty():Property
 		{
@@ -1136,6 +1184,12 @@
 			, tableColumnWidth:tableColumnWidthProperty
 			, frame:frameProperty
 			, rules:rulesProperty
+			, borderLeftPriority:borderLeftPriorityProperty
+			, borderRightPriority:borderRightPriorityProperty
+			, borderTopPriority:borderTopPriorityProperty
+			, borderBottomPriority:borderBottomPriorityProperty
+			, minCellHeight:minCellHeightProperty
+			, maxCellHeight:maxCellHeightProperty
 		}
 
 		/** Property descriptions accessible by name. @private */
@@ -2962,6 +3016,74 @@
 		{ setStyleByProperty(TextLayoutFormat.borderBottomColorProperty,value); }
 
 		/**
+		 * Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+		 * <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get borderLeftPriority():*
+		{ return _styles.borderLeftPriority; }
+		public function set borderLeftPriority(value:*):void
+		{ setStyleByProperty(TextLayoutFormat.borderLeftPriorityProperty,value); }
+		
+		/**
+		 * Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+		 * <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get borderRightPriority():*
+		{ return _styles.borderRightPriority; }
+		public function set borderRightPriority(value:*):void
+		{ setStyleByProperty(TextLayoutFormat.borderRightPriorityProperty,value); }
+		
+		/**
+		 * Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+		 * <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get borderTopPriority():*
+		{ return _styles.borderTopPriority; }
+		public function set borderTopPriority(value:*):void
+		{ setStyleByProperty(TextLayoutFormat.borderTopPriorityProperty,value); }
+		
+		/**
+		 * Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+		 * <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get borderBottomPriority():*
+		{ return _styles.borderBottomPriority; }
+		public function set borderBottomPriority(value:*):void
+		{ setStyleByProperty(TextLayoutFormat.borderBottomPriorityProperty,value); }
+
+		/**
 		 * left margin in pixels(adopts default value if undefined during cascade).
 		 * <p>Legal values are numbers from -8000 to 8000 and FormatValue.INHERIT.</p>
 		 * <p>Default value is undefined indicating not set.</p>
@@ -3102,6 +3224,44 @@
 		{ return _styles.tableColumnWidth; }
 		public function set tableColumnWidth(value:*):void
 		{ setStyleByProperty(TextLayoutFormat.tableColumnWidthProperty,value); }
+		
+		/**
+		 * Minimum height of a table cell. If there is no maximum, the cell will grow in height to fit the content. Minimum and maximum of the same values will give the cell a fixed height.
+		 * <p>Legal values as a number are from 2 to 10000.</p>
+		 * <p>Legal values include FormatValue.INHERIT.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will have a value of 2.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get minCellHeight():*
+		{ return _styles.minCellHeight; }
+		public function set minCellHeight(value:*):void
+		{ setStyleByProperty(TextLayoutFormat.minCellHeightProperty,value); }
+		
+		/**
+		 * Maximum height of a table cell. If there is no maximum, the cell will grow in height to fit the content. Minimum and maximum of the same values will give the cell a fixed height.
+		 * <p>Legal values as a number are from 2 to 10000.</p>
+		 * <p>Legal values include FormatValue.INHERIT.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will have a value of 2.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get maxCellHeight():*
+		{ return _styles.maxCellHeight; }
+		public function set maxCellHeight(value:*):void
+		{ setStyleByProperty(TextLayoutFormat.maxCellHeightProperty,value); }
+
+
 
 		[Inspectable(enumeration="void,above,below,hsides,vsides,lhs,rhs,box,border,inherit")]
 		/**
@@ -3241,6 +3401,18 @@
 				stylesObject.frame = TextLayoutFormat.frameProperty.defaultValue;
 			if (stylesObject.rules != undefined && stylesObject.rules != TextLayoutFormat.rulesProperty.defaultValue)
 				stylesObject.rules = TextLayoutFormat.rulesProperty.defaultValue;
+			if( stylesObject.borderBottomPriority != undefined &&  stylesObject.borderBottomPriority != TextLayoutFormat.borderBottomPriorityProperty.defaultValue)
+				stylesObject.borderBottomPriority = TextLayoutFormat.borderBottomPriorityProperty.defaultValue;
+			if( stylesObject.borderTopPriority != undefined &&  stylesObject.borderTopPriority != TextLayoutFormat.borderTopPriorityProperty.defaultValue)
+				stylesObject.borderTopPriority = TextLayoutFormat.borderTopPriorityProperty.defaultValue;
+			if( stylesObject.borderLeftPriority != undefined &&  stylesObject.borderLeftPriority != TextLayoutFormat.borderLeftPriorityProperty.defaultValue)
+				stylesObject.borderLeftPriority = TextLayoutFormat.borderLeftPriorityProperty.defaultValue;
+			if( stylesObject.borderRightPriority != undefined &&  stylesObject.borderRightPriority != TextLayoutFormat.borderRightPriorityProperty.defaultValue)
+				stylesObject.borderRightPriority = TextLayoutFormat.borderRightPriorityProperty.defaultValue;
+			if (stylesObject.minCellHeight != undefined && stylesObject.minCellHeight != TextLayoutFormat.minCellHeightProperty.defaultValue)
+				stylesObject.minCellHeight = TextLayoutFormat.minCellHeightProperty.defaultValue;
+			if (stylesObject.maxCellHeight != undefined && stylesObject.maxCellHeight != TextLayoutFormat.maxCellHeightProperty.defaultValue)
+				stylesObject.maxCellHeight = TextLayoutFormat.maxCellHeightProperty.defaultValue;
 		}
 
 		/**
diff --git a/textLayout/src/flashx/textLayout/formats/TextLayoutFormatInc.as b/textLayout/src/flashx/textLayout/formats/TextLayoutFormatInc.as
index c263433..2c1f6f6 100644
--- a/textLayout/src/flashx/textLayout/formats/TextLayoutFormatInc.as
+++ b/textLayout/src/flashx/textLayout/formats/TextLayoutFormatInc.as
@@ -1837,6 +1837,98 @@
 		}
 
 		/**
+ 		* TextLayoutFormat:
+ 		* Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+ 		* <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+ 		* <p>Default value is undefined indicating not set.</p>
+ 		* <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+ 		* 
+ 		* @throws RangeError when set value is not within range for this property
+ 		* 
+ 		* @playerversion Flash 10
+ 		* @playerversion AIR 1.5
+ 		* @langversion 3.0
+ 		*/
+		public function get borderLeftPriority():*
+		{
+			return _format ? _format.borderLeftPriority : undefined;
+		}
+		public function set borderLeftPriority(value:*):void
+		{ 
+			writableTextLayoutFormat().borderLeftPriority = value;
+			formatChanged();
+		}
+
+		/**
+		 * TextLayoutFormat:
+		 * Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+		 * <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get borderRightPriority():*
+		{
+			return  _format ? _format.borderRightPriority : undefined;
+		}
+		public function set borderRightPriority(value:*):void
+		{
+			writableTextLayoutFormat().borderRightPriority = value;
+			formatChanged();
+		}
+		
+		/**
+		 * TextLayoutFormat:
+		 * Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+		 * <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get borderTopPriority():*
+		{
+			return  _format ? _format.borderTopPriority : undefined;
+		}
+		public function set borderTopPriority(value:*):void
+		{
+			writableTextLayoutFormat().borderTopPriority = value;
+			formatChanged();
+		}
+		
+		/**
+		 * TextLayoutFormat:
+		 * Specifies the priority when drawing cell boundaries. When settings between two adjacent cells conflict, the border with the higher priority wins. If the priorities are equal, the latter of the two cells takes priority.
+		 * <p>Legal values are any rational number. Conflicts are resolved with the properties of the higher number being drawn.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will inherit, and default to 0.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get borderBottomPriority():*
+		{
+			return  _format ? _format.borderBottomPriority : undefined;
+		}
+		public function set borderBottomPriority(value:*):void
+		{
+			writableTextLayoutFormat().borderBottomPriority = value;
+			formatChanged();
+		}
+
+		/**
 		 * TextLayoutFormat:
 		 * left margin in pixels(adopts default value if undefined during cascade).
 		 * <p>Legal values are numbers from -8000 to 8000 and FormatValue.INHERIT.</p>
@@ -1849,6 +1941,7 @@
 		 * @playerversion AIR 1.5
 		 * @langversion 3.0
 		 */
+
 		public function get marginLeft():*
 		{
 			return _format ? _format.marginLeft : undefined;
@@ -2026,6 +2119,54 @@
 			formatChanged();
 		}
 
+		/**
+		 * TextLayoutFormat:
+		 * Minimum height of a table cell. If there is no maximum, the cell will grow in height to fit the content. Minimum and maximum of the same values will give the cell a fixed height.
+		 * <p>Legal values as a number are from 2 to 10000.</p>
+		 * <p>Legal values include FormatValue.INHERIT.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will have a value of 2.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get minCellHeight():*
+		{
+			return _format ? _format.minCellHeight : undefined;
+		}
+		public function set minCellHeight(value:*):void
+		{
+			writableTextLayoutFormat().minCellHeight = value;
+			formatChanged();
+		}
+		
+		/**
+		 * TextLayoutFormat:
+		 * Maximum height of a table cell. If there is no maximum, the cell will grow in height to fit the content. Minimum and maximum of the same values will give the cell a fixed height.
+		 * <p>Legal values as a number are from 2 to 10000.</p>
+		 * <p>Legal values include FormatValue.INHERIT.</p>
+		 * <p>Default value is undefined indicating not set.</p>
+		 * <p>If undefined during the cascade this property will have a value of 2.</p>
+		 * 
+		 * @throws RangeError when set value is not within range for this property
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get maxCellHeight():*
+		{
+			return _format ? _format.maxCellHeight : undefined;
+		}
+		public function set maxCellHeight(value:*):void
+		{
+			writableTextLayoutFormat().maxCellHeight = value;
+			formatChanged();
+		}
+
 		[Inspectable(enumeration="void,above,below,hsides,vsides,lhs,rhs,box,border,inherit")]
 		/**
 		 * TextLayoutFormat:
diff --git a/textLayout/src/flashx/textLayout/operations/ApplyElementStyleNameOperation.as b/textLayout/src/flashx/textLayout/operations/ApplyElementStyleNameOperation.as
index 5cd0474..679c7cb 100644
--- a/textLayout/src/flashx/textLayout/operations/ApplyElementStyleNameOperation.as
+++ b/textLayout/src/flashx/textLayout/operations/ApplyElementStyleNameOperation.as
@@ -31,7 +31,6 @@
 
 	use namespace tlf_internal;
 	
-	[Deprecated(replacement="ApplyFormatToElementOperation", deprecatedSince="2.0")]
 	/**
 	 * The ApplyElementStyleNameOperation class encapsulates a style name change.
 	 *
diff --git a/textLayout/src/flashx/textLayout/operations/ApplyElementUserStyleOperation.as b/textLayout/src/flashx/textLayout/operations/ApplyElementUserStyleOperation.as
index 02cf1d7..afc251f 100644
--- a/textLayout/src/flashx/textLayout/operations/ApplyElementUserStyleOperation.as
+++ b/textLayout/src/flashx/textLayout/operations/ApplyElementUserStyleOperation.as
@@ -30,7 +30,6 @@
 
 	use namespace tlf_internal;
 	
-	[Deprecated(replacement="ApplyFormatToElementOperation", deprecatedSince="2.0")]
 	/**
 	 * The ApplyElementUserStyleOperation class encapsulates a change in a style value of an element.
 	 *