You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by pi...@apache.org on 2014/11/28 01:20:42 UTC

[02/25] git commit: [flex-tlf] [refs/heads/develop] - Commit of table work

http://git-wip-us.apache.org/repos/asf/flex-tlf/blob/33df98ab/textLayout/src/flashx/textLayout/elements/ParagraphElement.as
----------------------------------------------------------------------
diff --git a/textLayout/src/flashx/textLayout/elements/ParagraphElement.as b/textLayout/src/flashx/textLayout/elements/ParagraphElement.as
index f4579fc..1fec299 100644
--- a/textLayout/src/flashx/textLayout/elements/ParagraphElement.as
+++ b/textLayout/src/flashx/textLayout/elements/ParagraphElement.as
@@ -31,9 +31,9 @@ package flashx.textLayout.elements
 	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 @@ package flashx.textLayout.elements
 	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 @@ package flashx.textLayout.elements
 	 
 	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 @@ package flashx.textLayout.elements
 			super();
 			_terminatorSpan = null;
 			_interactiveChildrenCount = 0 ;
+			_textBlockChildren = new Dictionary();
 		}
 		tlf_internal function get interactiveChildrenCount():int
 		{
@@ -106,26 +109,83 @@ package flashx.textLayout.elements
 		{
 			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
+		{
+			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++)
+			{
+				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 (!_textBlock)
+			if (!tb)
 				return;
-				
-			if (_textBlock.firstLine)	// A TextBlock may have no firstLine if it has previously been released.
+			
+			if (tb.firstLine)	// A TextBlock may have no firstLine if it has previously been released.
 			{
-				for (var textLineTest:TextLine = _textBlock.firstLine; textLineTest != null; textLineTest = textLineTest.nextLine)
+				for (var textLineTest:TextLine = tb.firstLine; textLineTest != null; textLineTest = textLineTest.nextLine)
 				{	
 					if(textLineTest.numChildren != 0)
 					{	
@@ -138,55 +198,169 @@ package flashx.textLayout.elements
 					}
 				}
 				
-				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 tb:TextBlock in tbs)
+			{
+				releaseTextBlockInternal(tb);
+			}
+			//_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 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 _textBlock; 
+			
+			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);
+			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 +373,7 @@ package flashx.textLayout.elements
 				}
 				
 				// 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 +386,15 @@ package flashx.textLayout.elements
  		/** @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 +403,20 @@ package flashx.textLayout.elements
 					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 +427,22 @@ package flashx.textLayout.elements
 					{
 						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 +452,42 @@ package flashx.textLayout.elements
 		}
 		
 		/** @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)
+						return [chldrn[i]];
+					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 (getTextBlocks().length == 0 || !tb)
 			{
 				child.releaseContentElement();
 				createTextBlock();	// does the whole tree
@@ -274,7 +495,7 @@ package flashx.textLayout.elements
 			}
 			var gc:Vector.<ContentElement>;	// scratch var
 			var group:GroupElement;			// scratch
-			if (numChildren == 1)
+			if (getChildrenInTextBlock(relativeStart).length < 2)
 			{
 				if (block is GroupElement)
 				{
@@ -285,19 +506,23 @@ package flashx.textLayout.elements
 					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 +532,21 @@ package flashx.textLayout.elements
 			}
 		}
 		
+		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 +555,63 @@ package flashx.textLayout.elements
 		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 +619,43 @@ package flashx.textLayout.elements
 			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.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 +682,7 @@ package flashx.textLayout.elements
 						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 +699,9 @@ package flashx.textLayout.elements
 			
 			// Now ensure para terminator
 			ensureTerminatorAfterReplace();
+			
+			// recreate text blocks to handle possible TableElement changes
+			createTextBlock();
 		}
 		
 		/** @private
@@ -411,17 +709,26 @@ package flashx.textLayout.elements
 		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 +785,46 @@ package flashx.textLayout.elements
 		 
 		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 +835,26 @@ package flashx.textLayout.elements
     					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 +879,41 @@ package flashx.textLayout.elements
 		 
 		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 +923,25 @@ package flashx.textLayout.elements
     					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 +949,27 @@ package flashx.textLayout.elements
 		/** @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 +1002,13 @@ package flashx.textLayout.elements
 				}
 				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 +1041,13 @@ package flashx.textLayout.elements
 				}
 				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 +1061,10 @@ package flashx.textLayout.elements
 				_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 +1112,10 @@ package flashx.textLayout.elements
 				}
 
 				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 +1124,21 @@ package flashx.textLayout.elements
 					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 +1152,8 @@ package flashx.textLayout.elements
 					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 +1161,13 @@ package flashx.textLayout.elements
 				//	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 +1177,10 @@ package flashx.textLayout.elements
 			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 +1188,7 @@ package flashx.textLayout.elements
 		/** @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 +1252,6 @@ package flashx.textLayout.elements
 			}
 		}
 		
-		// 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 +1280,20 @@ package flashx.textLayout.elements
 		/** @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 +1356,11 @@ package flashx.textLayout.elements
 		{
 			return _interactiveChildrenCount != 0 ;
 		}
+
+		tlf_internal function get terminatorSpan():SpanElement
+		{
+			return _terminatorSpan;
+		}
+
 	}
 }

http://git-wip-us.apache.org/repos/asf/flex-tlf/blob/33df98ab/textLayout/src/flashx/textLayout/elements/SpanElement.as
----------------------------------------------------------------------
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 @@ package flashx.textLayout.elements
 					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 @@ package flashx.textLayout.elements
 				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 @@ package flashx.textLayout.elements
 				{
 					// 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);
 					

http://git-wip-us.apache.org/repos/asf/flex-tlf/blob/33df98ab/textLayout/src/flashx/textLayout/elements/SubParagraphGroupElementBase.as
----------------------------------------------------------------------
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 @@ package flashx.textLayout.elements
 		}
 		
 		/** @private */
-		tlf_internal override function createContentAsGroup():GroupElement
+		tlf_internal override function createContentAsGroup(pos:int=0):GroupElement
 		{ return groupElement; }
 
 		/** @private */

http://git-wip-us.apache.org/repos/asf/flex-tlf/blob/33df98ab/textLayout/src/flashx/textLayout/elements/TableColElement.as
----------------------------------------------------------------------
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 @@ package flashx.textLayout.elements
 	{		
 		//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 @@ package flashx.textLayout.elements
 		{
 			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;
+		}
 	}
 }

http://git-wip-us.apache.org/repos/asf/flex-tlf/blob/33df98ab/textLayout/src/flashx/textLayout/elements/TableElement.as
----------------------------------------------------------------------
diff --git a/textLayout/src/flashx/textLayout/elements/TableElement.as b/textLayout/src/flashx/textLayout/elements/TableElement.as
index 92cefcc..ed82a28 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 @@ package flashx.textLayout.elements
 	 * 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 @@ package flashx.textLayout.elements
 	 */
 	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,714 @@ package flashx.textLayout.elements
 		/** @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
+		{
+			
+			markCellsDamaged();
+			hasCellDamage = true;
+			normalizeCells();
+			
+			super.cellSpacing = cellSpacingValue;
+		}
+		
+		public function get numRows():int
+		{
+			return rows.length;
+		}
+		
+		public function get numColumns():int
+		{
+			return columns.length;
+		}
+		
+		/**
+		 * Total number of cells
+		 **/
+		public function get numCells():int
 		{
-			return _row;
+			return getCells().length;
 		}
 		
-		public function get column():int
+		/**
+		 * 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
 		{
-			return _column;
+			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);
+			}
 		}
 
+		/**
+		 * 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
+		{
+			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 >= _column )
+			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;
+		}
+		
+		/**
+		 * 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);
+				}
+			}
+
+
+			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);
+				}
+			}
+			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 arColumn[columnIndex];
+			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 +804,22 @@ package flashx.textLayout.elements
 			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 +827,712 @@ package flashx.textLayout.elements
 				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;
+		}
+		
+		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;
 	}
 }

http://git-wip-us.apache.org/repos/asf/flex-tlf/blob/33df98ab/textLayout/src/flashx/textLayout/elements/TableFormattedElement.as
----------------------------------------------------------------------
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 @@ package flashx.textLayout.elements
 			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 @@ package flashx.textLayout.elements
 			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
+}
+